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
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index 4a69d5a..3b3c52d 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -672,6 +672,8 @@
};
// 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 @@
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 @@
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 1c1a4f1..d2aaeff 100644
--- a/src/core/SkGeometry.cpp
+++ b/src/core/SkGeometry.cpp
@@ -1254,28 +1254,47 @@
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 },
+ { 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, SK_ScalarRoot2Over2 },
- { -SK_Scalar1, SK_ScalarTanPIOver8 },
+ { 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, -SK_ScalarRoot2Over2 },
- { -SK_ScalarTanPIOver8, -SK_Scalar1 },
+ { -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, -SK_ScalarRoot2Over2 },
- { SK_Scalar1, -SK_ScalarTanPIOver8 },
+ { 0, -SK_Scalar1 },
+ { SK_ScalarTanPIOver8, -SK_Scalar1 },
+ { SK_ScalarRoot2Over2_QuadCircle, -SK_ScalarRoot2Over2_QuadCircle },
+ { SK_Scalar1, -SK_ScalarTanPIOver8 },
- { SK_Scalar1, 0 }
+ { SK_Scalar1, 0 }
};
int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop,
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 8197e68..6c8a400 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -77,6 +77,72 @@
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 @@
test_isfinite(reporter);
test_isfinite_after_transform(reporter);
test_tricky_cubic(reporter);
+ test_arb_round_rect_is_convex(reporter);
}
#include "TestClassDef.h"