Parametric SkPath oval/rect/rrect starting point
Extend the SkPath API to allow specifying the contour starting point. This will allow removing the explicit cubic code from Blink/Path. BUG=chromium:315277 R=reed@google.com,caryclark@google.com Review URL: https://codereview.chromium.org/1452203002
This commit is contained in:
parent
83a5d424ae
commit
c08d53ee17
@ -563,9 +563,26 @@ public:
|
||||
* Add a closed rectangle contour to the path
|
||||
* @param rect The rectangle to add as a closed contour to the path
|
||||
* @param dir The direction to wind the rectangle's contour.
|
||||
*
|
||||
* Note: the contour initial point index is 0 (as defined below).
|
||||
*/
|
||||
void addRect(const SkRect& rect, Direction dir = kCW_Direction);
|
||||
|
||||
/**
|
||||
* Add a closed rectangle contour to the path
|
||||
* @param rect The rectangle to add as a closed contour to the path
|
||||
* @param dir The direction to wind the rectangle's contour.
|
||||
* @param start Initial point of the contour (initial moveTo), expressed as
|
||||
* a corner index, starting in the upper-left position, clock-wise:
|
||||
*
|
||||
* 0 1
|
||||
* *-------*
|
||||
* | |
|
||||
* *-------*
|
||||
* 3 2
|
||||
*/
|
||||
void addRect(const SkRect& rect, Direction dir, unsigned start);
|
||||
|
||||
/**
|
||||
* Add a closed rectangle contour to the path
|
||||
*
|
||||
@ -578,6 +595,8 @@ public:
|
||||
* @param bottom The bottom of a rectangle to add as a closed contour to
|
||||
* the path
|
||||
* @param dir The direction to wind the rectangle's contour.
|
||||
*
|
||||
* Note: the contour initial point index is 0 (as defined above).
|
||||
*/
|
||||
void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
|
||||
Direction dir = kCW_Direction);
|
||||
@ -587,9 +606,30 @@ public:
|
||||
*
|
||||
* @param oval The bounding oval to add as a closed contour to the path
|
||||
* @param dir The direction to wind the oval's contour.
|
||||
*
|
||||
* Note: the contour initial point index is 1 (as defined below).
|
||||
*/
|
||||
void addOval(const SkRect& oval, Direction dir = kCW_Direction);
|
||||
|
||||
/**
|
||||
* Add a closed oval contour to the path
|
||||
*
|
||||
* @param oval The bounding oval to add as a closed contour to the path
|
||||
* @param dir The direction to wind the oval's contour.
|
||||
* @param start Initial point of the contour (initial moveTo), expressed
|
||||
* as an ellipse vertex index, starting at the top, clock-wise
|
||||
* (90/0/270/180deg order):
|
||||
*
|
||||
* 0
|
||||
* -*-
|
||||
* | |
|
||||
* 3 * * 1
|
||||
* | |
|
||||
* -*-
|
||||
* 2
|
||||
*/
|
||||
void addOval(const SkRect& oval, Direction dir, unsigned start);
|
||||
|
||||
/**
|
||||
* Add a closed circle contour to the path
|
||||
*
|
||||
@ -640,9 +680,30 @@ public:
|
||||
* Add an SkRRect contour to the path
|
||||
* @param rrect The rounded rect to add as a closed contour
|
||||
* @param dir The winding direction for the new contour.
|
||||
*
|
||||
* Note: the contour initial point index is either 6 (for dir == kCW_Direction)
|
||||
* or 7 (for dir == kCCW_Direction), as defined below.
|
||||
*
|
||||
*/
|
||||
void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction);
|
||||
|
||||
/**
|
||||
* Add an SkRRect contour to the path
|
||||
* @param rrect The rounded rect to add as a closed contour
|
||||
* @param dir The winding direction for the new contour.
|
||||
* @param start Initial point of the contour (initial moveTo), expressed as
|
||||
* an index of the radii minor/major points, ordered clock-wise:
|
||||
*
|
||||
* 0 1
|
||||
* *----*
|
||||
* 7 * * 2
|
||||
* | |
|
||||
* 6 * * 3
|
||||
* *----*
|
||||
* 5 4
|
||||
*/
|
||||
void addRRect(const SkRRect& rrect, Direction dir, unsigned start);
|
||||
|
||||
/**
|
||||
* Add a new contour made of just lines. This is just a fast version of
|
||||
* the following:
|
||||
|
@ -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;
|
||||
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,6 +1061,11 @@ void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
|
||||
}
|
||||
|
||||
void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
|
||||
// legacy start indices: 6 (CW) and 7(CCW)
|
||||
this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
|
||||
}
|
||||
|
||||
void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
|
||||
assert_known_direction(dir);
|
||||
|
||||
if (rrect.isEmpty()) {
|
||||
@ -988,9 +1075,11 @@ void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
|
||||
const SkRect& bounds = rrect.getBounds();
|
||||
|
||||
if (rrect.isRect()) {
|
||||
this->addRect(bounds, dir);
|
||||
// degenerate(rect) => radii points are collapsing
|
||||
this->addRect(bounds, dir, (startIndex + 1) / 2);
|
||||
} else if (rrect.isOval()) {
|
||||
this->addOval(bounds, dir);
|
||||
// degenerate(oval) => line points are collapsing
|
||||
this->addOval(bounds, dir, startIndex / 2);
|
||||
} else {
|
||||
fFirstDirection = this->hasOnlyMoveTos() ?
|
||||
(SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
|
||||
@ -998,44 +1087,41 @@ void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
|
||||
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;
|
||||
// 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;
|
||||
|
||||
this->incReserve(13);
|
||||
if (kCW_Direction == dir) {
|
||||
this->moveTo(L, B - rrect.fRadii[SkRRect::kLowerLeft_Corner].fY);
|
||||
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);
|
||||
|
||||
this->lineTo(L, T + rrect.fRadii[SkRRect::kUpperLeft_Corner].fY);
|
||||
this->conicTo(L, T, L + rrect.fRadii[SkRRect::kUpperLeft_Corner].fX, T, W);
|
||||
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->lineTo(R - rrect.fRadii[SkRRect::kUpperRight_Corner].fX, T);
|
||||
this->conicTo(R, T, R, T + rrect.fRadii[SkRRect::kUpperRight_Corner].fY, W);
|
||||
|
||||
this->lineTo(R, B - rrect.fRadii[SkRRect::kLowerRight_Corner].fY);
|
||||
this->conicTo(R, B, R - rrect.fRadii[SkRRect::kLowerRight_Corner].fX, B, W);
|
||||
|
||||
this->lineTo(L + rrect.fRadii[SkRRect::kLowerLeft_Corner].fX, B);
|
||||
this->conicTo(L, B, L, B - rrect.fRadii[SkRRect::kLowerLeft_Corner].fY, W);
|
||||
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 {
|
||||
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);
|
||||
|
||||
this->lineTo(L + rrect.fRadii[SkRRect::kUpperLeft_Corner].fX, T);
|
||||
this->conicTo(L, T, L, T + rrect.fRadii[SkRRect::kUpperLeft_Corner].fY, W);
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
this->lineTo(rrectIter.next());
|
||||
this->conicTo(rectIter.next(), rrectIter.next(), weight);
|
||||
}
|
||||
}
|
||||
this->close();
|
||||
|
||||
SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
|
||||
}
|
||||
|
||||
SkDEBUGCODE(fPathRef->validate();)
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user