fix circular dashing
authorcaryclark <caryclark@google.com>
Tue, 19 Jan 2016 16:07:49 +0000 (08:07 -0800)
committerCommit bot <commit-bot@chromium.org>
Tue, 19 Jan 2016 16:07:50 +0000 (08:07 -0800)
Path measure cannot use the same code approach for quadratics
and cubics. Subdividing cubics repeatedly does not result in
subdivided t values, e.g. a quarter circle cubic divided in
half twice does not have a t value equivalent to 1/4.

Instead, always compute the cubic segment from a pair of
t values.

When finding the length of the cubic through recursive measures,
it is enough to carry the point at a given t to the next
subdivision.

(Chrome suppression has landed already.)

R=reed@google.com
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1602153002

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

gm/dashcircle.cpp [new file with mode: 0644]
include/core/SkPathMeasure.h
samplecode/SampleQuadStroker.cpp
src/core/SkGeometry.cpp
src/core/SkGeometry.h
src/core/SkPathMeasure.cpp
tests/PathMeasureTest.cpp

diff --git a/gm/dashcircle.cpp b/gm/dashcircle.cpp
new file mode 100644 (file)
index 0000000..cddd913
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkPath.h"
+#include "SkDashPathEffect.h"
+
+int dash1[] = { 1, 1 };
+int dash2[] = { 1, 3 };
+int dash3[] = { 1, 1, 3, 3 };
+int dash4[] = { 1, 3, 2, 4 };
+
+struct DashExample {
+    int* pattern;
+    int length;
+} dashExamples[] = {
+    { dash1, SK_ARRAY_COUNT(dash1) },
+    { dash2, SK_ARRAY_COUNT(dash2) },
+    { dash3, SK_ARRAY_COUNT(dash3) },
+    { dash4, SK_ARRAY_COUNT(dash4) }
+};
+
+DEF_SIMPLE_GM(dashcircle, canvas, 900, 1200) {
+    SkPaint refPaint;
+    refPaint.setAntiAlias(true);
+    refPaint.setColor(0xFFbf3f7f);
+    refPaint.setStyle(SkPaint::kStroke_Style);
+    refPaint.setStrokeWidth(1);
+    const SkScalar radius = 125;
+    SkRect oval = SkRect::MakeLTRB(-radius - 20, -radius - 20, radius + 20, radius + 20);
+    SkPath circle;
+    circle.addCircle(0, 0, radius);
+    SkScalar circumference = radius * SK_ScalarPI * 2;
+    int wedges[] = { 6, 12, 36 };
+    canvas->translate(radius + 20, radius + 20);
+    for (int wedge : wedges) {
+        SkScalar arcLength = 360.f / wedge;
+        canvas->save();
+        for (const DashExample& dashExample : dashExamples) {
+            SkPath refPath;
+            int dashUnits = 0;
+            for (int index = 0; index < dashExample.length; ++index) {
+                dashUnits += dashExample.pattern[index];
+            }
+            SkScalar unitLength = arcLength / dashUnits;
+            SkScalar angle = 0;
+            for (int index = 0; index < wedge; ++index) {
+                for (int i2 = 0; i2 < dashExample.length; i2 += 2) {
+                    SkScalar span = dashExample.pattern[i2] * unitLength;
+                    refPath.moveTo(0, 0);
+                    refPath.arcTo(oval, angle, span, false);
+                    refPath.close();
+                    angle += span + (dashExample.pattern[i2 + 1]) * unitLength;
+                }
+            }
+            canvas->drawPath(refPath, refPaint);
+            SkPaint p;
+            p.setAntiAlias(true);
+            p.setStyle(SkPaint::kStroke_Style);
+            p.setStrokeWidth(10);
+            SkScalar intervals[4];
+            int intervalCount = dashExample.length;
+            SkScalar dashLength = circumference / wedge / dashUnits;
+            for (int index = 0; index < dashExample.length; ++index) {
+                intervals[index] = dashExample.pattern[index] * dashLength;
+            }
+            p.setPathEffect(SkDashPathEffect::Create(intervals, intervalCount, 0))->unref();
+            canvas->drawPath(circle, p);
+            canvas->translate(0, radius * 2 + 50);
+        }
+        canvas->restore();
+        canvas->translate(radius * 2  + 50, 0);
+    }
+}
index 7528736..415ee27 100644 (file)
@@ -107,7 +107,13 @@ private:
     void     buildSegments();
     SkScalar compute_quad_segs(const SkPoint pts[3], SkScalar distance,
                                 int mint, int maxt, int ptIndex);
+#ifdef SK_SUPPORT_LEGACY_CONIC_MEASURE
     SkScalar compute_conic_segs(const SkConic&, SkScalar distance, int mint, int maxt, int ptIndex);
+#else
+    SkScalar compute_conic_segs(const SkConic&, SkScalar distance,
+                                int mint, const SkPoint& minPt,
+                                int maxt, const SkPoint& maxPt, int ptIndex);
+#endif
     SkScalar compute_cubic_segs(const SkPoint pts[3], SkScalar distance,
                                 int mint, int maxt, int ptIndex);
     const Segment* distanceToSegment(SkScalar distance, SkScalar* t);
index 3b02980..88f7dfd 100644 (file)
@@ -9,6 +9,7 @@
 #include "SampleCode.h"
 #include "SkView.h"
 #include "SkCanvas.h"
+#include "SkGeometry.h"
 #include "SkPathMeasure.h"
 #include "SkRandom.h"
 #include "SkRRect.h"
@@ -122,6 +123,7 @@ class QuadStrokerView : public SampleView {
     bool fAnimate;
     bool fDrawRibs;
     bool fDrawTangents;
+    bool fDrawTDivs;
 #ifdef SK_DEBUG
     #define kStrokerErrorMin 0.001f
     #define kStrokerErrorMax 5
@@ -288,16 +290,84 @@ protected:
         SkScalar total = meas.getLength();
 
         SkScalar delta = 8;
-        SkPaint paint;
+        SkPaint paint, labelP;
         paint.setColor(color);
-
+        labelP.setColor(color & 0xff5f9f5f);
         SkPoint pos, tan;
+        int index = 0;
         for (SkScalar dist = 0; dist <= total; dist += delta) {
             if (meas.getPosTan(dist, &pos, &tan)) {
                 tan.scale(radius);
                 tan.rotateCCW();
                 canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
                                  pos.x() - tan.x(), pos.y() - tan.y(), paint);
+                if (0 == index % 10) {
+                    SkString label;
+                    label.appendS32(index);
+                    SkRect dot = SkRect::MakeXYWH(pos.x() - 2, pos.y() - 2, 4, 4);
+                    canvas->drawRect(dot, labelP);
+                    canvas->drawText(label.c_str(), label.size(),
+                        pos.x() - tan.x() * 1.25f, pos.y() - tan.y() * 1.25f, labelP);
+                }
+            }
+            ++index;
+        }
+    }
+
+    void draw_t_divs(SkCanvas* canvas, const SkPath& path, SkScalar width, SkColor color) {
+        const SkScalar radius = width / 2;
+        SkPaint paint;
+        paint.setColor(color);
+        SkPathMeasure meas(path, false);
+        SkScalar total = meas.getLength();
+        SkScalar delta = 8;
+        int ribs = 0;
+        for (SkScalar dist = 0; dist <= total; dist += delta) {
+            ++ribs;
+        }
+        SkPath::RawIter iter(path);
+        SkPoint pts[4];
+        if (SkPath::kMove_Verb != iter.next(pts)) {
+            SkASSERT(0);
+            return;
+        }
+        SkPath::Verb verb = iter.next(pts);
+        SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
+        SkPoint pos, tan;
+        for (int index = 0; index < ribs; ++index) {
+            SkScalar t = (SkScalar) index / ribs;
+            switch (verb) {
+                case SkPath::kLine_Verb:
+                    tan = pts[1] - pts[0];
+                    pos = pts[0];
+                    pos.fX += tan.fX * t;
+                    pos.fY += tan.fY * t;
+                    break;
+                case SkPath::kQuad_Verb:
+                    pos = SkEvalQuadAt(pts, t);
+                    tan = SkEvalQuadTangentAt(pts, t);
+                    break;
+                case SkPath::kConic_Verb: {
+                    SkConic conic(pts, iter.conicWeight());
+                    pos = conic.evalAt(t);
+                    tan = conic.evalTangentAt(t);
+                    } break;
+                case SkPath::kCubic_Verb:
+                    SkEvalCubicAt(pts, t, &pos, &tan, nullptr);
+                    break;
+                default:
+                    SkASSERT(0);
+                    return;
+            }
+            tan.setLength(radius);
+            tan.rotateCCW();
+            canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
+                                pos.x() - tan.x(), pos.y() - tan.y(), paint);
+            if (0 == index % 10) {
+                SkString label;
+                label.appendS32(index);
+                canvas->drawText(label.c_str(), label.size(),
+                    pos.x() + tan.x() * 1.25f, pos.y() + tan.y() * 1.25f, paint);
             }
         }
     }
@@ -343,6 +413,10 @@ protected:
             draw_ribs(canvas, scaled, width, 0xFF00FF00);
         }
 
+        if (fDrawTDivs) {
+            draw_t_divs(canvas, scaled, width, 0xFF3F3F00);
+        }
+
         SkPath fill;
 
         SkPaint p;
@@ -428,17 +502,24 @@ protected:
     void setForGeometry() {
         fDrawRibs = true;
         fDrawTangents = true;
+        fDrawTDivs = false;
         fWidthScale = 1;
     }
 
     void setForText() {
-        fDrawRibs = fDrawTangents = false;
+        fDrawRibs = fDrawTangents = fDrawTDivs = false;
         fWidthScale = 0.002f;
     }
 
+    void setForSingles() {
+        setForGeometry();
+        fDrawTDivs = true;
+    }
+
     void setAsNeeded() {
-        if (fConicButton.fEnabled || fCubicButton.fEnabled || fQuadButton.fEnabled
-                || fRRectButton.fEnabled || fCircleButton.fEnabled) {
+        if (fConicButton.fEnabled || fCubicButton.fEnabled || fQuadButton.fEnabled) {
+            setForSingles();
+        } else if (fRRectButton.fEnabled || fCircleButton.fEnabled) {
             setForGeometry();
         } else {
             setForText();
@@ -452,14 +533,15 @@ protected:
         if (fCubicButton.fEnabled) {
             path.moveTo(fPts[0]);
             path.cubicTo(fPts[1], fPts[2], fPts[3]);
-            setForGeometry();
+            setForSingles();
             draw_stroke(canvas, path, width, 950, false);
         }
 
         if (fConicButton.fEnabled) {
+            path.reset();
             path.moveTo(fPts[4]);
             path.conicTo(fPts[5], fPts[6], fWeight);
-            setForGeometry();
+            setForSingles();
             draw_stroke(canvas, path, width, 950, false);
         }
 
@@ -467,7 +549,7 @@ protected:
             path.reset();
             path.moveTo(fPts[7]);
             path.quadTo(fPts[8], fPts[9]);
-            setForGeometry();
+            setForSingles();
             draw_stroke(canvas, path, width, 950, false);
         }
 
index c25e186..2ea3095 100644 (file)
@@ -9,18 +9,6 @@
 #include "SkMatrix.h"
 #include "SkNx.h"
 
-#if 0
-static Sk2s from_point(const SkPoint& point) {
-    return Sk2s::Load(&point.fX);
-}
-
-static SkPoint to_point(const Sk2s& x) {
-    SkPoint point;
-    x.store(&point.fX);
-    return point;
-}
-#endif
-
 static SkVector to_vector(const Sk2s& x) {
     SkVector vector;
     x.store(&vector.fX);
@@ -220,7 +208,7 @@ void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t) {
 }
 
 void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) {
-    SkChopQuadAt(src, dst, 0.5f); return;
+    SkChopQuadAt(src, dst, 0.5f);
 }
 
 /** Quad'(t) = At + B, where
@@ -1246,8 +1234,34 @@ void SkConic::chopAt(SkScalar t, SkConic dst[2]) const {
     dst[1].fW = tmp2[2].fZ / root;
 }
 
-static Sk2s times_2(const Sk2s& value) {
-    return value + value;
+void SkConic::chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const {
+    if (0 == t1 || 1 == t2) {
+        if (0 == t1 && 1 == t2) {
+            *dst = *this;
+        } else {
+            SkConic pair[2];
+            this->chopAt(t1 ? t1 : t2, pair);
+            *dst = pair[SkToBool(t1)];
+        }
+        return;
+    }
+    SkConicCoeff coeff(*this);
+    Sk2s tt1(t1);
+    Sk2s aXY = coeff.fNumer.eval(tt1);
+    Sk2s aZZ = coeff.fDenom.eval(tt1);
+    Sk2s midTT((t1 + t2) / 2);
+    Sk2s dXY = coeff.fNumer.eval(midTT);
+    Sk2s dZZ = coeff.fDenom.eval(midTT);
+    Sk2s tt2(t2);
+    Sk2s cXY = coeff.fNumer.eval(tt2);
+    Sk2s cZZ = coeff.fDenom.eval(tt2);
+    Sk2s bXY = times_2(dXY) - (aXY + cXY) * Sk2s(0.5f);
+    Sk2s bZZ = times_2(dZZ) - (aZZ + cZZ) * Sk2s(0.5f);
+    dst->fPts[0] = to_point(aXY / aZZ);
+    dst->fPts[1] = to_point(bXY / bZZ);
+    dst->fPts[2] = to_point(cXY / cZZ);
+    Sk2s ww = bZZ / (aZZ * cZZ).sqrt();
+    dst->fW = ww.kth<0>();
 }
 
 SkPoint SkConic::evalAt(SkScalar t) const {
index 79cad2e..0fd5a61 100644 (file)
@@ -23,7 +23,11 @@ static inline SkPoint to_point(const Sk2s& x) {
 
 static inline Sk2s sk2s_cubic_eval(const Sk2s& A, const Sk2s& B, const Sk2s& C, const Sk2s& D,
                                    const Sk2s& t) {
-    return ((A * t + B) * t + C) * t + D;
+    return ((A * t + B) * t + C) * t + D;      
+}
+
+static Sk2s times_2(const Sk2s& value) {
+    return value + value;
 }
 
 /** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the
@@ -42,10 +46,10 @@ SkPoint SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t);
 void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent = nullptr);
 
 /**
- *  output is : eval(t) == coeff[0] * t^2 + coeff[1] * t + coeff[2]
- */
+ *  output is : eval(t) == coeff[0] * t^2 + coeff[1] * t + coeff[2]    
+ */    
 void SkQuadToCoeff(const SkPoint pts[3], SkPoint coeff[3]);
-
+  
 /**
  *  output is : eval(t) == coeff[0] * t^3 + coeff[1] * t^2 + coeff[2] * t + coeff[3]
  */
@@ -241,6 +245,7 @@ struct SkConic {
      */
     void evalAt(SkScalar t, SkPoint* pos, SkVector* tangent = nullptr) const;
     void chopAt(SkScalar t, SkConic dst[2]) const;
+    void chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const;
     void chop(SkConic dst[2]) const;
 
     SkPoint evalAt(SkScalar t) const;
@@ -287,6 +292,102 @@ struct SkConic {
                             const SkMatrix*, SkConic conics[kMaxConicsForArc]);
 };
 
+// inline helpers are contained in a namespace to avoid external leakage to fragile SkNx members
+namespace {
+
+/**
+ *  use for : eval(t) == A * t^2 + B * t + C
+ */
+struct SkQuadCoeff {
+    SkQuadCoeff() {}
+
+    SkQuadCoeff(const Sk2s& A, const Sk2s& B, const Sk2s& C)
+        : fA(A)
+        , fB(B)
+        , fC(C)
+    {
+    }
+
+    SkQuadCoeff(const SkPoint src[3]) {
+        fC = from_point(src[0]);
+        Sk2s P1 = from_point(src[1]);
+        Sk2s P2 = from_point(src[2]);
+        fB = times_2(P1 - fC);
+        fA = P2 - times_2(P1) + fC;
+    }
+
+    Sk2s eval(SkScalar t) {
+        Sk2s tt(t);
+        return eval(tt);
+    }
+
+    Sk2s eval(const Sk2s& tt) {
+        return (fA * tt + fB) * tt + fC;
+    }
+
+    Sk2s fA;
+    Sk2s fB;
+    Sk2s fC;
+};
+
+struct SkConicCoeff {
+    SkConicCoeff(const SkConic& conic) {
+        Sk2s p0 = from_point(conic.fPts[0]);
+        Sk2s p1 = from_point(conic.fPts[1]);
+        Sk2s p2 = from_point(conic.fPts[2]);
+        Sk2s ww(conic.fW);
+
+        Sk2s p1w = p1 * ww;
+        fNumer.fC = p0;
+        fNumer.fA = p2 - times_2(p1w) + p0;
+        fNumer.fB = times_2(p1w - p0);
+
+        fDenom.fC = Sk2s(1);
+        fDenom.fB = times_2(ww - fDenom.fC);
+        fDenom.fA = Sk2s(0) - fDenom.fB;
+    }
+
+    Sk2s eval(SkScalar t) {
+        Sk2s tt(t);
+        Sk2s numer = fNumer.eval(tt);
+        Sk2s denom = fDenom.eval(tt);
+        return numer / denom;
+    }
+
+    SkQuadCoeff fNumer;
+    SkQuadCoeff fDenom;
+};
+
+struct SkCubicCoeff {
+    SkCubicCoeff(const SkPoint src[4]) {
+        Sk2s P0 = from_point(src[0]);
+        Sk2s P1 = from_point(src[1]);
+        Sk2s P2 = from_point(src[2]);
+        Sk2s P3 = from_point(src[3]);
+        Sk2s three(3);
+        fA = P3 + three * (P1 - P2) - P0;
+        fB = three * (P2 - times_2(P1) + P0);
+        fC = three * (P1 - P0);
+        fD = P0;
+    }
+
+    Sk2s eval(SkScalar t) {
+        Sk2s tt(t);
+        return eval(tt);
+    }
+
+    Sk2s eval(const Sk2s& t) {
+        return ((fA * t + fB) * t + fC) * t + fD;
+    }
+
+    Sk2s fA;
+    Sk2s fB;
+    Sk2s fC;
+    Sk2s fD;
+};
+
+}
+
 #include "SkTemplates.h"
 
 /**
index eb80cf3..bc53503 100644 (file)
@@ -73,6 +73,15 @@ static bool quad_too_curvy(const SkPoint pts[3]) {
     return dist > CHEAP_DIST_LIMIT;
 }
 
+static bool conic_too_curvy(const SkPoint& firstPt, const SkPoint& midTPt,
+                            const SkPoint& lastPt) {
+    SkPoint midEnds = firstPt + lastPt;
+    midEnds *= 0.5f;
+    SkVector dxy = midTPt - midEnds;
+    SkScalar dist = SkMaxScalar(SkScalarAbs(dxy.fX), SkScalarAbs(dxy.fY));
+    return dist > CHEAP_DIST_LIMIT;
+}
+
 static bool cheap_dist_exceeds_limit(const SkPoint& pt,
                                      SkScalar x, SkScalar y) {
     SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY));
@@ -90,28 +99,58 @@ static bool cubic_too_curvy(const SkPoint pts[4]) {
                          SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3));
 }
 
+static SkScalar quad_folded_len(const SkPoint pts[3]) {
+    SkScalar t = SkFindQuadMaxCurvature(pts);
+    SkPoint pt = SkEvalQuadAt(pts, t);
+    SkVector a = pts[2] - pt;
+    SkScalar result = a.length();
+    if (0 != t) {
+        SkVector b = pts[0] - pt;
+        result += b.length();
+    }
+    SkASSERT(SkScalarIsFinite(result));
+    return result;
+}
+
 /* from http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/ */
+/* This works -- more needs to be done to see if it is performant on all platforms.
+   To use this to measure parts of quads requires recomputing everything -- perhaps
+   a chop-like interface can start from a larger measurement and get two new measurements
+   with one call here.
+ */
 static SkScalar compute_quad_len(const SkPoint pts[3]) {
-     SkPoint a,b;
-     a.fX = pts[0].fX - 2 * pts[1].fX + pts[2].fX;
-     a.fY = pts[0].fY - 2 * pts[1].fY + pts[2].fY;
-     b.fX = 2 * (pts[1].fX - pts[0].fX);
-     b.fY = 2 * (pts[1].fY - pts[0].fY);
-     SkScalar A = 4 * (a.fX * a.fX + a.fY * a.fY);
-     SkScalar B = 4 * (a.fX * b.fX + a.fY * b.fY);
-     SkScalar C =      b.fX * b.fX + b.fY * b.fY;
-
-     SkScalar Sabc = 2 * SkScalarSqrt(A + B + C);
-     SkScalar A_2  = SkScalarSqrt(A);
-     SkScalar A_32 = 2 * A * A_2;
-     SkScalar C_2  = 2 * SkScalarSqrt(C);
-     SkScalar BA   = B / A_2;
-
-     return (A_32 * Sabc + A_2 * B * (Sabc - C_2) +
-            (4 * C * A - B * B) * SkScalarLog((2 * A_2 + BA + Sabc) / (BA + C_2))) / (4 * A_32);
+    SkPoint a,b;
+    a.fX = pts[0].fX - 2 * pts[1].fX + pts[2].fX;
+    a.fY = pts[0].fY - 2 * pts[1].fY + pts[2].fY;
+    SkScalar A = 4 * (a.fX * a.fX + a.fY * a.fY);
+    if (0 == A) {
+        a = pts[2] - pts[0];
+        return a.length();
+    }
+    b.fX = 2 * (pts[1].fX - pts[0].fX);
+    b.fY = 2 * (pts[1].fY - pts[0].fY);
+    SkScalar B = 4 * (a.fX * b.fX + a.fY * b.fY);
+    SkScalar C =      b.fX * b.fX + b.fY * b.fY;
+    SkScalar Sabc = 2 * SkScalarSqrt(A + B + C);
+    SkScalar A_2  = SkScalarSqrt(A);
+    SkScalar A_32 = 2 * A * A_2;
+    SkScalar C_2  = 2 * SkScalarSqrt(C);
+    SkScalar BA   = B / A_2;
+    if (0 == BA + C_2) {
+        return quad_folded_len(pts);
+    }
+    SkScalar J = A_32 * Sabc + A_2 * B * (Sabc - C_2);
+    SkScalar K = 4 * C * A - B * B;
+    SkScalar L = (2 * A_2 + BA + Sabc) / (BA + C_2);
+    if (L <= 0) {
+        return quad_folded_len(pts);
+    }
+    SkScalar M = SkScalarLog(L);
+    SkScalar result = (J + K * M) / (4 * A_32);
+    SkASSERT(SkScalarIsFinite(result));
+    return result;
 }
 
-
 SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3],
                           SkScalar distance, int mint, int maxt, int ptIndex) {
     if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) {
@@ -136,6 +175,7 @@ SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3],
     return distance;
 }
 
+#ifdef SK_SUPPORT_LEGACY_CONIC_MEASURE
 SkScalar SkPathMeasure::compute_conic_segs(const SkConic& conic,
                                            SkScalar distance, int mint, int maxt, int ptIndex) {
     if (tspan_big_enough(maxt - mint) && quad_too_curvy(conic.fPts)) {
@@ -159,6 +199,30 @@ SkScalar SkPathMeasure::compute_conic_segs(const SkConic& conic,
     }
     return distance;
 }
+#else
+SkScalar SkPathMeasure::compute_conic_segs(const SkConic& conic, SkScalar distance,
+                                           int mint, const SkPoint& minPt,
+                                           int maxt, const SkPoint& maxPt, int ptIndex) {
+    int halft = (mint + maxt) >> 1;
+    SkPoint halfPt = conic.evalAt(tValue2Scalar(halft));
+    if (tspan_big_enough(maxt - mint) && conic_too_curvy(minPt, halfPt, maxPt)) {
+        distance = this->compute_conic_segs(conic, distance, mint, minPt, halft, halfPt, ptIndex);
+        distance = this->compute_conic_segs(conic, distance, halft, halfPt, maxt, maxPt, ptIndex);
+    } else {
+        SkScalar d = SkPoint::Distance(minPt, maxPt);
+        SkScalar prevD = distance;
+        distance += d;
+        if (distance > prevD) {
+            Segment* seg = fSegments.append();
+            seg->fDistance = distance;
+            seg->fPtIndex = ptIndex;
+            seg->fType = kConic_SegType;
+            seg->fTValue = maxt;
+        }
+    }
+    return distance;
+}
+#endif
 
 SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4],
                            SkScalar distance, int mint, int maxt, int ptIndex) {
@@ -253,7 +317,12 @@ void SkPathMeasure::buildSegments() {
             case SkPath::kConic_Verb: {
                 const SkConic conic(pts, fIter.conicWeight());
                 SkScalar prevD = distance;
+#ifdef SK_SUPPORT_LEGACY_CONIC_MEASURE
                 distance = this->compute_conic_segs(conic, distance, 0, kMaxTValue, ptIndex);
+#else
+                distance = this->compute_conic_segs(conic, distance, 0, conic.fPts[0],
+                                                    kMaxTValue, conic.fPts[2], ptIndex);
+#endif
                 if (distance > prevD) {
                     // we store the conic weight in our next point, followed by the last 2 pts
                     // thus to reconstitue a conic, you'd need to say
@@ -406,7 +475,8 @@ static void seg_to(const SkPoint pts[], int segType,
                     dst->conicTo(tmp[0].fPts[1], tmp[0].fPts[2], tmp[0].fW);
                 }
             } else {
-                SkConic tmp1[2];
+#ifdef SK_SUPPORT_LEGACY_CONIC_MEASURE
+                SkConic tmp1[2];       
                 conic.chopAt(startT, tmp1);
                 if (SK_Scalar1 == stopT) {
                     dst->conicTo(tmp1[1].fPts[1], tmp1[1].fPts[2], tmp1[1].fW);
@@ -415,6 +485,17 @@ static void seg_to(const SkPoint pts[], int segType,
                     tmp1[1].chopAt((stopT - startT) / (SK_Scalar1 - startT), tmp2);
                     dst->conicTo(tmp2[0].fPts[1], tmp2[0].fPts[2], tmp2[0].fW);
                 }
+#else
+                if (SK_Scalar1 == stopT) {
+                    SkConic tmp1[2];
+                    conic.chopAt(startT, tmp1);
+                    dst->conicTo(tmp1[1].fPts[1], tmp1[1].fPts[2], tmp1[1].fW);
+                } else {
+                    SkConic tmp;
+                    conic.chopAt(startT, stopT, &tmp);
+                    dst->conicTo(tmp.fPts[1], tmp.fPts[2], tmp.fW);
+                }
+#endif
             }
         } break;
         case kCubic_SegType:
index 578f4eb..df66578 100644 (file)
@@ -201,3 +201,19 @@ DEF_TEST(PathMeasure, reporter) {
     test_small_segment2();
     test_small_segment3();
 }
+
+DEF_TEST(PathMeasureConic, reporter) {
+    SkPoint stdP, hiP, pts[] = {{0,0}, {100,0}, {100,0}};
+    SkPath p;
+    p.moveTo(0, 0);
+    p.conicTo(pts[1], pts[2], 1);
+    SkPathMeasure stdm(p, false);
+    REPORTER_ASSERT(reporter, stdm.getPosTan(20, &stdP, nullptr));
+    p.reset();
+    p.moveTo(0, 0);
+    p.conicTo(pts[1], pts[2], 10);
+    stdm.setPath(&p, false);
+    REPORTER_ASSERT(reporter, stdm.getPosTan(20, &hiP, nullptr));
+    REPORTER_ASSERT(reporter, 19.5f < stdP.fX && stdP.fX < 20.5f);
+    REPORTER_ASSERT(reporter, 19.5f < hiP.fX && hiP.fX < 20.5f);
+}