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
This commit is contained in:
hstern 2016-08-10 10:55:09 -07:00 committed by Commit bot
parent cb0f4c3404
commit 4ab47e087e
2 changed files with 92 additions and 60 deletions

View File

@ -6,10 +6,66 @@
*/ */
#include "SkCurveMeasure.h" #include "SkCurveMeasure.h"
#include "SkGeometry.h"
// for abs // for abs
#include <cmath> #include <cmath>
#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, static inline Sk8f evaluateDerivativeLength(const Sk8f& ts,
const Sk8f (&xCoeff)[3], const Sk8f (&xCoeff)[3],
const Sk8f (&yCoeff)[3], const Sk8f (&yCoeff)[3],
@ -22,17 +78,18 @@ static inline Sk8f evaluateDerivativeLength(const Sk8f& ts,
y = yCoeff[0]*ts + yCoeff[1]; y = yCoeff[0]*ts + yCoeff[1];
break; break;
case kLine_SegType: case kLine_SegType:
SkDebugf("Unimplemented"); // length of line derivative is constant
break; // and we precompute it in the constructor
return xCoeff[0];
case kCubic_SegType: case kCubic_SegType:
x = (xCoeff[0]*ts + xCoeff[1])*ts + xCoeff[2]; x = (xCoeff[0]*ts + xCoeff[1])*ts + xCoeff[2];
y = (yCoeff[0]*ts + yCoeff[1])*ts + yCoeff[2]; y = (yCoeff[0]*ts + yCoeff[1])*ts + yCoeff[2];
break; break;
case kConic_SegType: case kConic_SegType:
SkDebugf("Unimplemented"); UNIMPLEMENTED;
break; break;
default: default:
SkDebugf("Unimplemented"); UNIMPLEMENTED;
} }
x = x * x; x = x * x;
@ -40,6 +97,7 @@ static inline Sk8f evaluateDerivativeLength(const Sk8f& ts,
return (x + y).sqrt(); return (x + y).sqrt();
} }
ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType) ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
: fSegType(segType) { : fSegType(segType) {
switch (fSegType) { switch (fSegType) {
@ -59,8 +117,13 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
yCoeff[1] = Sk8f(2.0f*(By - Ay)); yCoeff[1] = Sk8f(2.0f*(By - Ay));
} }
break; break;
case kLine_SegType: case kLine_SegType: {
SkDEBUGF(("Unimplemented")); // 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; break;
case kCubic_SegType: case kCubic_SegType:
{ {
@ -73,6 +136,7 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
float Cy = pts[2].y(); float Cy = pts[2].y();
float Dy = pts[3].y(); float Dy = pts[3].y();
// precompute coefficients for derivative
xCoeff[0] = Sk8f(3.0f*(-Ax + 3.0f*(Bx - Cx) + Dx)); xCoeff[0] = Sk8f(3.0f*(-Ax + 3.0f*(Bx - Cx) + Dx));
xCoeff[1] = Sk8f(3.0f*(2.0f*(Ax - 2.0f*Bx + Cx))); xCoeff[1] = Sk8f(3.0f*(2.0f*(Ax - 2.0f*Bx + Cx)));
xCoeff[2] = Sk8f(3.0f*(-Ax + Bx)); xCoeff[2] = Sk8f(3.0f*(-Ax + Bx));
@ -83,10 +147,10 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
} }
break; break;
case kConic_SegType: case kConic_SegType:
SkDEBUGF(("Unimplemented")); UNIMPLEMENTED;
break; break;
default: default:
SkDEBUGF(("Unimplemented")); UNIMPLEMENTED;
} }
} }
@ -117,7 +181,8 @@ SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType)
} }
break; break;
case SkSegType::kLine_SegType: case SkSegType::kLine_SegType:
SkDebugf("Unimplemented"); fPts[0] = pts[0];
fPts[1] = pts[1];
break; break;
case SkSegType::kCubic_SegType: case SkSegType::kCubic_SegType:
for (size_t i = 0; i < 4; i++) { for (size_t i = 0; i < 4; i++) {
@ -125,10 +190,12 @@ SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType)
} }
break; break;
case SkSegType::kConic_SegType: case SkSegType::kConic_SegType:
SkDebugf("Unimplemented"); for (size_t i = 0; i < 4; i++) {
fPts[i] = pts[i];
}
break; break;
default: default:
SkDEBUGF(("Unimplemented")); UNIMPLEMENTED;
break; break;
} }
fIntegrator = ArcLengthIntegrator(fPts, fSegType); fIntegrator = ArcLengthIntegrator(fPts, fSegType);
@ -199,9 +266,8 @@ SkScalar SkCurveMeasure::getTime(SkScalar targetLength) {
prevT = currentT; prevT = currentT;
if (iterations < kNewtonIters) { if (iterations < kNewtonIters) {
// TODO(hstern) switch here on curve type.
// This is just newton's formula. // This is just newton's formula.
SkScalar dt = evaluateQuadDerivative(currentT).length(); SkScalar dt = evaluateDerivative(fPts, fSegType, currentT).length();
newT = currentT - (lengthDiff / dt); newT = currentT - (lengthDiff / dt);
// If newT is out of bounds, bisect inside newton. // If newT is out of bounds, bisect inside newton.
@ -242,45 +308,9 @@ void SkCurveMeasure::getPosTanTime(SkScalar targetLength, SkPoint* pos,
*time = t; *time = t;
} }
if (pos) { if (pos) {
// TODO(hstern) switch here on curve type. *pos = evaluate(fPts, fSegType, t);
*pos = evaluateQuad(t);
} }
if (tan) { if (tan) {
// TODO(hstern) switch here on curve type. *tan = evaluateDerivative(fPts, fSegType, t);
*tan = evaluateQuadDerivative(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);
}

View File

@ -44,6 +44,15 @@ private:
class SkCurveMeasure { class SkCurveMeasure {
public: public:
SkCurveMeasure() {} 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); SkCurveMeasure(const SkPoint* pts, SkSegType segType);
SkScalar getTime(SkScalar targetLength); SkScalar getTime(SkScalar targetLength);
@ -51,13 +60,6 @@ public:
SkScalar getLength(); SkScalar getLength();
private: 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 SkScalar kTolerance = 0.0001f;
const int kNewtonIters = 5; const int kNewtonIters = 5;
const int kBisectIters = 5; const int kBisectIters = 5;