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
This commit is contained in:
robertphillips@google.com 2012-10-18 15:26:12 +00:00
parent 872017694b
commit b95eaa8d08
3 changed files with 109 additions and 19 deletions

View File

@ -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;

View File

@ -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 },
{ 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,

View File

@ -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"