Add better bounds checks for getTime to fix perf debug assert below
Due to rounding, we request a length slightly larger than the total length in MeasureBench. This will be fixed in a following CL and there will be another CL adding unit tests for bounds checking and other problems. Revert "Revert386ba54
and4ab47e0
: perf debug assert." This reverts commit69aaa5a49a
. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2233983003 Review-Url: https://codereview.chromium.org/2233983003
This commit is contained in:
parent
9f129de595
commit
5a4b18c6f2
@ -6,10 +6,66 @@
|
||||
*/
|
||||
|
||||
#include "SkCurveMeasure.h"
|
||||
#include "SkGeometry.h"
|
||||
|
||||
// for abs
|
||||
#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,
|
||||
const Sk8f (&xCoeff)[3],
|
||||
const Sk8f (&yCoeff)[3],
|
||||
@ -21,18 +77,15 @@ static inline Sk8f evaluateDerivativeLength(const Sk8f& ts,
|
||||
x = xCoeff[0]*ts + xCoeff[1];
|
||||
y = yCoeff[0]*ts + yCoeff[1];
|
||||
break;
|
||||
case kLine_SegType:
|
||||
SkDebugf("Unimplemented");
|
||||
break;
|
||||
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 +93,7 @@ static inline Sk8f evaluateDerivativeLength(const Sk8f& ts,
|
||||
|
||||
return (x + y).sqrt();
|
||||
}
|
||||
|
||||
ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
|
||||
: fSegType(segType) {
|
||||
switch (fSegType) {
|
||||
@ -59,9 +113,6 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
|
||||
yCoeff[1] = Sk8f(2.0f*(By - Ay));
|
||||
}
|
||||
break;
|
||||
case kLine_SegType:
|
||||
SkDEBUGF(("Unimplemented"));
|
||||
break;
|
||||
case kCubic_SegType:
|
||||
{
|
||||
float Ax = pts[0].x();
|
||||
@ -73,6 +124,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 +135,10 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
|
||||
}
|
||||
break;
|
||||
case kConic_SegType:
|
||||
SkDEBUGF(("Unimplemented"));
|
||||
UNIMPLEMENTED;
|
||||
break;
|
||||
default:
|
||||
SkDEBUGF(("Unimplemented"));
|
||||
UNIMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,7 +169,9 @@ SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType)
|
||||
}
|
||||
break;
|
||||
case SkSegType::kLine_SegType:
|
||||
SkDebugf("Unimplemented");
|
||||
fPts[0] = pts[0];
|
||||
fPts[1] = pts[1];
|
||||
fLength = (fPts[1] - fPts[0]).length();
|
||||
break;
|
||||
case SkSegType::kCubic_SegType:
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
@ -125,13 +179,17 @@ 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);
|
||||
if (kLine_SegType != segType) {
|
||||
fIntegrator = ArcLengthIntegrator(fPts, fSegType);
|
||||
}
|
||||
}
|
||||
|
||||
SkScalar SkCurveMeasure::getLength() {
|
||||
@ -151,15 +209,18 @@ SkScalar SkCurveMeasure::getLength() {
|
||||
// which is equal to the length of the tangent (so we have to do a sqrt).
|
||||
|
||||
SkScalar SkCurveMeasure::getTime(SkScalar targetLength) {
|
||||
if (targetLength == 0.0f) {
|
||||
if (targetLength <= 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
SkScalar currentLength = getLength();
|
||||
|
||||
if (SkScalarNearlyEqual(targetLength, currentLength)) {
|
||||
if (targetLength > currentLength || (SkScalarNearlyEqual(targetLength, currentLength))) {
|
||||
return 1.0f;
|
||||
}
|
||||
if (kLine_SegType == fSegType) {
|
||||
return targetLength / currentLength;
|
||||
}
|
||||
|
||||
// initial estimate of t is percentage of total length
|
||||
SkScalar currentT = targetLength / currentLength;
|
||||
@ -199,9 +260,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 +278,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 +295,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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user