Chromium Code Reviews| Index: src/core/SkPath.cpp |
| diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp |
| index 60cfe0373c08628573cf5be16ff829f428e7cd0f..aaedd40db6f63c91d6bcaa66198733f4a64aaf9f 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,133 @@ 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. 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]; |
| + } |
| + } 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 +1097,31 @@ void SkPath::addRRect(const SkRRect& rrect, Direction dir) { |
| this->addRect(bounds, dir); |
| } else if (rrect.isOval()) { |
| this->addOval(bounds, dir); |
|
robertphillips
2013/11/06 13:31:58
I don't think this needs to be #ifdef protected. C
caryclark
2013/11/06 13:39:55
This is ifdef protected to avoid recursive functio
|
| +#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); |
|
robertphillips
2013/11/06 13:31:58
Do we need a "SkAutoDisableDirectionCheck(this);"
caryclark
2013/11/06 13:39:55
I didn't know about this helper. Yes, it looks lik
caryclark
2013/11/06 13:58:28
Done.
|
| + 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 +1157,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 +1185,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) { |