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

Unified Diff: src/core/SkPath.cpp

Issue 60203002: use quads for mixed radius rrects (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: remove float slop; add test Created 7 years, 1 month 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 | « no previous file | tests/PathTest.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/core/SkPath.cpp
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index c480624a166a287a56701259adf8609c4bdf2dee..873f43336405f58386f8f3542093b93eccb0e70c 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -950,41 +950,6 @@ static int build_arc_points(const SkRect& oval, SkScalar startAngle,
&matrix, pts);
}
-static void add_corner_arc(SkPath* path, const SkRect& rect,
- SkScalar rx, SkScalar ry, int startAngle,
- SkPath::Direction dir, bool forceMoveTo) {
- // 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
- // these conservative asserts.
- SkASSERT(0 <= rx && rx <= rect.width());
- SkASSERT(0 <= ry && ry <= rect.height());
-
- SkRect r;
- r.set(-rx, -ry, rx, ry);
-
- switch (startAngle) {
- case 0:
- r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
- break;
- case 90:
- r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom);
- break;
- case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break;
- case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
- default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc");
- }
-
- SkScalar start = SkIntToScalar(startAngle);
- SkScalar sweep = SkIntToScalar(90);
- if (SkPath::kCCW_Direction == dir) {
- start += sweep;
- sweep = -sweep;
- }
-
- path->arcTo(r, start, sweep, forceMoveTo);
-}
-
void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
Direction dir) {
SkRRect rrect;
@@ -992,6 +957,131 @@ void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
this->addRRect(rrect, dir);
}
+/* The inline clockwise and counterclockwise round rect quad approximations
+ make it easier to see the symmetry patterns used by add corner quads.
+Clockwise corner value
+ path->lineTo(rect.fLeft, rect.fTop + ry); 0 upper left
+ path->quadTo(rect.fLeft, rect.fTop + offPtY,
+ rect.fLeft + midPtX, rect.fTop + midPtY);
+ path->quadTo(rect.fLeft + offPtX, rect.fTop,
+ rect.fLeft + rx, rect.fTop);
+
+ path->lineTo(rect.fRight - rx, rect.fTop); 1 upper right
+ path->quadTo(rect.fRight - offPtX, rect.fTop,
+ rect.fRight - midPtX, rect.fTop + midPtY);
+ path->quadTo(rect.fRight, rect.fTop + offPtY,
+ rect.fRight, rect.fTop + ry);
+
+ path->lineTo(rect.fRight, rect.fBottom - ry); 2 lower right
+ path->quadTo(rect.fRight, rect.fBottom - offPtY,
+ rect.fRight - midPtX, rect.fBottom - midPtY);
+ path->quadTo(rect.fRight - offPtX, rect.fBottom,
+ rect.fRight - rx, rect.fBottom);
+
+ path->lineTo(rect.fLeft + rx, rect.fBottom); 3 lower left
+ path->quadTo(rect.fLeft + offPtX, rect.fBottom,
+ rect.fLeft + midPtX, rect.fBottom - midPtY);
+ path->quadTo(rect.fLeft, rect.fBottom - offPtY,
+ rect.fLeft, rect.fBottom - ry);
+
+Counterclockwise
+ path->lineTo(rect.fLeft, rect.fBottom - ry); 3 lower left
+ path->quadTo(rect.fLeft, rect.fBottom - offPtY,
+ rect.fLeft + midPtX, rect.fBottom - midPtY);
+ path->quadTo(rect.fLeft + offPtX, rect.fBottom,
+ rect.fLeft + rx, rect.fBottom);
+
+ path->lineTo(rect.fRight - rx, rect.fBottom); 2 lower right
+ path->quadTo(rect.fRight - offPtX, rect.fBottom,
+ rect.fRight - midPtX, rect.fBottom - midPtY);
+ path->quadTo(rect.fRight, rect.fBottom - offPtY,
+ rect.fRight, rect.fBottom - ry);
+
+ path->lineTo(rect.fRight, rect.fTop + ry); 1 upper right
+ path->quadTo(rect.fRight, rect.fTop + offPtY,
+ rect.fRight - midPtX, rect.fTop + midPtY);
+ path->quadTo(rect.fRight - offPtX, rect.fTop,
+ rect.fRight - rx, rect.fTop);
+
+ path->lineTo(rect.fLeft + rx, rect.fTop); 0 upper left
+ path->quadTo(rect.fLeft + offPtX, rect.fTop,
+ rect.fLeft + midPtX, rect.fTop + midPtY);
+ path->quadTo(rect.fLeft, rect.fTop + offPtY,
+ rect.fLeft, rect.fTop + ry);
+*/
+static void add_corner_quads(SkPath* path, const SkRRect& rrect,
+ SkRRect::Corner corner, SkPath::Direction dir) {
+ const SkRect& rect = rrect.rect();
+ const SkVector& radii = rrect.radii(corner);
+ SkScalar rx = radii.fX;
+ SkScalar ry = radii.fY;
+ // The mid point of the quadratic arc approximation is half way between the two
+ // control points.
+ SkScalar midPtX = rx - rx * (SK_Scalar1 + SK_ScalarTanPIOver8) / 2;
reed1 2013/11/08 15:46:40 micro-perf-nit: can/should we rewrite this expres
+ SkScalar midPtY = ry - ry * (SK_Scalar1 + SK_ScalarTanPIOver8) / 2;
+ SkScalar offPtX = rx - rx * SK_ScalarTanPIOver8;
+ SkScalar offPtY = ry - ry * SK_ScalarTanPIOver8;
+ static const int kCornerPts = 5;
+ SkScalar xOff[kCornerPts];
+ SkScalar yOff[kCornerPts];
+
+ if ((corner & 1) == (dir == SkPath::kCCW_Direction)) { // corners always alternate direction
+ SkASSERT(dir == SkPath::kCCW_Direction
+ ? corner == SkRRect::kLowerLeft_Corner || corner == SkRRect::kUpperRight_Corner
+ : corner == SkRRect::kUpperLeft_Corner || corner == SkRRect::kLowerRight_Corner);
+ xOff[0] = xOff[1] = 0;
+ xOff[2] = midPtX;
+ xOff[3] = offPtX;
+ xOff[4] = rx;
+ yOff[0] = ry;
+ yOff[1] = offPtY;
+ yOff[2] = midPtY;
+ yOff[3] = yOff[4] = 0;
+ } else {
+ xOff[0] = rx;
+ xOff[1] = offPtX;
+ xOff[2] = midPtX;
+ xOff[3] = xOff[4] = 0;
+ yOff[0] = yOff[1] = 0;
+ yOff[2] = midPtY;
+ yOff[3] = offPtY;
+ yOff[4] = ry;
+ }
+ if ((corner - 1) & 2) {
+ SkASSERT(corner == SkRRect::kLowerLeft_Corner || corner == SkRRect::kUpperLeft_Corner);
+ for (int i = 0; i < kCornerPts; ++i) {
+ xOff[i] = rect.fLeft + xOff[i];
+ }
+ } else {
+ SkASSERT(corner == SkRRect::kLowerRight_Corner || corner == SkRRect::kUpperRight_Corner);
+ for (int i = 0; i < kCornerPts; ++i) {
+ xOff[i] = rect.fRight - xOff[i];
+ }
+ }
+ if (corner < SkRRect::kLowerRight_Corner) {
+ for (int i = 0; i < kCornerPts; ++i) {
+ yOff[i] = rect.fTop + yOff[i];
+ }
+ } else {
+ for (int i = 0; i < kCornerPts; ++i) {
+ yOff[i] = rect.fBottom - yOff[i];
+ }
+ }
+
+ SkPoint lastPt;
+ SkAssertResult(path->getLastPt(&lastPt));
+ if (lastPt.fX != xOff[0] || lastPt.fY != yOff[0]) {
+ path->lineTo(xOff[0], yOff[0]);
+ }
+ if (rx || ry) {
+ path->quadTo(xOff[1], yOff[1], xOff[2], yOff[2]);
+ path->quadTo(xOff[3], yOff[3], xOff[4], yOff[4]);
+ } else {
+ path->lineTo(xOff[2], yOff[2]);
+ path->lineTo(xOff[4], yOff[4]);
+ }
+}
+
void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
assert_known_direction(dir);
@@ -1005,22 +1095,32 @@ void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
this->addRect(bounds, dir);
} else if (rrect.isOval()) {
this->addOval(bounds, dir);
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT
} else if (rrect.isSimple()) {
const SkVector& rad = rrect.getSimpleRadii();
this->addRoundRect(bounds, rad.x(), rad.y(), dir);
+#endif
} else {
+ fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
+
SkAutoPathBoundsUpdate apbu(this, bounds);
+ SkAutoDisableDirectionCheck(this);
+ this->incReserve(21);
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);
+ this->moveTo(bounds.fLeft,
+ bounds.fBottom - rrect.fRadii[SkRRect::kLowerLeft_Corner].fY);
+ add_corner_quads(this, rrect, SkRRect::kUpperLeft_Corner, dir);
+ add_corner_quads(this, rrect, SkRRect::kUpperRight_Corner, dir);
+ add_corner_quads(this, rrect, SkRRect::kLowerRight_Corner, dir);
+ add_corner_quads(this, rrect, SkRRect::kLowerLeft_Corner, dir);
} 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);
+ this->moveTo(bounds.fLeft,
+ bounds.fTop + rrect.fRadii[SkRRect::kUpperLeft_Corner].fY);
+ add_corner_quads(this, rrect, SkRRect::kLowerLeft_Corner, dir);
+ add_corner_quads(this, rrect, SkRRect::kLowerRight_Corner, dir);
+ add_corner_quads(this, rrect, SkRRect::kUpperRight_Corner, dir);
+ add_corner_quads(this, rrect, SkRRect::kUpperLeft_Corner, dir);
}
this->close();
}
@@ -1056,6 +1156,7 @@ void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
return;
}
+#ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT
SkScalar w = rect.width();
SkScalar halfW = SkScalarHalf(w);
SkScalar h = rect.height();
@@ -1083,133 +1184,68 @@ void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
} else if (skip_vert) {
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
-// The mid point of the quadratic arc approximation is half way between the two
-// control points. The float epsilon adjustment moves the on curve point out by
-// two bits, distributing the convex test error between the round rect approximation
-// and the convex cross product sign equality test.
- SkScalar midPtX = rx * (SK_Scalar1 + SK_ScalarTanPIOver8 + FLT_EPSILON * 4) / 2;
- SkScalar midPtY = ry * (SK_Scalar1 + SK_ScalarTanPIOver8 + FLT_EPSILON * 4) / 2;
-
- SkScalar offPtX = rx * SK_ScalarTanPIOver8;
- SkScalar offPtY = ry * SK_ScalarTanPIOver8;
-
- this->incReserve(21);
-#endif
this->moveTo(rect.fRight - rx, rect.fTop); // top-right
if (dir == kCCW_Direction) {
if (!skip_hori) {
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
- this->quadTo(rect.fLeft + rx - offPtX, rect.fTop,
- rect.fLeft + rx - midPtX, rect.fTop + ry - midPtY);
- this->quadTo(rect.fLeft, rect.fTop + ry - offPtY,
- rect.fLeft, rect.fTop + ry);
-#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
- this->quadTo(rect.fLeft, rect.fBottom - ry + offPtY,
- rect.fLeft + rx - midPtX, rect.fBottom - ry + midPtY);
- this->quadTo(rect.fLeft + rx - offPtX, rect.fBottom,
- rect.fLeft + rx, rect.fBottom);
-#endif
if (!skip_hori) {
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
- this->quadTo(rect.fRight - rx + offPtX, rect.fBottom,
- rect.fRight - rx + midPtX, rect.fBottom - ry + midPtY);
- this->quadTo(rect.fRight, rect.fBottom - ry + offPtY,
- rect.fRight, rect.fBottom - ry);
-#endif
if (!skip_vert) {
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
- this->quadTo(rect.fRight, rect.fTop + ry - offPtY,
- rect.fRight - rx + midPtX, rect.fTop + ry - midPtY);
- this->quadTo(rect.fRight - rx + offPtX, rect.fTop,
- rect.fRight - rx, rect.fTop);
-#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
- this->quadTo(rect.fRight - rx + offPtX, rect.fTop,
- rect.fRight - rx + midPtX, rect.fTop + ry - midPtY);
- this->quadTo(rect.fRight, rect.fTop + ry - offPtY,
- rect.fRight, rect.fTop + ry);
-#endif
if (!skip_vert) {
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
- this->quadTo(rect.fRight, rect.fBottom - ry + offPtY,
- rect.fRight - rx + midPtX, rect.fBottom - ry + midPtY);
- this->quadTo(rect.fRight - rx + offPtX, rect.fBottom,
- rect.fRight - rx, rect.fBottom);
-#endif
if (!skip_hori) {
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
- this->quadTo(rect.fLeft + rx - offPtX, rect.fBottom,
- rect.fLeft + rx - midPtX, rect.fBottom - ry + midPtY);
- this->quadTo(rect.fLeft, rect.fBottom - ry + offPtY,
- rect.fLeft, rect.fBottom - ry);
-#endif
if (!skip_vert) {
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
- this->quadTo(rect.fLeft, rect.fTop + ry - offPtY,
- rect.fLeft + rx - midPtX, rect.fTop + ry - midPtY);
- this->quadTo(rect.fLeft + rx - offPtX, rect.fTop,
- rect.fLeft + rx, rect.fTop);
-#endif
if (!skip_hori) {
this->lineTo(rect.fRight - rx, rect.fTop); // top
}
}
this->close();
+#else
+ SkRRect rrect;
+ rrect.setRectXY(rect, rx, ry);
+ this->addRRect(rrect, dir);
+#endif
}
void SkPath::addOval(const SkRect& oval, Direction dir) {
« no previous file with comments | « no previous file | tests/PathTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698