diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index b37342f3c9..4b3505a160 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -1909,8 +1909,26 @@ CONTOUR_END: SkDEBUGCODE(++fContourCounter;) } +// returns cross product of (p1 - p0) and (p2 - p0) static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) { - return SkPoint::CrossProduct(p1 - p0, p2 - p0); + SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0); + // We may get 0 when the above subtracts underflow. We expect this to be + // very rare and lazily promote to double. + if (0 == cross) { + double p0x = SkScalarToDouble(p0.fX); + double p0y = SkScalarToDouble(p0.fY); + + double p1x = SkScalarToDouble(p1.fX); + double p1y = SkScalarToDouble(p1.fY); + + double p2x = SkScalarToDouble(p2.fX); + double p2y = SkScalarToDouble(p2.fY); + + cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) - + (p1y - p0y) * (p2x - p0x)); + + } + return cross; } // Returns the first pt with the maximum Y coordinate diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index d156e5796d..affc223200 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -15,6 +15,21 @@ #include "SkSize.h" #include "SkWriter32.h" +/** + * cheapIsDirection can take a shortcut when a path is marked convex. + * This function ensures that we always test cheapIsDirection when the path + * is flagged with unknown convexity status. + */ +static void check_direction(SkPath* path, + SkPath::Direction expectedDir, + skiatest::Reporter* reporter) { + if (SkPath::kConvex_Convexity == path->getConvexity()) { + REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir)); + path->setConvexity(SkPath::kUnknown_Convexity); + } + REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir)); +} + static void test_direction(skiatest::Reporter* reporter) { size_t i; SkPath path; @@ -46,7 +61,7 @@ static void test_direction(skiatest::Reporter* reporter) { path.reset(); bool valid = SkParsePath::FromSVGString(gCW[i], &path); REPORTER_ASSERT(reporter, valid); - REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction)); + check_direction(&path, SkPath::kCW_Direction, reporter); } static const char* gCCW[] = { @@ -58,7 +73,7 @@ static void test_direction(skiatest::Reporter* reporter) { path.reset(); bool valid = SkParsePath::FromSVGString(gCCW[i], &path); REPORTER_ASSERT(reporter, valid); - REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction)); + check_direction(&path, SkPath::kCCW_Direction, reporter); } // Test two donuts, each wound a different direction. Only the outer contour @@ -66,12 +81,20 @@ static void test_direction(skiatest::Reporter* reporter) { path.reset(); path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction); path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction); - REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction)); - + check_direction(&path, SkPath::kCW_Direction, reporter); + path.reset(); path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction); path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction); - REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction)); + check_direction(&path, SkPath::kCCW_Direction, reporter); + + // triangle with one point really far from the origin. + path.reset(); + // the first point is roughly 1.05e10, 1.05e10 + path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652))); + path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1); + path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1); + check_direction(&path, SkPath::kCCW_Direction, reporter); } static void add_rect(SkPath* path, const SkRect& r) {