Index: src/core/SkPath.cpp |
=================================================================== |
--- src/core/SkPath.cpp (revision 11792) |
+++ src/core/SkPath.cpp (working copy) |
@@ -899,9 +899,73 @@ |
SkDEBUGCODE(this->validate();) |
} |
+#include "SkGeometry.h" |
+ |
+static int build_arc_points(const SkRect& oval, SkScalar startAngle, |
+ SkScalar sweepAngle, |
+ SkPoint pts[kSkBuildQuadArcStorage]) { |
+ |
+ if (0 == sweepAngle && |
+ (0 == startAngle || SkIntToScalar(360) == startAngle)) { |
+ // Chrome uses this path to move into and out of ovals. If not |
+ // treated as a special case the moves can distort the oval's |
+ // bounding box (and break the circle special case). |
+ pts[0].set(oval.fRight, oval.centerY()); |
+ return 1; |
+ } else if (0 == oval.width() && 0 == oval.height()) { |
+ // Chrome will sometimes create 0 radius round rects. Having degenerate |
+ // quad segments in the path prevents the path from being recognized as |
+ // a rect. |
+ // TODO: optimizing the case where only one of width or height is zero |
+ // should also be considered. This case, however, doesn't seem to be |
+ // as common as the single point case. |
+ pts[0].set(oval.fRight, oval.fTop); |
+ return 1; |
+ } |
+ |
+ SkVector start, stop; |
+ |
+ start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX); |
+ stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), |
+ &stop.fX); |
+ |
+ /* If the sweep angle is nearly (but less than) 360, then due to precision |
+ loss in radians-conversion and/or sin/cos, we may end up with coincident |
+ vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead |
+ of drawing a nearly complete circle (good). |
+ e.g. canvas.drawArc(0, 359.99, ...) |
+ -vs- canvas.drawArc(0, 359.9, ...) |
+ We try to detect this edge case, and tweak the stop vector |
+ */ |
+ if (start == stop) { |
+ SkScalar sw = SkScalarAbs(sweepAngle); |
+ if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) { |
+ SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle); |
+ // make a guess at a tiny angle (in radians) to tweak by |
+ SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle); |
+ // not sure how much will be enough, so we use a loop |
+ do { |
+ stopRad -= deltaRad; |
+ stop.fY = SkScalarSinCos(stopRad, &stop.fX); |
+ } while (start == stop); |
+ } |
+ } |
+ |
+ SkMatrix matrix; |
+ |
+ matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); |
+ matrix.postTranslate(oval.centerX(), oval.centerY()); |
+ |
+ return SkBuildQuadArc(start, stop, |
+ sweepAngle > 0 ? kCW_SkRotationDirection : |
+ kCCW_SkRotationDirection, |
+ &matrix, pts); |
+} |
+ |
static void add_corner_arc(SkPath* path, const SkRect& rect, |
SkScalar rx, SkScalar ry, int startAngle, |
- SkPath::Direction dir, bool forceMoveTo) { |
+ SkPath::Direction dir, bool addArcTo, |
+ bool forceMoveTo = false) { |
// These two asserts are not sufficient, since really we want to know |
// that the pair of radii (e.g. left and right, or top and bottom) sum |
// to <= dimension, but we don't have that data here, so we just have |
@@ -931,7 +995,15 @@ |
sweep = -sweep; |
} |
- path->arcTo(r, start, sweep, forceMoveTo); |
+ if (addArcTo) { |
+ path->arcTo(r, start, sweep, forceMoveTo); |
+ } else { |
+ SkPoint pts[kSkBuildQuadArcStorage]; |
+ int count = build_arc_points(r, start, sweep, pts); |
+ for (int i = 1; i < count; i += 2) { |
+ path->quadTo(pts[i], pts[i+1]); |
+ } |
+ } |
} |
void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[], |
@@ -961,15 +1033,15 @@ |
SkAutoPathBoundsUpdate apbu(this, bounds); |
if (kCW_Direction == dir) { |
- add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true); |
- add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, false); |
- add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY, 0, dir, false); |
- add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY, 90, dir, false); |
+ add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true, true); |
+ add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, true); |
+ add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY, 0, dir, true); |
+ add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY, 90, dir, true); |
} else { |
- add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true); |
- add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY, 90, dir, false); |
- add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY, 0, dir, false); |
- add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, false); |
+ add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true, true); |
+ add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY, 90, dir, true); |
+ add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY, 0, dir, true); |
+ add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, true); |
} |
this->close(); |
} |
@@ -989,7 +1061,9 @@ |
return true; |
} |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
#define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) |
+#endif |
void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, |
Direction dir) { |
@@ -1031,60 +1105,96 @@ |
ry = halfH; |
} |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); |
SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); |
this->incReserve(17); |
+#else |
+ this->incReserve(13); |
+#endif |
this->moveTo(rect.fRight - rx, rect.fTop); |
if (dir == kCCW_Direction) { |
if (!skip_hori) { |
- this->lineTo(rect.fLeft + rx, rect.fTop); // top |
+ this->lineTo(rect.fLeft + rx, rect.fTop); // top |
} |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
this->cubicTo(rect.fLeft + rx - sx, rect.fTop, |
rect.fLeft, rect.fTop + ry - sy, |
rect.fLeft, rect.fTop + ry); // top-left |
+#else |
+ add_corner_arc(this, rect, rx, ry, 180, dir, false); // top-left |
+#endif |
if (!skip_vert) { |
this->lineTo(rect.fLeft, rect.fBottom - ry); // left |
} |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
this->cubicTo(rect.fLeft, rect.fBottom - ry + sy, |
rect.fLeft + rx - sx, rect.fBottom, |
rect.fLeft + rx, rect.fBottom); // bot-left |
+#else |
+ add_corner_arc(this, rect, rx, ry, 90, dir, false); // bot-left |
+#endif |
if (!skip_hori) { |
- this->lineTo(rect.fRight - rx, rect.fBottom); // bottom |
+ this->lineTo(rect.fRight - rx, rect.fBottom); // bottom |
} |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
this->cubicTo(rect.fRight - rx + sx, rect.fBottom, |
rect.fRight, rect.fBottom - ry + sy, |
rect.fRight, rect.fBottom - ry); // bot-right |
+#else |
+ add_corner_arc(this, rect, rx, ry, 0, dir, false); // bot-right |
+#endif |
if (!skip_vert) { |
- this->lineTo(rect.fRight, rect.fTop + ry); |
+ this->lineTo(rect.fRight, rect.fTop + ry); // right |
} |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
this->cubicTo(rect.fRight, rect.fTop + ry - sy, |
rect.fRight - rx + sx, rect.fTop, |
rect.fRight - rx, rect.fTop); // top-right |
+#else |
+ add_corner_arc(this, rect, rx, ry, 270, dir, false); // top-right |
+#endif |
} else { |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
this->cubicTo(rect.fRight - rx + sx, rect.fTop, |
rect.fRight, rect.fTop + ry - sy, |
rect.fRight, rect.fTop + ry); // top-right |
+#else |
+ add_corner_arc(this, rect, rx, ry, 270, dir, false); // top-right |
+#endif |
if (!skip_vert) { |
- this->lineTo(rect.fRight, rect.fBottom - ry); |
+ this->lineTo(rect.fRight, rect.fBottom - ry); // right |
} |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
this->cubicTo(rect.fRight, rect.fBottom - ry + sy, |
rect.fRight - rx + sx, rect.fBottom, |
rect.fRight - rx, rect.fBottom); // bot-right |
+#else |
+ add_corner_arc(this, rect, rx, ry, 0, dir, false); // bot-right |
+#endif |
if (!skip_hori) { |
- this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom |
+ this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom |
} |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
this->cubicTo(rect.fLeft + rx - sx, rect.fBottom, |
rect.fLeft, rect.fBottom - ry + sy, |
rect.fLeft, rect.fBottom - ry); // bot-left |
+#else |
+ add_corner_arc(this, rect, rx, ry, 90, dir, false); // bot-left |
+#endif |
if (!skip_vert) { |
- this->lineTo(rect.fLeft, rect.fTop + ry); // left |
+ this->lineTo(rect.fLeft, rect.fTop + ry); // left |
} |
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
this->cubicTo(rect.fLeft, rect.fTop + ry - sy, |
rect.fLeft + rx - sx, rect.fTop, |
rect.fLeft + rx, rect.fTop); // top-left |
+#else |
+ add_corner_arc(this, rect, rx, ry, 180, dir, false); // top-left |
+#endif |
if (!skip_hori) { |
- this->lineTo(rect.fRight - rx, rect.fTop); // top |
+ this->lineTo(rect.fRight - rx, rect.fTop); // top |
} |
} |
this->close(); |
@@ -1172,69 +1282,6 @@ |
} |
} |
-#include "SkGeometry.h" |
- |
-static int build_arc_points(const SkRect& oval, SkScalar startAngle, |
- SkScalar sweepAngle, |
- SkPoint pts[kSkBuildQuadArcStorage]) { |
- |
- if (0 == sweepAngle && |
- (0 == startAngle || SkIntToScalar(360) == startAngle)) { |
- // Chrome uses this path to move into and out of ovals. If not |
- // treated as a special case the moves can distort the oval's |
- // bounding box (and break the circle special case). |
- pts[0].set(oval.fRight, oval.centerY()); |
- return 1; |
- } else if (0 == oval.width() && 0 == oval.height()) { |
- // Chrome will sometimes create 0 radius round rects. Having degenerate |
- // quad segments in the path prevents the path from being recognized as |
- // a rect. |
- // TODO: optimizing the case where only one of width or height is zero |
- // should also be considered. This case, however, doesn't seem to be |
- // as common as the single point case. |
- pts[0].set(oval.fRight, oval.fTop); |
- return 1; |
- } |
- |
- SkVector start, stop; |
- |
- start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX); |
- stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), |
- &stop.fX); |
- |
- /* If the sweep angle is nearly (but less than) 360, then due to precision |
- loss in radians-conversion and/or sin/cos, we may end up with coincident |
- vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead |
- of drawing a nearly complete circle (good). |
- e.g. canvas.drawArc(0, 359.99, ...) |
- -vs- canvas.drawArc(0, 359.9, ...) |
- We try to detect this edge case, and tweak the stop vector |
- */ |
- if (start == stop) { |
- SkScalar sw = SkScalarAbs(sweepAngle); |
- if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) { |
- SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle); |
- // make a guess at a tiny angle (in radians) to tweak by |
- SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle); |
- // not sure how much will be enough, so we use a loop |
- do { |
- stopRad -= deltaRad; |
- stop.fY = SkScalarSinCos(stopRad, &stop.fX); |
- } while (start == stop); |
- } |
- } |
- |
- SkMatrix matrix; |
- |
- matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); |
- matrix.postTranslate(oval.centerX(), oval.centerY()); |
- |
- return SkBuildQuadArc(start, stop, |
- sweepAngle > 0 ? kCW_SkRotationDirection : |
- kCCW_SkRotationDirection, |
- &matrix, pts); |
-} |
- |
void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, |
bool forceMoveTo) { |
if (oval.width() < 0 || oval.height() < 0) { |
@@ -1255,8 +1302,7 @@ |
} |
} |
-void SkPath::addArc(const SkRect& oval, SkScalar startAngle, |
- SkScalar sweepAngle) { |
+void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) { |
if (oval.isEmpty() || 0 == sweepAngle) { |
return; |
} |