Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(288)

Unified Diff: src/core/SkPath.cpp

Issue 26372006: Replace cubic round rect corners with quads (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: base files missing retry Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « expectations/gm/ignored-tests.txt ('k') | src/core/SkPathRef.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
}
« no previous file with comments | « expectations/gm/ignored-tests.txt ('k') | src/core/SkPathRef.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698