| Index: src/core/SkPath.cpp
|
| diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
|
| index 1326362524c2847227c821926abeac1958430489..a3f5e13d7fa924f29a639ecc9fd6d10fff64a21c 100644
|
| --- a/src/core/SkPath.cpp
|
| +++ b/src/core/SkPath.cpp
|
| @@ -840,36 +840,118 @@ void SkPath::close() {
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| +namespace {
|
| +
|
| +template <unsigned N>
|
| +class PointIterator {
|
| +public:
|
| + PointIterator(SkPath::Direction dir, unsigned startIndex)
|
| + : fCurrent(startIndex % N)
|
| + , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { }
|
| +
|
| + const SkPoint& current() const {
|
| + SkASSERT(fCurrent < N);
|
| + return fPts[fCurrent];
|
| + }
|
| +
|
| + const SkPoint& next() {
|
| + fCurrent = (fCurrent + fAdvance) % N;
|
| + return this->current();
|
| + }
|
| +
|
| +protected:
|
| + SkPoint fPts[N];
|
| +
|
| +private:
|
| + unsigned fCurrent;
|
| + unsigned fAdvance;
|
| +};
|
| +
|
| +class RectPointIterator : public PointIterator<4> {
|
| +public:
|
| + RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startIndex)
|
| + : PointIterator(dir, startIndex) {
|
| +
|
| + fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
|
| + fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
|
| + fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
|
| + fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
|
| + }
|
| +};
|
| +
|
| +class OvalPointIterator : public PointIterator<4> {
|
| +public:
|
| + OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startIndex)
|
| + : PointIterator(dir, startIndex) {
|
| +
|
| + const SkScalar cx = oval.centerX();
|
| + const SkScalar cy = oval.centerY();
|
| +
|
| + fPts[0] = SkPoint::Make(cx, oval.fTop);
|
| + fPts[1] = SkPoint::Make(oval.fRight, cy);
|
| + fPts[2] = SkPoint::Make(cx, oval.fBottom);
|
| + fPts[3] = SkPoint::Make(oval.fLeft, cy);
|
| + }
|
| +};
|
| +
|
| +class RRectPointIterator : public PointIterator<8> {
|
| +public:
|
| + RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned startIndex)
|
| + : PointIterator(dir, startIndex) {
|
| +
|
| + const SkRect& bounds = rrect.getBounds();
|
| + const SkScalar L = bounds.fLeft;
|
| + const SkScalar T = bounds.fTop;
|
| + const SkScalar R = bounds.fRight;
|
| + const SkScalar B = bounds.fBottom;
|
| +
|
| + fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
|
| + fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
|
| + fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY);
|
| + fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY);
|
| + fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
|
| + fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
|
| + fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY);
|
| + fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY);
|
| + }
|
| +};
|
| +
|
| +} // anonymous namespace
|
| +
|
| static void assert_known_direction(int dir) {
|
| SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
|
| }
|
|
|
| void SkPath::addRect(const SkRect& rect, Direction dir) {
|
| - this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir);
|
| + this->addRect(rect, dir, 0);
|
| }
|
|
|
| void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
|
| SkScalar bottom, Direction dir) {
|
| + this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0);
|
| +}
|
| +
|
| +void SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) {
|
| assert_known_direction(dir);
|
| fFirstDirection = this->hasOnlyMoveTos() ?
|
| - (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
|
| + (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
|
| SkAutoDisableDirectionCheck addc(this);
|
| + SkAutoPathBoundsUpdate apbu(this, rect);
|
|
|
| - SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
|
| + SkDEBUGCODE(int initialVerbCount = this->countVerbs());
|
|
|
| - this->incReserve(5);
|
| + const int kVerbs = 5; // moveTo + 3x lineTo + close
|
| + this->incReserve(kVerbs);
|
|
|
| - this->moveTo(left, top);
|
| - if (dir == kCCW_Direction) {
|
| - this->lineTo(left, bottom);
|
| - this->lineTo(right, bottom);
|
| - this->lineTo(right, top);
|
| - } else {
|
| - this->lineTo(right, top);
|
| - this->lineTo(right, bottom);
|
| - this->lineTo(left, bottom);
|
| - }
|
| + RectPointIterator iter(rect, dir, startIndex);
|
| +
|
| + this->moveTo(iter.current());
|
| + this->lineTo(iter.next());
|
| + this->lineTo(iter.next());
|
| + this->lineTo(iter.next());
|
| this->close();
|
| +
|
| + SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
|
| }
|
|
|
| void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
|
| @@ -979,64 +1061,68 @@ void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
|
| }
|
|
|
| void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
|
| - assert_known_direction(dir);
|
| -
|
| - if (rrect.isEmpty()) {
|
| - return;
|
| - }
|
| -
|
| - const SkRect& bounds = rrect.getBounds();
|
| -
|
| - if (rrect.isRect()) {
|
| - this->addRect(bounds, dir);
|
| - } else if (rrect.isOval()) {
|
| - this->addOval(bounds, dir);
|
| - } else {
|
| - fFirstDirection = this->hasOnlyMoveTos() ?
|
| - (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
|
| -
|
| - SkAutoPathBoundsUpdate apbu(this, bounds);
|
| - SkAutoDisableDirectionCheck addc(this);
|
| -
|
| - const SkScalar L = bounds.fLeft;
|
| - const SkScalar T = bounds.fTop;
|
| - const SkScalar R = bounds.fRight;
|
| - const SkScalar B = bounds.fBottom;
|
| - const SkScalar W = SK_ScalarRoot2Over2;
|
| -
|
| - this->incReserve(13);
|
| - if (kCW_Direction == dir) {
|
| - this->moveTo(L, B - rrect.fRadii[SkRRect::kLowerLeft_Corner].fY);
|
| + // legacy start indices: 6 (CW) and 7(CCW)
|
| + this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
|
| +}
|
|
|
| - this->lineTo(L, T + rrect.fRadii[SkRRect::kUpperLeft_Corner].fY);
|
| - this->conicTo(L, T, L + rrect.fRadii[SkRRect::kUpperLeft_Corner].fX, T, W);
|
| +void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
|
| + assert_known_direction(dir);
|
|
|
| - this->lineTo(R - rrect.fRadii[SkRRect::kUpperRight_Corner].fX, T);
|
| - this->conicTo(R, T, R, T + rrect.fRadii[SkRRect::kUpperRight_Corner].fY, W);
|
| + if (rrect.isEmpty()) {
|
| + return;
|
| + }
|
|
|
| - this->lineTo(R, B - rrect.fRadii[SkRRect::kLowerRight_Corner].fY);
|
| - this->conicTo(R, B, R - rrect.fRadii[SkRRect::kLowerRight_Corner].fX, B, W);
|
| + const SkRect& bounds = rrect.getBounds();
|
|
|
| - this->lineTo(L + rrect.fRadii[SkRRect::kLowerLeft_Corner].fX, B);
|
| - this->conicTo(L, B, L, B - rrect.fRadii[SkRRect::kLowerLeft_Corner].fY, W);
|
| + if (rrect.isRect()) {
|
| + // degenerate(rect) => radii points are collapsing
|
| + this->addRect(bounds, dir, (startIndex + 1) / 2);
|
| + } else if (rrect.isOval()) {
|
| + // degenerate(oval) => line points are collapsing
|
| + this->addOval(bounds, dir, startIndex / 2);
|
| } else {
|
| - this->moveTo(L, T + rrect.fRadii[SkRRect::kUpperLeft_Corner].fY);
|
| -
|
| - this->lineTo(L, B - rrect.fRadii[SkRRect::kLowerLeft_Corner].fY);
|
| - this->conicTo(L, B, L + rrect.fRadii[SkRRect::kLowerLeft_Corner].fX, B, W);
|
| -
|
| - this->lineTo(R - rrect.fRadii[SkRRect::kLowerRight_Corner].fX, B);
|
| - this->conicTo(R, B, R, B - rrect.fRadii[SkRRect::kLowerRight_Corner].fY, W);
|
| -
|
| - this->lineTo(R, T + rrect.fRadii[SkRRect::kUpperRight_Corner].fY);
|
| - this->conicTo(R, T, R - rrect.fRadii[SkRRect::kUpperRight_Corner].fX, T, W);
|
| + fFirstDirection = this->hasOnlyMoveTos() ?
|
| + (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
|
| +
|
| + SkAutoPathBoundsUpdate apbu(this, bounds);
|
| + SkAutoDisableDirectionCheck addc(this);
|
| +
|
| + // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
|
| + const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direction));
|
| + const SkScalar weight = SK_ScalarRoot2Over2;
|
| +
|
| + SkDEBUGCODE(int initialVerbCount = this->countVerbs());
|
| + const int kVerbs = startsWithConic
|
| + ? 9 // moveTo + 4x conicTo + 3x lineTo + close
|
| + : 10; // moveTo + 4x lineTo + 4x conicTo + close
|
| + this->incReserve(kVerbs);
|
| +
|
| + RRectPointIterator rrectIter(rrect, dir, startIndex);
|
| + // Corner iterator indices follow the collapsed radii model,
|
| + // adjusted such that the start pt is "behind" the radii start pt.
|
| + const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Direction ? 0 : 1);
|
| + RectPointIterator rectIter(bounds, dir, rectStartIndex);
|
| +
|
| + this->moveTo(rrectIter.current());
|
| + if (startsWithConic) {
|
| + for (unsigned i = 0; i < 3; ++i) {
|
| + this->conicTo(rectIter.next(), rrectIter.next(), weight);
|
| + this->lineTo(rrectIter.next());
|
| + }
|
| + this->conicTo(rectIter.next(), rrectIter.next(), weight);
|
| + // final lineTo handled by close().
|
| + } else {
|
| + for (unsigned i = 0; i < 4; ++i) {
|
| + this->lineTo(rrectIter.next());
|
| + this->conicTo(rectIter.next(), rrectIter.next(), weight);
|
| + }
|
| + }
|
| + this->close();
|
|
|
| - this->lineTo(L + rrect.fRadii[SkRRect::kUpperLeft_Corner].fX, T);
|
| - this->conicTo(L, T, L, T + rrect.fRadii[SkRRect::kUpperLeft_Corner].fY, W);
|
| + SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
|
| }
|
| - this->close();
|
| - }
|
| - SkDEBUGCODE(fPathRef->validate();)
|
| +
|
| + SkDEBUGCODE(fPathRef->validate();)
|
| }
|
|
|
| bool SkPath::hasOnlyMoveTos() const {
|
| @@ -1072,6 +1158,11 @@ void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
|
| }
|
|
|
| void SkPath::addOval(const SkRect& oval, Direction dir) {
|
| + // legacy start index: 1
|
| + this->addOval(oval, dir, 1);
|
| +}
|
| +
|
| +void SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex) {
|
| assert_known_direction(dir);
|
|
|
| /* If addOval() is called after previous moveTo(),
|
| @@ -1088,32 +1179,25 @@ void SkPath::addOval(const SkRect& oval, Direction dir) {
|
| }
|
|
|
| SkAutoDisableDirectionCheck addc(this);
|
| -
|
| SkAutoPathBoundsUpdate apbu(this, oval);
|
|
|
| - const SkScalar L = oval.fLeft;
|
| - const SkScalar T = oval.fTop;
|
| - const SkScalar R = oval.fRight;
|
| - const SkScalar B = oval.fBottom;
|
| - const SkScalar cx = oval.centerX();
|
| - const SkScalar cy = oval.centerY();
|
| + SkDEBUGCODE(int initialVerbCount = this->countVerbs());
|
| + const int kVerbs = 6; // moveTo + 4x conicTo + close
|
| + this->incReserve(kVerbs);
|
| +
|
| + OvalPointIterator ovalIter(oval, dir, startPointIndex);
|
| + // The corner iterator pts are tracking "behind" the oval/radii pts.
|
| + RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Direction ? 0 : 1));
|
| const SkScalar weight = SK_ScalarRoot2Over2;
|
|
|
| - this->incReserve(9); // move + 4 conics
|
| - this->moveTo(R, cy);
|
| - if (dir == kCCW_Direction) {
|
| - this->conicTo(R, T, cx, T, weight);
|
| - this->conicTo(L, T, L, cy, weight);
|
| - this->conicTo(L, B, cx, B, weight);
|
| - this->conicTo(R, B, R, cy, weight);
|
| - } else {
|
| - this->conicTo(R, B, cx, B, weight);
|
| - this->conicTo(L, B, L, cy, weight);
|
| - this->conicTo(L, T, cx, T, weight);
|
| - this->conicTo(R, T, R, cy, weight);
|
| + this->moveTo(ovalIter.current());
|
| + for (unsigned i = 0; i < 4; ++i) {
|
| + this->conicTo(rectIter.next(), ovalIter.next(), weight);
|
| }
|
| this->close();
|
|
|
| + SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
|
| +
|
| SkPathRef::Editor ed(&fPathRef);
|
|
|
| ed.setIsOval(isOval);
|
| @@ -1121,9 +1205,7 @@ void SkPath::addOval(const SkRect& oval, Direction dir) {
|
|
|
| void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
|
| if (r > 0) {
|
| - SkRect rect;
|
| - rect.set(x - r, y - r, x + r, y + r);
|
| - this->addOval(rect, dir);
|
| + this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
|
| }
|
| }
|
|
|
|
|