aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Cary Clark <caryclark@skia.org>2017-12-05 11:10:06 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-12-06 16:50:39 +0000
commitc3823848483eba33c3fa36187de7713e87651c1c (patch)
tree4247f737f408813ede6a110101dd049218c88057 /src
parente45ff46a6587ac0e481a62a85cc42b1a0865d856 (diff)
keep SVG arcs axis aligned
Computing the arc width introduces rounding errors that cause the arc to exceed 1/4 circle and cause integer anchored arcs to start outside their marks. A round rect may lose convexity as a result. Check if arcTo() inputs are integers and arc is 90 degrees; if so, output conics which are axis-aligned on integers as well. This is triggered when using SVG to represent a round rect. Possible future enhancements are recorded in bug.skia.org/7383 R=reed@google.com,djsollen@google.com Change-Id: I6609456fcefabcda6c9560a044533ecb5cda2d31 Reviewed-on: https://skia-review.googlesource.com/79423 Reviewed-by: Derek Sollenberger <djsollen@google.com> Commit-Queue: Derek Sollenberger <djsollen@google.com>
Diffstat (limited to 'src')
-rw-r--r--src/core/SkPath.cpp20
1 files changed, 19 insertions, 1 deletions
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index c433424635..ed3f466a4a 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1417,7 +1417,8 @@ void SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arc
pointTransform.setRotate(angle);
pointTransform.preScale(rx, ry);
- int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (SK_ScalarPI / 2)));
+ // 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)));
SkScalar thetaWidth = thetaArc / segments;
SkScalar t = SkScalarTan(0.5f * thetaWidth);
if (!SkScalarIsFinite(t)) {
@@ -1425,6 +1426,12 @@ void SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arc
}
SkScalar startTheta = theta1;
SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
+ 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);
for (int i = 0; i < segments; ++i) {
SkScalar endTheta = startTheta + thetaWidth;
SkScalar cosEndTheta, sinEndTheta = SkScalarSinCos(endTheta, &cosEndTheta);
@@ -1435,6 +1442,17 @@ 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.
+ */
+ if (expectIntegers) {
+ SkScalar* mappedScalars = &mapped[0].fX;
+ for (unsigned index = 0; index < sizeof(mapped) / sizeof(SkScalar); ++index) {
+ mappedScalars[index] = SkScalarRoundToScalar(mappedScalars[index]);
+ }
+ }
this->conicTo(mapped[0], mapped[1], w);
startTheta = endTheta;
}