aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkPath.cpp
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-11-08 15:51:12 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-11-08 15:51:12 +0000
commit42feaaf0a5fbb508b237d5c844c484a1a3b0c865 (patch)
tree92a7a2255e5b2cdb62877577705390efc31debd7 /src/core/SkPath.cpp
parentc78b8f2f738d3e20ceb971f2b0da0a0802eab9bd (diff)
use quads for mixed radius rrects
Create a specialized version of adding a pair of corner quads that avoids the overhead of the full arc machinery. This is on the way to changing Chrome to calling Skia directly to create fully general round rects rather than rolling their own. R=robertphillips@google.com, reed@google.com Author: caryclark@google.com Review URL: https://codereview.chromium.org/60203002 git-svn-id: http://skia.googlecode.com/svn/trunk@12190 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/core/SkPath.cpp')
-rw-r--r--src/core/SkPath.cpp262
1 files changed, 149 insertions, 113 deletions
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index c480624a16..873f433364 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;
+ 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) {