Move Dashing filterPath to a dashing utils file
authoregdaniel <egdaniel@google.com>
Wed, 4 Jun 2014 15:15:53 +0000 (08:15 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 4 Jun 2014 15:15:53 +0000 (08:15 -0700)
From inside GrContext, we have a need to create an SkPath an original path and
some dashing info. We do not have access to the original path effect so we need
a way to make the FilterPath function accessible outside of the effect. So I moved
the core filterPath code (and all need helper functions) out of SkDashPathEffect
and created a SkDashPath in utils to store these helper functions.

BUG=skia:
R=bsalomon@google.com, reed@google.com, rmistry@google.com

Author: egdaniel@google.com

Review URL: https://codereview.chromium.org/314623004

gyp/utils.gyp
include/effects/SkDashPathEffect.h
src/effects/SkDashPathEffect.cpp
src/utils/SkDashPath.cpp [new file with mode: 0644]
src/utils/SkDashPathPriv.h [new file with mode: 0644]

index 9009ad9..ea79746 100644 (file)
@@ -72,6 +72,8 @@
         '../src/utils/SkCanvasStateUtils.cpp',
         '../src/utils/SkCubicInterval.cpp',
         '../src/utils/SkCullPoints.cpp',
+        '../src/utils/SkDashPath.cpp',
+        '../src/utils/SkDashPathPriv.h',
         '../src/utils/SkDeferredCanvas.cpp',
         '../src/utils/SkDumpCanvas.cpp',
         '../src/utils/SkEventTracer.cpp',
index 10d8d2d..6fab962 100644 (file)
@@ -61,8 +61,6 @@ protected:
     virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
 
 private:
-    void setInternalMembers(SkScalar phase);
-
     SkScalar*   fIntervals;
     int32_t     fCount;
     SkScalar    fPhase;
index 4fd4185..2838b1f 100644 (file)
@@ -6,74 +6,10 @@
  */
 
 #include "SkDashPathEffect.h"
+
+#include "SkDashPathPriv.h"
 #include "SkReadBuffer.h"
 #include "SkWriteBuffer.h"
-#include "SkPathMeasure.h"
-
-static inline int is_even(int x) {
-    return (~x) << 31;
-}
-
-static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
-                                  int32_t* index, int count) {
-    for (int i = 0; i < count; ++i) {
-        if (phase > intervals[i]) {
-            phase -= intervals[i];
-        } else {
-            *index = i;
-            return intervals[i] - phase;
-        }
-    }
-    // If we get here, phase "appears" to be larger than our length. This
-    // shouldn't happen with perfect precision, but we can accumulate errors
-    // during the initial length computation (rounding can make our sum be too
-    // big or too small. In that event, we just have to eat the error here.
-    *index = 0;
-    return intervals[0];
-}
-
-void SkDashPathEffect::setInternalMembers(SkScalar phase) {
-    SkScalar len = 0;
-    for (int i = 0; i < fCount; i++) {
-        len += fIntervals[i];
-    }
-    fIntervalLength = len;
-
-    // watch out for values that might make us go out of bounds
-    if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) {
-
-        // Adjust phase to be between 0 and len, "flipping" phase if negative.
-        // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80
-        if (phase < 0) {
-            phase = -phase;
-            if (phase > len) {
-                phase = SkScalarMod(phase, len);
-            }
-            phase = len - phase;
-
-            // Due to finite precision, it's possible that phase == len,
-            // even after the subtract (if len >>> phase), so fix that here.
-            // This fixes http://crbug.com/124652 .
-            SkASSERT(phase <= len);
-            if (phase == len) {
-                phase = 0;
-            }
-        } else if (phase >= len) {
-            phase = SkScalarMod(phase, len);
-        }
-        SkASSERT(phase >= 0 && phase < len);
-
-        fPhase = phase;
-
-        fInitialDashLength = FindFirstInterval(fIntervals, fPhase,
-                                               &fInitialDashIndex, fCount);
-
-        SkASSERT(fInitialDashLength >= 0);
-        SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
-    } else {
-        fInitialDashLength = -1;    // signal bad dash intervals
-    }
-}
 
 SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
                                    SkScalar phase) {
@@ -87,247 +23,19 @@ SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
         fIntervals[i] = intervals[i];
     }
 
-    this->setInternalMembers(phase);
+    // set the internal data members
+    SkDashPath::CalcDashParameters(phase, fIntervals, fCount, &fInitialDashLength,
+                                   &fInitialDashIndex, &fIntervalLength, &fPhase);
 }
 
 SkDashPathEffect::~SkDashPathEffect() {
     sk_free(fIntervals);
 }
 
-static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) {
-    SkScalar radius = SkScalarHalf(rec.getWidth());
-    if (0 == radius) {
-        radius = SK_Scalar1;    // hairlines
-    }
-    if (SkPaint::kMiter_Join == rec.getJoin()) {
-        radius = SkScalarMul(radius, rec.getMiter());
-    }
-    rect->outset(radius, radius);
-}
-
-// Only handles lines for now. If returns true, dstPath is the new (smaller)
-// path. If returns false, then dstPath parameter is ignored.
-static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec,
-                      const SkRect* cullRect, SkScalar intervalLength,
-                      SkPath* dstPath) {
-    if (NULL == cullRect) {
-        return false;
-    }
-
-    SkPoint pts[2];
-    if (!srcPath.isLine(pts)) {
-        return false;
-    }
-
-    SkRect bounds = *cullRect;
-    outset_for_stroke(&bounds, rec);
-
-    SkScalar dx = pts[1].x() - pts[0].x();
-    SkScalar dy = pts[1].y() - pts[0].y();
-
-    // just do horizontal lines for now (lazy)
-    if (dy) {
-        return false;
-    }
-
-    SkScalar minX = pts[0].fX;
-    SkScalar maxX = pts[1].fX;
-
-    if (maxX < bounds.fLeft || minX > bounds.fRight) {
-        return false;
-    }
-
-    if (dx < 0) {
-        SkTSwap(minX, maxX);
-    }
-
-    // Now we actually perform the chop, removing the excess to the left and
-    // right of the bounds (keeping our new line "in phase" with the dash,
-    // hence the (mod intervalLength).
-
-    if (minX < bounds.fLeft) {
-        minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX,
-                                          intervalLength);
-    }
-    if (maxX > bounds.fRight) {
-        maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight,
-                                           intervalLength);
-    }
-
-    SkASSERT(maxX >= minX);
-    if (dx < 0) {
-        SkTSwap(minX, maxX);
-    }
-    pts[0].fX = minX;
-    pts[1].fX = maxX;
-
-    dstPath->moveTo(pts[0]);
-    dstPath->lineTo(pts[1]);
-    return true;
-}
-
-class SpecialLineRec {
-public:
-    bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec,
-              int intervalCount, SkScalar intervalLength) {
-        if (rec->isHairlineStyle() || !src.isLine(fPts)) {
-            return false;
-        }
-
-        // can relax this in the future, if we handle square and round caps
-        if (SkPaint::kButt_Cap != rec->getCap()) {
-            return false;
-        }
-
-        SkScalar pathLength = SkPoint::Distance(fPts[0], fPts[1]);
-
-        fTangent = fPts[1] - fPts[0];
-        if (fTangent.isZero()) {
-            return false;
-        }
-
-        fPathLength = pathLength;
-        fTangent.scale(SkScalarInvert(pathLength));
-        fTangent.rotateCCW(&fNormal);
-        fNormal.scale(SkScalarHalf(rec->getWidth()));
-
-        // now estimate how many quads will be added to the path
-        //     resulting segments = pathLen * intervalCount / intervalLen
-        //     resulting points = 4 * segments
-
-        SkScalar ptCount = SkScalarMulDiv(pathLength,
-                                          SkIntToScalar(intervalCount),
-                                          intervalLength);
-        int n = SkScalarCeilToInt(ptCount) << 2;
-        dst->incReserve(n);
-
-        // we will take care of the stroking
-        rec->setFillStyle();
-        return true;
-    }
-
-    void addSegment(SkScalar d0, SkScalar d1, SkPath* path) const {
-        SkASSERT(d0 < fPathLength);
-        // clamp the segment to our length
-        if (d1 > fPathLength) {
-            d1 = fPathLength;
-        }
-
-        SkScalar x0 = fPts[0].fX + SkScalarMul(fTangent.fX, d0);
-        SkScalar x1 = fPts[0].fX + SkScalarMul(fTangent.fX, d1);
-        SkScalar y0 = fPts[0].fY + SkScalarMul(fTangent.fY, d0);
-        SkScalar y1 = fPts[0].fY + SkScalarMul(fTangent.fY, d1);
-
-        SkPoint pts[4];
-        pts[0].set(x0 + fNormal.fX, y0 + fNormal.fY);   // moveTo
-        pts[1].set(x1 + fNormal.fX, y1 + fNormal.fY);   // lineTo
-        pts[2].set(x1 - fNormal.fX, y1 - fNormal.fY);   // lineTo
-        pts[3].set(x0 - fNormal.fX, y0 - fNormal.fY);   // lineTo
-
-        path->addPoly(pts, SK_ARRAY_COUNT(pts), false);
-    }
-
-private:
-    SkPoint fPts[2];
-    SkVector fTangent;
-    SkVector fNormal;
-    SkScalar fPathLength;
-};
-
 bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
                               SkStrokeRec* rec, const SkRect* cullRect) const {
-    // we do nothing if the src wants to be filled, or if our dashlength is 0
-    if (rec->isFillStyle() || fInitialDashLength < 0) {
-        return false;
-    }
-
-    const SkScalar* intervals = fIntervals;
-    SkScalar        dashCount = 0;
-    int             segCount = 0;
-
-    SkPath cullPathStorage;
-    const SkPath* srcPtr = &src;
-    if (cull_path(src, *rec, cullRect, fIntervalLength, &cullPathStorage)) {
-        srcPtr = &cullPathStorage;
-    }
-
-    SpecialLineRec lineRec;
-    bool specialLine = lineRec.init(*srcPtr, dst, rec, fCount >> 1, fIntervalLength);
-
-    SkPathMeasure   meas(*srcPtr, false);
-
-    do {
-        bool        skipFirstSegment = meas.isClosed();
-        bool        addedSegment = false;
-        SkScalar    length = meas.getLength();
-        int         index = fInitialDashIndex;
-
-        // Since the path length / dash length ratio may be arbitrarily large, we can exert
-        // significant memory pressure while attempting to build the filtered path. To avoid this,
-        // we simply give up dashing beyond a certain threshold.
-        //
-        // The original bug report (http://crbug.com/165432) is based on a path yielding more than
-        // 90 million dash segments and crashing the memory allocator. A limit of 1 million
-        // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the
-        // maximum dash memory overhead at roughly 17MB per path.
-        static const SkScalar kMaxDashCount = 1000000;
-        dashCount += length * (fCount >> 1) / fIntervalLength;
-        if (dashCount > kMaxDashCount) {
-            dst->reset();
-            return false;
-        }
-
-        // Using double precision to avoid looping indefinitely due to single precision rounding
-        // (for extreme path_length/dash_length ratios). See test_infinite_dash() unittest.
-        double  distance = 0;
-        double  dlen = fInitialDashLength;
-
-        while (distance < length) {
-            SkASSERT(dlen >= 0);
-            addedSegment = false;
-            if (is_even(index) && dlen > 0 && !skipFirstSegment) {
-                addedSegment = true;
-                ++segCount;
-
-                if (specialLine) {
-                    lineRec.addSegment(SkDoubleToScalar(distance),
-                                       SkDoubleToScalar(distance + dlen),
-                                       dst);
-                } else {
-                    meas.getSegment(SkDoubleToScalar(distance),
-                                    SkDoubleToScalar(distance + dlen),
-                                    dst, true);
-                }
-            }
-            distance += dlen;
-
-            // clear this so we only respect it the first time around
-            skipFirstSegment = false;
-
-            // wrap around our intervals array if necessary
-            index += 1;
-            SkASSERT(index <= fCount);
-            if (index == fCount) {
-                index = 0;
-            }
-
-            // fetch our next dlen
-            dlen = intervals[index];
-        }
-
-        // extend if we ended on a segment and we need to join up with the (skipped) initial segment
-        if (meas.isClosed() && is_even(fInitialDashIndex) &&
-                fInitialDashLength > 0) {
-            meas.getSegment(0, fInitialDashLength, dst, !addedSegment);
-            ++segCount;
-        }
-    } while (meas.nextContour());
-
-    if (segCount > 1) {
-        dst->setConvexity(SkPath::kConcave_Convexity);
-    }
-
-    return true;
+    return SkDashPath::FilterDashPath(dst, src, rec, cullRect, fIntervals, fCount,
+                                      fInitialDashLength, fInitialDashIndex, fIntervalLength);
 }
 
 // Currently asPoints is more restrictive then it needs to be. In the future
@@ -570,6 +278,9 @@ SkDashPathEffect::SkDashPathEffect(SkReadBuffer& buffer) : INHERITED(buffer) {
             fPhase += fIntervals[fInitialDashIndex] - fInitialDashLength;
         }
     } else {
-        this->setInternalMembers(fPhase);
+        // set the internal data members, fPhase should have been between 0 and intervalLength
+        // when written to buffer so no need to adjust it
+        SkDashPath::CalcDashParameters(fPhase, fIntervals, fCount, &fInitialDashLength,
+                                       &fInitialDashIndex, &fIntervalLength);
     }
 }
diff --git a/src/utils/SkDashPath.cpp b/src/utils/SkDashPath.cpp
new file mode 100644 (file)
index 0000000..3c4aef3
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDashPathPriv.h"
+#include "SkPathMeasure.h"
+
+static inline int is_even(int x) {
+    return (~x) << 31;
+}
+
+static SkScalar find_first_interval(const SkScalar intervals[], SkScalar phase,
+                                    int32_t* index, int count) {
+    for (int i = 0; i < count; ++i) {
+        if (phase > intervals[i]) {
+            phase -= intervals[i];
+        } else {
+            *index = i;
+            return intervals[i] - phase;
+        }
+    }
+    // If we get here, phase "appears" to be larger than our length. This
+    // shouldn't happen with perfect precision, but we can accumulate errors
+    // during the initial length computation (rounding can make our sum be too
+    // big or too small. In that event, we just have to eat the error here.
+    *index = 0;
+    return intervals[0];
+}
+
+void SkDashPath::CalcDashParameters(SkScalar phase, const SkScalar intervals[], int32_t count,
+                                    SkScalar* initialDashLength, int32_t* initialDashIndex,
+                                    SkScalar* intervalLength, SkScalar* adjustedPhase) {
+    SkScalar len = 0;
+    for (int i = 0; i < count; i++) {
+        len += intervals[i];
+    }
+    *intervalLength = len;
+
+    // watch out for values that might make us go out of bounds
+    if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) {
+
+        // Adjust phase to be between 0 and len, "flipping" phase if negative.
+        // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80
+        if (adjustedPhase) {
+            if (phase < 0) {
+                phase = -phase;
+                if (phase > len) {
+                    phase = SkScalarMod(phase, len);
+                }
+                phase = len - phase;
+
+                // Due to finite precision, it's possible that phase == len,
+                // even after the subtract (if len >>> phase), so fix that here.
+                // This fixes http://crbug.com/124652 .
+                SkASSERT(phase <= len);
+                if (phase == len) {
+                    phase = 0;
+                }
+            } else if (phase >= len) {
+                phase = SkScalarMod(phase, len);
+            }
+            *adjustedPhase = phase;
+        }
+        SkASSERT(phase >= 0 && phase < len);
+
+        *initialDashLength = find_first_interval(intervals, phase,
+                                                initialDashIndex, count);
+
+        SkASSERT(*initialDashLength >= 0);
+        SkASSERT(*initialDashIndex >= 0 && *initialDashIndex < count);
+    } else {
+        *initialDashLength = -1;    // signal bad dash intervals
+    }
+}
+
+static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) {
+    SkScalar radius = SkScalarHalf(rec.getWidth());
+    if (0 == radius) {
+        radius = SK_Scalar1;    // hairlines
+    }
+    if (SkPaint::kMiter_Join == rec.getJoin()) {
+        radius = SkScalarMul(radius, rec.getMiter());
+    }
+    rect->outset(radius, radius);
+}
+
+// Only handles lines for now. If returns true, dstPath is the new (smaller)
+// path. If returns false, then dstPath parameter is ignored.
+static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec,
+                      const SkRect* cullRect, SkScalar intervalLength,
+                      SkPath* dstPath) {
+    if (NULL == cullRect) {
+        return false;
+    }
+
+    SkPoint pts[2];
+    if (!srcPath.isLine(pts)) {
+        return false;
+    }
+
+    SkRect bounds = *cullRect;
+    outset_for_stroke(&bounds, rec);
+
+    SkScalar dx = pts[1].x() - pts[0].x();
+    SkScalar dy = pts[1].y() - pts[0].y();
+
+    // just do horizontal lines for now (lazy)
+    if (dy) {
+        return false;
+    }
+
+    SkScalar minX = pts[0].fX;
+    SkScalar maxX = pts[1].fX;
+
+    if (maxX < bounds.fLeft || minX > bounds.fRight) {
+        return false;
+    }
+
+    if (dx < 0) {
+        SkTSwap(minX, maxX);
+    }
+
+    // Now we actually perform the chop, removing the excess to the left and
+    // right of the bounds (keeping our new line "in phase" with the dash,
+    // hence the (mod intervalLength).
+
+    if (minX < bounds.fLeft) {
+        minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX,
+                                          intervalLength);
+    }
+    if (maxX > bounds.fRight) {
+        maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight,
+                                           intervalLength);
+    }
+
+    SkASSERT(maxX >= minX);
+    if (dx < 0) {
+        SkTSwap(minX, maxX);
+    }
+    pts[0].fX = minX;
+    pts[1].fX = maxX;
+
+    dstPath->moveTo(pts[0]);
+    dstPath->lineTo(pts[1]);
+    return true;
+}
+
+class SpecialLineRec {
+public:
+    bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec,
+              int intervalCount, SkScalar intervalLength) {
+        if (rec->isHairlineStyle() || !src.isLine(fPts)) {
+            return false;
+        }
+
+        // can relax this in the future, if we handle square and round caps
+        if (SkPaint::kButt_Cap != rec->getCap()) {
+            return false;
+        }
+
+        SkScalar pathLength = SkPoint::Distance(fPts[0], fPts[1]);
+
+        fTangent = fPts[1] - fPts[0];
+        if (fTangent.isZero()) {
+            return false;
+        }
+
+        fPathLength = pathLength;
+        fTangent.scale(SkScalarInvert(pathLength));
+        fTangent.rotateCCW(&fNormal);
+        fNormal.scale(SkScalarHalf(rec->getWidth()));
+
+        // now estimate how many quads will be added to the path
+        //     resulting segments = pathLen * intervalCount / intervalLen
+        //     resulting points = 4 * segments
+
+        SkScalar ptCount = SkScalarMulDiv(pathLength,
+                                          SkIntToScalar(intervalCount),
+                                          intervalLength);
+        int n = SkScalarCeilToInt(ptCount) << 2;
+        dst->incReserve(n);
+
+        // we will take care of the stroking
+        rec->setFillStyle();
+        return true;
+    }
+
+    void addSegment(SkScalar d0, SkScalar d1, SkPath* path) const {
+        SkASSERT(d0 < fPathLength);
+        // clamp the segment to our length
+        if (d1 > fPathLength) {
+            d1 = fPathLength;
+        }
+
+        SkScalar x0 = fPts[0].fX + SkScalarMul(fTangent.fX, d0);
+        SkScalar x1 = fPts[0].fX + SkScalarMul(fTangent.fX, d1);
+        SkScalar y0 = fPts[0].fY + SkScalarMul(fTangent.fY, d0);
+        SkScalar y1 = fPts[0].fY + SkScalarMul(fTangent.fY, d1);
+
+        SkPoint pts[4];
+        pts[0].set(x0 + fNormal.fX, y0 + fNormal.fY);   // moveTo
+        pts[1].set(x1 + fNormal.fX, y1 + fNormal.fY);   // lineTo
+        pts[2].set(x1 - fNormal.fX, y1 - fNormal.fY);   // lineTo
+        pts[3].set(x0 - fNormal.fX, y0 - fNormal.fY);   // lineTo
+
+        path->addPoly(pts, SK_ARRAY_COUNT(pts), false);
+    }
+
+private:
+    SkPoint fPts[2];
+    SkVector fTangent;
+    SkVector fNormal;
+    SkScalar fPathLength;
+};
+
+
+bool SkDashPath::FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
+                                const SkRect* cullRect, const SkScalar aIntervals[],
+                                int32_t count, SkScalar initialDashLength, int32_t initialDashIndex,
+                                SkScalar intervalLength) {
+
+    // we do nothing if the src wants to be filled, or if our dashlength is 0
+    if (rec->isFillStyle() || initialDashLength < 0) {
+        return false;
+    }
+
+    const SkScalar* intervals = aIntervals;
+    SkScalar        dashCount = 0;
+    int             segCount = 0;
+
+    SkPath cullPathStorage;
+    const SkPath* srcPtr = &src;
+    if (cull_path(src, *rec, cullRect, intervalLength, &cullPathStorage)) {
+        srcPtr = &cullPathStorage;
+    }
+
+    SpecialLineRec lineRec;
+    bool specialLine = lineRec.init(*srcPtr, dst, rec, count >> 1, intervalLength);
+
+    SkPathMeasure   meas(*srcPtr, false);
+
+    do {
+        bool        skipFirstSegment = meas.isClosed();
+        bool        addedSegment = false;
+        SkScalar    length = meas.getLength();
+        int         index = initialDashIndex;
+
+        // Since the path length / dash length ratio may be arbitrarily large, we can exert
+        // significant memory pressure while attempting to build the filtered path. To avoid this,
+        // we simply give up dashing beyond a certain threshold.
+        //
+        // The original bug report (http://crbug.com/165432) is based on a path yielding more than
+        // 90 million dash segments and crashing the memory allocator. A limit of 1 million
+        // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the
+        // maximum dash memory overhead at roughly 17MB per path.
+        static const SkScalar kMaxDashCount = 1000000;
+        dashCount += length * (count >> 1) / intervalLength;
+        if (dashCount > kMaxDashCount) {
+            dst->reset();
+            return false;
+        }
+
+        // Using double precision to avoid looping indefinitely due to single precision rounding
+        // (for extreme path_length/dash_length ratios). See test_infinite_dash() unittest.
+        double  distance = 0;
+        double  dlen = initialDashLength;
+
+        while (distance < length) {
+            SkASSERT(dlen >= 0);
+            addedSegment = false;
+            if (is_even(index) && dlen > 0 && !skipFirstSegment) {
+                addedSegment = true;
+                ++segCount;
+
+                if (specialLine) {
+                    lineRec.addSegment(SkDoubleToScalar(distance),
+                                       SkDoubleToScalar(distance + dlen),
+                                       dst);
+                } else {
+                    meas.getSegment(SkDoubleToScalar(distance),
+                                    SkDoubleToScalar(distance + dlen),
+                                    dst, true);
+                }
+            }
+            distance += dlen;
+
+            // clear this so we only respect it the first time around
+            skipFirstSegment = false;
+
+            // wrap around our intervals array if necessary
+            index += 1;
+            SkASSERT(index <= count);
+            if (index == count) {
+                index = 0;
+            }
+
+            // fetch our next dlen
+            dlen = intervals[index];
+        }
+
+        // extend if we ended on a segment and we need to join up with the (skipped) initial segment
+        if (meas.isClosed() && is_even(initialDashIndex) &&
+            initialDashLength > 0) {
+            meas.getSegment(0, initialDashLength, dst, !addedSegment);
+            ++segCount;
+        }
+    } while (meas.nextContour());
+
+    if (segCount > 1) {
+        dst->setConvexity(SkPath::kConcave_Convexity);
+    }
+
+    return true;
+}
+
+bool SkDashPath::FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
+                                const SkRect* cullRect, const SkPathEffect::DashInfo& info) {
+    SkScalar initialDashLength = 0;
+    int32_t initialDashIndex = 0;
+    SkScalar intervalLength = 0;
+    CalcDashParameters(info.fPhase, info.fIntervals, info.fCount,
+                       &initialDashLength, &initialDashIndex, &intervalLength);
+    return FilterDashPath(dst, src, rec, cullRect, info.fIntervals, info.fCount, initialDashLength,
+                          initialDashIndex, intervalLength);
+}
diff --git a/src/utils/SkDashPathPriv.h b/src/utils/SkDashPathPriv.h
new file mode 100644 (file)
index 0000000..94222a2
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDashPathPriv_DEFINED
+#define SkDashPathPriv_DEFINED
+
+#include "SkPathEffect.h"
+
+namespace SkDashPath {
+    /*
+     * Calculates the initialDashLength, initialDashIndex, and intervalLength based on the
+     * inputed phase and intervals. If adjustedPhase is passed in, then the phase will be
+     * adjusted to be between 0 and intervalLength. The result will be stored in adjustedPhase.
+     * If adjustedPhase is NULL then it is assumed phase is already between 0 and intervalLength
+     */
+    void CalcDashParameters(SkScalar phase, const SkScalar intervals[], int32_t count,
+                            SkScalar* initialDashLength, int32_t* initialDashIndex,
+                            SkScalar* intervalLength, SkScalar* adjustedPhase = NULL);
+
+    bool FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
+                        const SkScalar aIntervals[], int32_t count, SkScalar initialDashLength,
+                        int32_t initialDashIndex, SkScalar intervalLength);
+    
+    bool FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
+                        const SkPathEffect::DashInfo& info);
+}
+
+#endif