diff options
author | 2012-10-18 15:26:12 +0000 | |
---|---|---|
committer | 2012-10-18 15:26:12 +0000 | |
commit | b95eaa8d0842a8bba97f0bc7e19cfd9172d09722 (patch) | |
tree | ce79e8cf032bd82c29a84945faa6336168becf77 | |
parent | 872017694b29fd08256ac99e3221bcd0b8a3f32b (diff) |
Altered arcTo's canonical points to (usually) be convex
https://codereview.appspot.com/6709051/
This will require rebaselining of: degeneratesegments, shadertext & shadertext2
git-svn-id: http://skia.googlecode.com/svn/trunk@5997 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | bench/PathBench.cpp | 8 | ||||
-rw-r--r-- | src/core/SkGeometry.cpp | 61 | ||||
-rw-r--r-- | tests/PathTest.cpp | 67 |
3 files changed, 113 insertions, 23 deletions
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp index 4a69d5a540..3b3c52d0cb 100644 --- a/bench/PathBench.cpp +++ b/bench/PathBench.cpp @@ -672,6 +672,8 @@ private: }; // Chrome creates its own round rects with each corner possibly being different +// Note: PathTest::test_arb_round_rect_is_convex performs almost exactly +// the same test (but with no drawing) class ArbRoundRectBench : public SkBenchmark { protected: SkString fName; @@ -727,8 +729,7 @@ protected: add_corner_arc(path, r, xCorner, yCorner, 90); add_corner_arc(path, r, xCorner, yCorner, 180); - // TODO: re-enable once arcTo convexity issue is resolved - //SkASSERT(path->isConvex()); + SkASSERT(path->isConvex()); } virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { @@ -741,6 +742,9 @@ protected: paint.setAntiAlias(true); SkScalar radius = rand.nextUScalar1() * 30; + if (radius < SK_Scalar1) { + continue; + } r.fLeft = rand.nextUScalar1() * 300; r.fTop = rand.nextUScalar1() * 300; r.fRight = r.fLeft + 2 * radius; diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp index 1c1a4f1045..d2aaeff6d9 100644 --- a/src/core/SkGeometry.cpp +++ b/src/core/SkGeometry.cpp @@ -1254,28 +1254,47 @@ static bool quad_pt2OffCurve(const SkPoint quad[3], SkScalar x, SkScalar y, SkPo return false; } +#ifdef SK_SCALAR_IS_FLOAT +// Due to floating point issues (i.e., 1.0f - SK_ScalarRoot2Over2 != +// SK_ScalarRoot2Over2 - SK_ScalarTanPIOver8) a cruder root2over2 +// approximation is required to make the quad circle points convex. The +// root of the problem is that with the root2over2 value in SkScalar.h +// the arcs really are ever so slightly concave. Some alternative fixes +// to this problem (besides just arbitrarily pushing out the mid-point as +// is done here) are: +// Adjust all the points (not just the middle) to both better approximate +// the curve and remain convex +// Switch over to using cubics rather then quads +// Use a different method to create the mid-point (e.g., compute +// the two side points, average them, then move it out as needed + #define SK_ScalarRoot2Over2_QuadCircle 0.7072f +#else + #define SK_ScalarRoot2Over2_QuadCircle SK_FixedRoot2Over2 +#endif + + static const SkPoint gQuadCirclePts[kSkBuildQuadArcStorage] = { - { SK_Scalar1, 0 }, - { SK_Scalar1, SK_ScalarTanPIOver8 }, - { SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 }, - { SK_ScalarTanPIOver8, SK_Scalar1 }, - - { 0, SK_Scalar1 }, - { -SK_ScalarTanPIOver8, SK_Scalar1 }, - { -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 }, - { -SK_Scalar1, SK_ScalarTanPIOver8 }, - - { -SK_Scalar1, 0 }, - { -SK_Scalar1, -SK_ScalarTanPIOver8 }, - { -SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 }, - { -SK_ScalarTanPIOver8, -SK_Scalar1 }, - - { 0, -SK_Scalar1 }, - { SK_ScalarTanPIOver8, -SK_Scalar1 }, - { SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 }, - { SK_Scalar1, -SK_ScalarTanPIOver8 }, - - { SK_Scalar1, 0 } + { SK_Scalar1, 0 }, + { SK_Scalar1, SK_ScalarTanPIOver8 }, + { SK_ScalarRoot2Over2_QuadCircle, SK_ScalarRoot2Over2_QuadCircle }, + { SK_ScalarTanPIOver8, SK_Scalar1 }, + + { 0, SK_Scalar1 }, + { -SK_ScalarTanPIOver8, SK_Scalar1 }, + { -SK_ScalarRoot2Over2_QuadCircle, SK_ScalarRoot2Over2_QuadCircle }, + { -SK_Scalar1, SK_ScalarTanPIOver8 }, + + { -SK_Scalar1, 0 }, + { -SK_Scalar1, -SK_ScalarTanPIOver8 }, + { -SK_ScalarRoot2Over2_QuadCircle, -SK_ScalarRoot2Over2_QuadCircle }, + { -SK_ScalarTanPIOver8, -SK_Scalar1 }, + + { 0, -SK_Scalar1 }, + { SK_ScalarTanPIOver8, -SK_Scalar1 }, + { SK_ScalarRoot2Over2_QuadCircle, -SK_ScalarRoot2Over2_QuadCircle }, + { SK_Scalar1, -SK_ScalarTanPIOver8 }, + + { SK_Scalar1, 0 } }; int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop, diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index 8197e685ff..6c8a400de9 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -77,6 +77,72 @@ static void test_isfinite_after_transform(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, path.getBounds().isEmpty()); } +static void add_corner_arc(SkPath* path, const SkRect& rect, + SkScalar xIn, SkScalar yIn, + int startAngle) +{ + + SkScalar rx = SkMinScalar(rect.width(), xIn); + SkScalar ry = SkMinScalar(rect.height(), yIn); + + SkRect arcRect; + arcRect.set(-rx, -ry, rx, ry); + switch (startAngle) { + case 0: + arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom); + break; + case 90: + arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom); + break; + case 180: + arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop); + break; + case 270: + arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop); + break; + default: + break; + } + + path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false); +} + +static void make_arb_round_rect(SkPath* path, const SkRect& r, + SkScalar xCorner, SkScalar yCorner) { + // we are lazy here and use the same x & y for each corner + add_corner_arc(path, r, xCorner, yCorner, 270); + add_corner_arc(path, r, xCorner, yCorner, 0); + add_corner_arc(path, r, xCorner, yCorner, 90); + add_corner_arc(path, r, xCorner, yCorner, 180); +} + +// Chrome creates its own round rects with each corner possibly being different. +// Performance will suffer if they are not convex. +// Note: PathBench::ArbRoundRectBench performs almost exactly +// the same test (but with drawing) +static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) { + SkRandom rand; + SkRect r; + + for (int i = 0; i < 5000; ++i) { + + SkScalar radius = rand.nextUScalar1() * 30; + if (radius < SK_Scalar1) { + continue; + } + r.fLeft = rand.nextUScalar1() * 300; + r.fTop = rand.nextUScalar1() * 300; + r.fRight = r.fLeft + 2 * radius; + r.fBottom = r.fTop + 2 * radius; + + SkPath temp; + + make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15); + + REPORTER_ASSERT(reporter, temp.isConvex()); + } +} + static void test_rect_isfinite(skiatest::Reporter* reporter) { const SkScalar inf = SK_ScalarInfinity; const SkScalar nan = SK_ScalarNaN; @@ -1616,6 +1682,7 @@ static void TestPath(skiatest::Reporter* reporter) { test_isfinite(reporter); test_isfinite_after_transform(reporter); test_tricky_cubic(reporter); + test_arb_round_rect_is_convex(reporter); } #include "TestClassDef.h" |