From 4ab47e087ecfc82f070cbbaef4d9eb562d3fd163 Mon Sep 17 00:00:00 2001 From: hstern Date: Wed, 10 Aug 2016 10:55:09 -0700 Subject: [PATCH] Refactor SkCurveMeasure to use existing eval code - Use quad, cubic, conic eval code from SkGeometry.h - Implement evaluateDerivativeLength, evaluateDerivative and evaluate switch cases for lines along with the refactor BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2226973004 Review-Url: https://codereview.chromium.org/2226973004 --- src/utils/SkCurveMeasure.cpp | 136 +++++++++++++++++++++-------------- src/utils/SkCurveMeasure.h | 16 +++-- 2 files changed, 92 insertions(+), 60 deletions(-) diff --git a/src/utils/SkCurveMeasure.cpp b/src/utils/SkCurveMeasure.cpp index fc2aa84faa..823f56adcf 100644 --- a/src/utils/SkCurveMeasure.cpp +++ b/src/utils/SkCurveMeasure.cpp @@ -6,10 +6,66 @@ */ #include "SkCurveMeasure.h" +#include "SkGeometry.h" // for abs #include +#define UNIMPLEMENTED SkDEBUGF(("%s:%d unimplemented\n", __FILE__, __LINE__)) + +/// Used inside SkCurveMeasure::getTime's Newton's iteration +static inline SkPoint evaluate(const SkPoint pts[4], SkSegType segType, + SkScalar t) { + SkPoint pos; + switch (segType) { + case kQuad_SegType: + pos = SkEvalQuadAt(pts, t); + break; + case kLine_SegType: + pos = SkPoint::Make(SkScalarInterp(pts[0].x(), pts[1].x(), t), + SkScalarInterp(pts[0].y(), pts[1].y(), t)); + break; + case kCubic_SegType: + SkEvalCubicAt(pts, t, &pos, nullptr, nullptr); + break; + case kConic_SegType: { + SkConic conic(pts, pts[3].x()); + conic.evalAt(t, &pos); + } + break; + default: + UNIMPLEMENTED; + } + + return pos; +} + +/// Used inside SkCurveMeasure::getTime's Newton's iteration +static inline SkVector evaluateDerivative(const SkPoint pts[4], + SkSegType segType, SkScalar t) { + SkVector tan; + switch (segType) { + case kQuad_SegType: + tan = SkEvalQuadTangentAt(pts, t); + break; + case kLine_SegType: + tan = pts[1] - pts[0]; + break; + case kCubic_SegType: + SkEvalCubicAt(pts, t, nullptr, &tan, nullptr); + break; + case kConic_SegType: { + SkConic conic(pts, pts[3].x()); + conic.evalAt(t, nullptr, &tan); + } + break; + default: + UNIMPLEMENTED; + } + + return tan; +} +/// Used in ArcLengthIntegrator::computeLength static inline Sk8f evaluateDerivativeLength(const Sk8f& ts, const Sk8f (&xCoeff)[3], const Sk8f (&yCoeff)[3], @@ -22,17 +78,18 @@ static inline Sk8f evaluateDerivativeLength(const Sk8f& ts, y = yCoeff[0]*ts + yCoeff[1]; break; case kLine_SegType: - SkDebugf("Unimplemented"); - break; + // length of line derivative is constant + // and we precompute it in the constructor + return xCoeff[0]; case kCubic_SegType: x = (xCoeff[0]*ts + xCoeff[1])*ts + xCoeff[2]; y = (yCoeff[0]*ts + yCoeff[1])*ts + yCoeff[2]; break; case kConic_SegType: - SkDebugf("Unimplemented"); + UNIMPLEMENTED; break; default: - SkDebugf("Unimplemented"); + UNIMPLEMENTED; } x = x * x; @@ -40,6 +97,7 @@ static inline Sk8f evaluateDerivativeLength(const Sk8f& ts, return (x + y).sqrt(); } + ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType) : fSegType(segType) { switch (fSegType) { @@ -59,8 +117,13 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType) yCoeff[1] = Sk8f(2.0f*(By - Ay)); } break; - case kLine_SegType: - SkDEBUGF(("Unimplemented")); + case kLine_SegType: { + // the length of the derivative of a line is constant + // we put in in both coeff arrays for consistency's sake + SkScalar length = (pts[1] - pts[0]).length(); + xCoeff[0] = Sk8f(length); + yCoeff[0] = Sk8f(length); + } break; case kCubic_SegType: { @@ -73,6 +136,7 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType) float Cy = pts[2].y(); float Dy = pts[3].y(); + // precompute coefficients for derivative xCoeff[0] = Sk8f(3.0f*(-Ax + 3.0f*(Bx - Cx) + Dx)); xCoeff[1] = Sk8f(3.0f*(2.0f*(Ax - 2.0f*Bx + Cx))); xCoeff[2] = Sk8f(3.0f*(-Ax + Bx)); @@ -83,10 +147,10 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType) } break; case kConic_SegType: - SkDEBUGF(("Unimplemented")); + UNIMPLEMENTED; break; default: - SkDEBUGF(("Unimplemented")); + UNIMPLEMENTED; } } @@ -117,7 +181,8 @@ SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType) } break; case SkSegType::kLine_SegType: - SkDebugf("Unimplemented"); + fPts[0] = pts[0]; + fPts[1] = pts[1]; break; case SkSegType::kCubic_SegType: for (size_t i = 0; i < 4; i++) { @@ -125,10 +190,12 @@ SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType) } break; case SkSegType::kConic_SegType: - SkDebugf("Unimplemented"); + for (size_t i = 0; i < 4; i++) { + fPts[i] = pts[i]; + } break; default: - SkDEBUGF(("Unimplemented")); + UNIMPLEMENTED; break; } fIntegrator = ArcLengthIntegrator(fPts, fSegType); @@ -199,9 +266,8 @@ SkScalar SkCurveMeasure::getTime(SkScalar targetLength) { prevT = currentT; if (iterations < kNewtonIters) { - // TODO(hstern) switch here on curve type. // This is just newton's formula. - SkScalar dt = evaluateQuadDerivative(currentT).length(); + SkScalar dt = evaluateDerivative(fPts, fSegType, currentT).length(); newT = currentT - (lengthDiff / dt); // If newT is out of bounds, bisect inside newton. @@ -218,7 +284,7 @@ SkScalar SkCurveMeasure::getTime(SkScalar targetLength) { newT = (minT + maxT) * 0.5f; } else { SkDEBUGF(("%.7f %.7f didn't get close enough after bisection.\n", - currentT, currentLength)); + currentT, currentLength)); break; } currentT = newT; @@ -235,52 +301,16 @@ SkScalar SkCurveMeasure::getTime(SkScalar targetLength) { } void SkCurveMeasure::getPosTanTime(SkScalar targetLength, SkPoint* pos, - SkVector* tan, SkScalar* time) { + SkVector* tan, SkScalar* time) { SkScalar t = getTime(targetLength); if (time) { *time = t; } if (pos) { - // TODO(hstern) switch here on curve type. - *pos = evaluateQuad(t); + *pos = evaluate(fPts, fSegType, t); } if (tan) { - // TODO(hstern) switch here on curve type. - *tan = evaluateQuadDerivative(t); + *tan = evaluateDerivative(fPts, fSegType, t); } } - -// this is why I feel that the ArcLengthIntegrator should be combined -// with some sort of evaluator that caches the constants computed from the -// control points. this is basically the same code in ArcLengthIntegrator -SkPoint SkCurveMeasure::evaluateQuad(SkScalar t) { - SkScalar ti = 1.0f - t; - - SkScalar Ax = fPts[0].x(); - SkScalar Bx = fPts[1].x(); - SkScalar Cx = fPts[2].x(); - SkScalar Ay = fPts[0].y(); - SkScalar By = fPts[1].y(); - SkScalar Cy = fPts[2].y(); - - SkScalar x = Ax*ti*ti + 2.0f*Bx*t*ti + Cx*t*t; - SkScalar y = Ay*ti*ti + 2.0f*By*t*ti + Cy*t*t; - return SkPoint::Make(x, y); -} - -SkVector SkCurveMeasure::evaluateQuadDerivative(SkScalar t) { - SkScalar Ax = fPts[0].x(); - SkScalar Bx = fPts[1].x(); - SkScalar Cx = fPts[2].x(); - SkScalar Ay = fPts[0].y(); - SkScalar By = fPts[1].y(); - SkScalar Cy = fPts[2].y(); - - SkScalar A2BCx = 2.0f*(Ax - 2*Bx + Cx); - SkScalar A2BCy = 2.0f*(Ay - 2*By + Cy); - SkScalar ABx = 2.0f*(Bx - Ax); - SkScalar ABy = 2.0f*(By - Ay); - - return SkPoint::Make(A2BCx*t + ABx, A2BCy*t + ABy); -} diff --git a/src/utils/SkCurveMeasure.h b/src/utils/SkCurveMeasure.h index 2846103633..5807211236 100644 --- a/src/utils/SkCurveMeasure.h +++ b/src/utils/SkCurveMeasure.h @@ -44,6 +44,15 @@ private: class SkCurveMeasure { public: SkCurveMeasure() {} + + // Almost exactly the same as in SkPath::Iter: + // kLine_SegType -> 2 points: start end + // kQuad_SegType -> 3 points: start control end + // kCubic_SegType -> 4 points: start control1 control2 end + // kConic_SegType -> 4 points: start control end (w, w) + // + // i.e. the only difference is that the conic's last point is a point + // consisting of the w value twice SkCurveMeasure(const SkPoint* pts, SkSegType segType); SkScalar getTime(SkScalar targetLength); @@ -51,13 +60,6 @@ public: SkScalar getLength(); private: - SkPoint evaluateQuad(SkScalar t); - SkVector evaluateQuadDerivative(SkScalar t); - //SkPoint evaluate_cubic(SkScalar t); - //SkVector evaluate_cubic_derivative(SkScalar t); - //SkPoint evaluate_conic(SkScalar t); - //SkVector evaluate_conic_derivative(SkScalar t); - const SkScalar kTolerance = 0.0001f; const int kNewtonIters = 5; const int kBisectIters = 5;