Index: src/core/SkPath.cpp |
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp |
index 60cfe0373c08628573cf5be16ff829f428e7cd0f..9bcf18c8f7ba85a84679c0aaf290b94603ab71ec 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); |
+*/ |
robertphillips
2013/11/05 16:47:56
Would it make sense to pass in the SkRRect itself
caryclark
2013/11/05 20:29:55
Done.
|
+static void add_corner_quads(SkPath* path, const SkRect& rect, const SkVector& radii, |
+ SkRRect::Corner corner, SkPath::Direction dir) { |
+ SkScalar rx = radii.fX; |
+ SkScalar ry = radii.fY; |
+ // 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 - rx * (SK_Scalar1 + SK_ScalarTanPIOver8 + FLT_EPSILON * 4) / 2; |
+ SkScalar midPtY = ry - ry * (SK_Scalar1 + SK_ScalarTanPIOver8 + FLT_EPSILON * 4) / 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]; // cw lower right, lower left |
+ } |
+ } 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); |
@@ -1009,18 +1099,23 @@ void SkPath::addRRect(const SkRRect& rrect, Direction dir) { |
const SkVector& rad = rrect.getSimpleRadii(); |
this->addRoundRect(bounds, rad.x(), rad.y(), dir); |
} else { |
+ fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; |
+ |
SkAutoPathBoundsUpdate apbu(this, bounds); |
+ this->incReserve(21); |
if (kCW_Direction == dir) { |
robertphillips
2013/11/05 16:47:56
Should 0 be 3 here? One of the Corner enum values
caryclark
2013/11/05 20:29:55
Done.
|
- 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[0].fY); // bottom-left |
+ add_corner_quads(this, bounds, rrect.fRadii[0], SkRRect::kUpperLeft_Corner, dir); |
+ add_corner_quads(this, bounds, rrect.fRadii[1], SkRRect::kUpperRight_Corner, dir); |
+ add_corner_quads(this, bounds, rrect.fRadii[2], SkRRect::kLowerRight_Corner, dir); |
+ add_corner_quads(this, bounds, rrect.fRadii[3], 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[0].fY); // top-left |
+ add_corner_quads(this, bounds, rrect.fRadii[3], SkRRect::kLowerLeft_Corner, dir); |
+ add_corner_quads(this, bounds, rrect.fRadii[2], SkRRect::kLowerRight_Corner, dir); |
+ add_corner_quads(this, bounds, rrect.fRadii[1], SkRRect::kUpperRight_Corner, dir); |
+ add_corner_quads(this, bounds, rrect.fRadii[0], SkRRect::kUpperLeft_Corner, dir); |
} |
this->close(); |
} |