aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Cary Clark <caryclark@skia.org>2017-12-08 11:37:01 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-12-08 18:34:11 +0000
commit1acd3c7e845572be3fa84022b80643d75dfcfd1a (patch)
tree98ebfcd6f21a362b840b5336ab04dfff4255b577
parent8b225c1396657a8f45281a3226b92d6d3b64e0a0 (diff)
keep SVG arcs axis aligned with suppression
another attempt at https://skia-review.googlesource.com/c/skia/+/79423 this time, bracket change with SK_SUPPORT_LEGACY_SVG_ARC_TO to avoid prematurely changing SVG layout test results. R=djsollen@google.com Bug: b:69622768 Change-Id: Ia929f89d4f4292dfead191a381cdb431f743726a Reviewed-on: https://skia-review.googlesource.com/82723 Commit-Queue: Cary Clark <caryclark@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com>
-rw-r--r--src/core/SkPath.cpp26
-rw-r--r--tests/PathTest.cpp31
2 files changed, 57 insertions, 0 deletions
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index c433424635..422c2b911d 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1417,7 +1417,12 @@ void SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arc
pointTransform.setRotate(angle);
pointTransform.preScale(rx, ry);
+#ifdef SK_SUPPORT_LEGACY_SVG_ARC_TO
int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (SK_ScalarPI / 2)));
+#else
+ // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd
+ int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3)));
+#endif
SkScalar thetaWidth = thetaArc / segments;
SkScalar t = SkScalarTan(0.5f * thetaWidth);
if (!SkScalarIsFinite(t)) {
@@ -1425,6 +1430,14 @@ void SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arc
}
SkScalar startTheta = theta1;
SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
+#ifndef SK_SUPPORT_LEGACY_SVG_ARC_TO
+ auto scalar_is_integer = [](SkScalar scalar) -> bool {
+ return scalar == SkScalarFloorToScalar(scalar);
+ };
+ bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) &&
+ scalar_is_integer(rx) && scalar_is_integer(ry) &&
+ scalar_is_integer(x) && scalar_is_integer(y);
+#endif
for (int i = 0; i < segments; ++i) {
SkScalar endTheta = startTheta + thetaWidth;
SkScalar cosEndTheta, sinEndTheta = SkScalarSinCos(endTheta, &cosEndTheta);
@@ -1435,6 +1448,19 @@ void SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arc
unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
SkPoint mapped[2];
pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
+ /*
+ Computing the arc width introduces rounding errors that cause arcs to start
+ outside their marks. A round rect may lose convexity as a result. If the input
+ values are on integers, place the conic on integers as well.
+ */
+#ifndef SK_SUPPORT_LEGACY_SVG_ARC_TO
+ if (expectIntegers) {
+ SkScalar* mappedScalars = &mapped[0].fX;
+ for (unsigned index = 0; index < sizeof(mapped) / sizeof(SkScalar); ++index) {
+ mappedScalars[index] = SkScalarRoundToScalar(mappedScalars[index]);
+ }
+ }
+#endif
this->conicTo(mapped[0], mapped[1], w);
startTheta = endTheta;
}
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index b275342e2d..88ca3f8f05 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -4915,3 +4915,34 @@ DEF_TEST(NonFinitePathIteration, reporter) {
REPORTER_ASSERT(reporter, verbs == 0);
}
+
+
+#ifndef SK_SUPPORT_LEGACY_SVG_ARC_TO
+DEF_TEST(AndroidArc, reporter) {
+ const char* tests[] = {
+ "M50,0A50,50,0,0 1 100,50 L100,85 A15,15,0,0 1 85,100 L50,100 A50,50,0,0 1 50,0z",
+ "M50,0L92,0 A8,8,0,0 1 100,8 L100,92 A8,8,0,0 1 92,100 L8,100"
+ " A8,8,0,0 1 0,92 L 0,8 A8,8,0,0 1 8,0z",
+ "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0"
+ };
+ for (auto test : tests) {
+ SkPath aPath;
+ SkAssertResult(SkParsePath::FromSVGString(test, &aPath));
+ SkASSERT(aPath.isConvex());
+ for (SkScalar scale = 1; scale < 1000; scale *= 1.1f) {
+ SkPath scalePath = aPath;
+ SkMatrix matrix;
+ matrix.setScale(scale, scale);
+ scalePath.transform(matrix);
+ SkASSERT(scalePath.isConvex());
+ }
+ for (SkScalar scale = 1; scale < .001; scale /= 1.1f) {
+ SkPath scalePath = aPath;
+ SkMatrix matrix;
+ matrix.setScale(scale, scale);
+ scalePath.transform(matrix);
+ SkASSERT(scalePath.isConvex());
+ }
+ }
+}
+#endif