aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--bench/PathBench.cpp80
-rw-r--r--src/core/SkGeometry.cpp161
-rw-r--r--src/core/SkGeometry.h4
-rw-r--r--tests/GeometryTest.cpp53
4 files changed, 263 insertions, 35 deletions
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index b597f919f8..3a4eb63830 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -832,29 +832,91 @@ private:
};
class ConicBench_ChopHalf : public Benchmark {
- SkConic fRQ;
+protected:
+ SkConic fRQ, fDst[2];
+ SkString fName;
+ const bool fUseV2;
public:
- ConicBench_ChopHalf() {
+ ConicBench_ChopHalf(bool useV2) : fUseV2(useV2) {
fRQ.fPts[0].set(0, 0);
fRQ.fPts[1].set(100, 0);
fRQ.fPts[2].set(100, 100);
fRQ.fW = SkScalarCos(SK_ScalarPI/4);
+
+ fName.printf("conic-chop-half%d", useV2);
}
-private:
- const char* onGetName() SK_OVERRIDE {
- return "ratquad-chop-half";
+ bool isSuitableFor(Backend backend) SK_OVERRIDE {
+ return backend == kNonRendering_Backend;
}
+private:
+ const char* onGetName() SK_OVERRIDE { return fName.c_str(); }
+
void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
- SkConic dst[2];
- for (int i = 0; i < loops; ++i) {
- fRQ.chop(dst);
+ if (fUseV2) {
+ for (int i = 0; i < loops; ++i) {
+ fRQ.chop2(fDst);
+ }
+ } else {
+ for (int i = 0; i < loops; ++i) {
+ fRQ.chop(fDst);
+ }
}
}
typedef Benchmark INHERITED;
};
+DEF_BENCH( return new ConicBench_ChopHalf(false); )
+DEF_BENCH( return new ConicBench_ChopHalf(true); )
+
+class ConicBench_EvalPos : public ConicBench_ChopHalf {
+public:
+ ConicBench_EvalPos(bool useV2) : ConicBench_ChopHalf(useV2) {
+ fName.printf("conic-eval-pos%d", useV2);
+ }
+ void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+ if (fUseV2) {
+ for (int i = 0; i < loops; ++i) {
+ for (int j = 0; j < 1000; ++j) {
+ fDst[0].fPts[0] = fRQ.evalAt(0.4f);
+ }
+ }
+ } else {
+ for (int i = 0; i < loops; ++i) {
+ for (int j = 0; j < 1000; ++j) {
+ fRQ.evalAt(0.4f, &fDst[0].fPts[0], NULL);
+ }
+ }
+ }
+ }
+};
+DEF_BENCH( return new ConicBench_EvalPos(false); )
+DEF_BENCH( return new ConicBench_EvalPos(true); )
+
+class ConicBench_EvalTan : public ConicBench_ChopHalf {
+public:
+ ConicBench_EvalTan(bool useV2) : ConicBench_ChopHalf(useV2) {
+ fName.printf("conic-eval-tan%d", useV2);
+ }
+ void onDraw(const int loops, SkCanvas*) SK_OVERRIDE {
+ if (fUseV2) {
+ for (int i = 0; i < loops; ++i) {
+ for (int j = 0; j < 1000; ++j) {
+ fDst[0].fPts[0] = fRQ.evalTangentAt(0.4f);
+ }
+ }
+ } else {
+ for (int i = 0; i < loops; ++i) {
+ for (int j = 0; j < 1000; ++j) {
+ fRQ.evalAt(0.4f, NULL, &fDst[0].fPts[0]);
+ }
+ }
+ }
+ }
+};
+DEF_BENCH( return new ConicBench_EvalTan(false); )
+DEF_BENCH( return new ConicBench_EvalTan(true); )
///////////////////////////////////////////////////////////////////////////////
@@ -1012,10 +1074,10 @@ DEF_BENCH( return new ConservativelyContainsBench(ConservativelyContainsBench::k
DEF_BENCH( return new ConservativelyContainsBench(ConservativelyContainsBench::kRoundRect_Type); )
DEF_BENCH( return new ConservativelyContainsBench(ConservativelyContainsBench::kOval_Type); )
+
// These seem to be optimized away, which is troublesome for timing.
/*
DEF_BENCH( return new ConicBench_Chop5() )
-DEF_BENCH( return new ConicBench_ChopHalf() )
DEF_BENCH( return new ConicBench_ComputeError() )
DEF_BENCH( return new ConicBench_asQuadTol() )
DEF_BENCH( return new ConicBench_quadPow2() )
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
index aea1fe5665..284f07e285 100644
--- a/src/core/SkGeometry.cpp
+++ b/src/core/SkGeometry.cpp
@@ -9,6 +9,33 @@
#include "SkMatrix.h"
#include "Sk2x.h"
+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;
+}
+
+static SkVector to_vector(const Sk2s& x) {
+ SkVector vector;
+ x.store(&vector.fX);
+ return vector;
+}
+
+#if 0
+static Sk2s divide(const Sk2s& numer, const Sk2s& denom) {
+ SkScalar numerStorage[2], denomStorage[2];
+ numer.store(numerStorage);
+ denom.store(denomStorage);
+ numerStorage[0] /= denomStorage[0];
+ numerStorage[1] /= denomStorage[1];
+ return Sk2s::Load(numerStorage);
+}
+#endif
+
/** If defined, this makes eval_quad and eval_cubic do more setup (sometimes
involving integer multiplies by 2 or 3, but fewer calls to SkScalarMul.
May also introduce overflow of fixed when we compute our setup.
@@ -92,6 +119,10 @@ int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) {
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
+static Sk2s quad_poly_eval(const Sk2s& A, const Sk2s& B, const Sk2s& C, const Sk2s& t) {
+ return (A * t + B) * t + C;
+}
+
static SkScalar eval_quad(const SkScalar src[], SkScalar t) {
SkASSERT(src);
SkASSERT(t >= 0 && t <= SK_Scalar1);
@@ -132,35 +163,31 @@ SkPoint SkEvalQuadAt(const SkPoint src[3], SkScalar t) {
SkASSERT(src);
SkASSERT(t >= 0 && t <= SK_Scalar1);
- const Sk2f t2(t);
+ const Sk2s t2(t);
- Sk2f P0 = Sk2f::Load(&src[0].fX);
- Sk2f P1 = Sk2f::Load(&src[1].fX);
- Sk2f P2 = Sk2f::Load(&src[2].fX);
+ Sk2s P0 = from_point(src[0]);
+ Sk2s P1 = from_point(src[1]);
+ Sk2s P2 = from_point(src[2]);
- Sk2f B = P1 - P0;
- Sk2f A = P2 - P1 - B;
+ Sk2s B = P1 - P0;
+ Sk2s A = P2 - P1 - B;
- SkPoint result;
- ((A * t2 + B+B) * t2 + P0).store(&result.fX);
- return result;
+ return to_point((A * t2 + B+B) * t2 + P0);
}
SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t) {
SkASSERT(src);
SkASSERT(t >= 0 && t <= SK_Scalar1);
- Sk2f P0 = Sk2f::Load(&src[0].fX);
- Sk2f P1 = Sk2f::Load(&src[1].fX);
- Sk2f P2 = Sk2f::Load(&src[2].fX);
+ Sk2s P0 = from_point(src[0]);
+ Sk2s P1 = from_point(src[1]);
+ Sk2s P2 = from_point(src[2]);
- Sk2f B = P1 - P0;
- Sk2f A = P2 - P1 - B;
- Sk2f T = A * Sk2f(t) + B;
+ Sk2s B = P1 - P0;
+ Sk2s A = P2 - P1 - B;
+ Sk2s T = A * Sk2s(t) + B;
- SkVector result;
- (T + T).store(&result.fX);
- return result;
+ return to_vector(T + T);
}
static void interp_quad_coords(const SkScalar* src, SkScalar* dst, SkScalar t) {
@@ -188,19 +215,19 @@ static inline Sk2s interp(const Sk2s& v0, const Sk2s& v1, const Sk2s& t) {
void SkChopQuadAt2(const SkPoint src[3], SkPoint dst[5], SkScalar t) {
SkASSERT(t > 0 && t < SK_Scalar1);
- Sk2s p0 = Sk2f::Load(&src[0].fX);
- Sk2s p1 = Sk2f::Load(&src[1].fX);
- Sk2s p2 = Sk2f::Load(&src[2].fX);
+ Sk2s p0 = from_point(src[0]);
+ Sk2s p1 = from_point(src[1]);
+ Sk2s p2 = from_point(src[2]);
Sk2s tt = Sk2s(t);
Sk2s p01 = interp(p0, p1, tt);
Sk2s p12 = interp(p1, p2, tt);
- p0.store(&dst[0].fX);
- p01.store(&dst[1].fX);
- interp(p01, p12, tt).store(&dst[2].fX);
- p12.store(&dst[3].fX);
- p2.store(&dst[4].fX);
+ dst[0] = to_point(p0);
+ dst[1] = to_point(p01);
+ dst[2] = to_point(interp(p01, p12, tt));
+ dst[3] = to_point(p12);
+ dst[4] = to_point(p2);
}
void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) {
@@ -1251,6 +1278,65 @@ 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;
+}
+
+SkPoint SkConic::evalAt(SkScalar t) const {
+ Sk2s p0 = from_point(fPts[0]);
+ Sk2s p1 = from_point(fPts[1]);
+ Sk2s p2 = from_point(fPts[2]);
+ Sk2s tt = Sk2s(t);
+ Sk2s ww = Sk2s(fW);
+ Sk2s one = Sk2s(1);
+
+ Sk2s p1w = p1 * ww;
+ Sk2s C = p0;
+ Sk2s A = p2 - times_2(p1w) + p0;
+ Sk2s B = times_2(p1w - C);
+ Sk2s numer = quad_poly_eval(A, B, C, tt);
+
+ B = times_2(ww - one);
+ A = -B;
+ Sk2s denom = quad_poly_eval(A, B, one, tt);
+
+ return to_point(numer / denom);
+}
+
+SkVector SkConic::evalTangentAt(SkScalar t) const {
+ Sk2s p0 = from_point(fPts[0]);
+ Sk2s p1 = from_point(fPts[1]);
+ Sk2s p2 = from_point(fPts[2]);
+ Sk2s ww = Sk2s(fW);
+
+ Sk2s p20 = p2 - p0;
+ Sk2s p10 = p1 - p0;
+
+ Sk2s C = ww * p10;
+ Sk2s A = ww * p20 - p20;
+ Sk2s B = p20 - C - C;
+
+ return to_vector(quad_poly_eval(A, B, C, Sk2s(t)));
+#if 0
+ static void conic_deriv_coeff(const SkScalar src[],
+ SkScalar w,
+ SkScalar coeff[3]) {
+ const SkScalar P20 = src[4] - src[0];
+ const SkScalar P10 = src[2] - src[0];
+ const SkScalar wP10 = w * P10;
+ coeff[0] = w * P20 - P20;
+ coeff[1] = P20 - 2 * wP10;
+ coeff[2] = wP10;
+ }
+
+ static SkScalar conic_eval_tan(const SkScalar coord[], SkScalar w, SkScalar t) {
+ SkScalar coeff[3];
+ conic_deriv_coeff(coord, w, coeff);
+ return t * (t * coeff[0] + coeff[1]) + coeff[2];
+ }
+#endif
+}
+
static SkScalar subdivide_w_value(SkScalar w) {
return SkScalarSqrt(SK_ScalarHalf + w * SK_ScalarHalf);
}
@@ -1275,6 +1361,29 @@ void SkConic::chop(SkConic dst[2]) const {
dst[0].fW = dst[1].fW = subdivide_w_value(fW);
}
+void SkConic::chop2(SkConic * SK_RESTRICT dst) const {
+ Sk2s scale(SkScalarInvert(SK_Scalar1 + fW));
+// Sk2s scale = Sk2s(SK_Scalar1 + fW).invert();
+ SkScalar newW = subdivide_w_value(fW);
+
+ Sk2s p0 = from_point(fPts[0]);
+ Sk2s p1 = from_point(fPts[1]);
+ Sk2s p2 = from_point(fPts[2]);
+ Sk2s ww = Sk2s(fW);
+ Sk2s half = Sk2s(0.5f);
+
+ Sk2s wp1 = ww * p1;
+ Sk2s m = ((p0 + wp1 + wp1 + p2) * half) * scale;
+
+ dst[0].fPts[0] = fPts[0];
+ dst[0].fPts[1] = to_point((p0 + wp1) * scale);
+ dst[0].fPts[2] = dst[1].fPts[0] = to_point(m);
+ dst[1].fPts[1] = to_point((wp1 + p2) * scale);
+ dst[1].fPts[2] = fPts[2];
+
+ dst[0].fW = dst[1].fW = newW;
+}
+
/*
* "High order approximation of conic sections by quadratic splines"
* by Michael Floater, 1993
diff --git a/src/core/SkGeometry.h b/src/core/SkGeometry.h
index 5f7aa5e7b3..5a6dcb5d37 100644
--- a/src/core/SkGeometry.h
+++ b/src/core/SkGeometry.h
@@ -219,6 +219,10 @@ struct SkConic {
void chopAt(SkScalar t, SkConic dst[2]) const;
void chop(SkConic dst[2]) const;
+ SkPoint evalAt(SkScalar t) const;
+ SkVector evalTangentAt(SkScalar t) const;
+ void chop2(SkConic dst[2]) const;
+
void computeAsQuadError(SkVector* err) const;
bool asQuadTol(SkScalar tol) const;
diff --git a/tests/GeometryTest.cpp b/tests/GeometryTest.cpp
index aba8dd224b..8a7bfe3b49 100644
--- a/tests/GeometryTest.cpp
+++ b/tests/GeometryTest.cpp
@@ -77,6 +77,58 @@ static void test_evalquadat(skiatest::Reporter* reporter) {
}
}
+static void test_conic_eval_pos(skiatest::Reporter* reporter, const SkConic& conic, SkScalar t) {
+ SkPoint p0, p1;
+ conic.evalAt(t, &p0, NULL);
+ p1 = conic.evalAt(t);
+ check_pairs(reporter, 0, t, "conic-pos", p0.fX, p0.fY, p1.fX, p1.fY);
+}
+
+static void test_conic_eval_tan(skiatest::Reporter* reporter, const SkConic& conic, SkScalar t) {
+ SkVector v0, v1;
+ conic.evalAt(t, NULL, &v0);
+ v1 = conic.evalTangentAt(t);
+ check_pairs(reporter, 0, t, "conic-tan", v0.fX, v0.fY, v1.fX, v1.fY);
+}
+
+static void test_conic_chop_half(skiatest::Reporter* reporter, const SkConic& conic) {
+ SkConic dst0[2], dst1[2];
+ conic.chop(dst0);
+ conic.chop2(dst1);
+
+ for (int i = 0; i < 2; ++i) {
+ REPORTER_ASSERT(reporter, dst0[i].fW == dst1[i].fW);
+ for (int j = 0; j < 3; ++j) {
+ check_pairs(reporter, j, 0.5f, "conic-chop",
+ dst0[i].fPts[j].fX, dst0[i].fPts[j].fY,
+ dst0[i].fPts[j].fX, dst1[i].fPts[j].fY);
+ }
+ }
+}
+
+static void test_conic(skiatest::Reporter* reporter) {
+ SkRandom rand;
+ for (int i = 0; i < 1000; ++i) {
+ SkPoint pts[3];
+ for (int j = 0; j < 3; ++j) {
+ pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100);
+ }
+ for (int k = 0; k < 10; ++k) {
+ SkScalar w = rand.nextUScalar1() * 2;
+ SkConic conic(pts, w);
+ test_conic_chop_half(reporter, conic);
+
+ const SkScalar dt = SK_Scalar1 / 128;
+ SkScalar t = dt;
+ for (int j = 1; j < 128; ++j) {
+ test_conic_eval_pos(reporter, conic, t);
+ test_conic_eval_tan(reporter, conic, t);
+ t += dt;
+ }
+ }
+ }
+}
+
DEF_TEST(Geometry, reporter) {
SkPoint pts[3], dst[5];
@@ -103,4 +155,5 @@ DEF_TEST(Geometry, reporter) {
testChopCubic(reporter);
test_evalquadat(reporter);
+ test_conic(reporter);
}