diff options
author | bsalomon <bsalomon@google.com> | 2016-05-31 10:42:16 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-05-31 10:42:16 -0700 |
commit | 1978ce22eb488e3f05670189ea35d9dfae6a6784 (patch) | |
tree | 5c0940dd4b2e1b439ba3e35f943b5b88ca7d0317 | |
parent | 18c4943e90db5a63ea051ac3e508c75d6613aa46 (diff) |
Fix bug where SkPath will convert an arc to an oval and change the starting point.
Adds unit tests to test the behavior of addArc to oval conversions.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2017743002
Review-Url: https://codereview.chromium.org/2017743002
-rw-r--r-- | src/core/SkPath.cpp | 18 | ||||
-rw-r--r-- | tests/PathTest.cpp | 70 |
2 files changed, 83 insertions, 5 deletions
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index 03bc3170d8..aabcafdd11 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include <cmath> #include "SkBuffer.h" #include "SkCubicClipper.h" #include "SkErrorInternals.h" @@ -1421,10 +1422,21 @@ void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle const SkScalar kFullCircleAngle = SkIntToScalar(360); if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) { - this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction); - } else { - this->arcTo(oval, startAngle, sweepAngle, true); + // We can treat the arc as an oval if it begins at one of our legal starting positions. + // See SkPath::addOval() docs. + SkScalar startOver90 = startAngle / 90.f; + SkScalar startOver90I = SkScalarRoundToScalar(startOver90); + SkScalar error = startOver90 - startOver90I; + if (SkScalarNearlyEqual(error, 0)) { + // Index 1 is at startAngle == 0. + SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f); + startIndex = startIndex < 0 ? startIndex + 4.f : startIndex; + this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction, + (unsigned) startIndex); + return; + } } + this->arcTo(oval, startAngle, sweepAngle, true); } /* diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index 36e93074ef..f4b488b1f9 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include <cmath> #include "SkCanvas.h" #include "SkGeometry.h" #include "SkPaint.h" @@ -3306,12 +3307,12 @@ static void test_arc(skiatest::Reporter* reporter) { p.reset(); SkPath cwOval; cwOval.addOval(oval); - p.addArc(oval, 1, 360); + p.addArc(oval, 0, 360); REPORTER_ASSERT(reporter, p == cwOval); p.reset(); SkPath ccwOval; ccwOval.addOval(oval, SkPath::kCCW_Direction); - p.addArc(oval, 1, -360); + p.addArc(oval, 0, -360); REPORTER_ASSERT(reporter, p == ccwOval); p.reset(); p.addArc(oval, 1, 180); @@ -3321,6 +3322,70 @@ static void test_arc(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, p.isConvex()); } +static inline SkScalar oval_start_index_to_angle(unsigned start) { + switch (start) { + case 0: + return 270.f; + case 1: + return 0.f; + case 2: + return 90.f; + case 3: + return 180.f; + default: + return -1.f; + } +} + +static inline SkScalar canonical_start_angle(float angle) { + while (angle < 0.f) { + angle += 360.f; + } + while (angle >= 360.f) { + angle -= 360.f; + } + return angle; +} + +static void check_oval_arc(skiatest::Reporter* reporter, SkScalar start, SkScalar sweep, + const SkPath& path) { + SkRect r = SkRect::MakeEmpty(); + SkPath::Direction d = SkPath::kCCW_Direction; + unsigned s = ~0U; + bool isOval = path.isOval(&r, &d, &s); + REPORTER_ASSERT(reporter, isOval); + SkPath recreatedPath; + recreatedPath.addOval(r, d, s); + REPORTER_ASSERT(reporter, path == recreatedPath); + REPORTER_ASSERT(reporter, oval_start_index_to_angle(s) == canonical_start_angle(start)); + REPORTER_ASSERT(reporter, (SkPath::kCW_Direction == d) == (sweep > 0.f)); +} + +static void test_arc_ovals(skiatest::Reporter* reporter) { + SkRect oval = SkRect::MakeWH(10, 20); + for (SkScalar sweep : {-720.f, -540.f, -360.f, 360.f, 432.f, 720.f}) { + for (SkScalar start = -360.f; start <= 360.f; start += 1.f) { + SkPath path; + path.addArc(oval, start, sweep); + // SkPath's interfaces for inserting and extracting ovals only allow contours + // to start at multiples of 90 degrees. + if (std::fmod(start, 90.f) == 0) { + check_oval_arc(reporter, start, sweep, path); + } else { + REPORTER_ASSERT(reporter, !path.isOval(nullptr)); + } + } + // Test start angles that are nearly at valid oval start angles. + for (float start : {-180.f, -90.f, 90.f, 180.f}) { + for (float delta : {-SK_ScalarNearlyZero, SK_ScalarNearlyZero}) { + SkPath path; + path.addArc(oval, start + delta, sweep); + check_oval_arc(reporter, start, sweep, path); + } + } + } +} + static void check_move(skiatest::Reporter* reporter, SkPath::RawIter* iter, SkScalar x0, SkScalar y0) { SkPoint pts[4]; @@ -4121,6 +4186,7 @@ DEF_TEST(Paths, reporter) { test_path_to_region(reporter); test_rrect(reporter); test_arc(reporter); + test_arc_ovals(reporter); test_arcTo(reporter); test_addPath(reporter); test_addPathMode(reporter, false, false); |