Index: src/core/SkPathMeasure.cpp |
diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp |
index eb80cf3b831acd3f4493610f8bc0ab2da87daf7d..67862d5e40307b799f2c96ae42ea28ec0d5917d4 100644 |
--- a/src/core/SkPathMeasure.cpp |
+++ b/src/core/SkPathMeasure.cpp |
@@ -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: |