automatically inject a moveTo if we see a close followed by a line/quad/cubic

git-svn-id: http://skia.googlecode.com/svn/trunk@3027 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2012-01-12 18:17:11 +00:00
parent 51a283b4ad
commit d335d1d784
3 changed files with 56 additions and 16 deletions

View File

@ -740,6 +740,7 @@ private:
SkTDArray<SkPoint> fPts;
SkTDArray<uint8_t> fVerbs;
mutable SkRect fBounds;
int fLastMoveToIndex;
uint8_t fFillType;
uint8_t fSegmentMask;
mutable uint8_t fBoundsIsDirty;
@ -767,6 +768,14 @@ private:
*/
void reversePathTo(const SkPath&);
// called before we add points for lineTo, quadTo, cubicTo, checking to see
// if we need to inject a leading moveTo first
//
// SkPath path; path.lineTo(...); <--- need a leading moveTo(0, 0)
// SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo)
//
inline void injectMoveToIfNeeded();
friend const SkPoint* sk_get_path_points(const SkPath&, int index);
friend class SkAutoPathBoundsUpdate;
};

View File

@ -101,11 +101,15 @@ static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
////////////////////////////////////////////////////////////////////////////
// flag to require a moveTo if we begin with something else, like lineTo etc.
#define INITIAL_LASTMOVETOINDEX_VALUE ~0
SkPath::SkPath()
: fFillType(kWinding_FillType)
, fBoundsIsDirty(true) {
fConvexity = kUnknown_Convexity;
fSegmentMask = 0;
fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
#ifdef SK_BUILD_FOR_ANDROID
fGenerationID = 0;
#endif
@ -135,6 +139,7 @@ SkPath& SkPath::operator=(const SkPath& src) {
fBoundsIsDirty = src.fBoundsIsDirty;
fConvexity = src.fConvexity;
fSegmentMask = src.fSegmentMask;
fLastMoveToIndex = src.fLastMoveToIndex;
GEN_ID_INC;
}
SkDEBUGCODE(this->validate();)
@ -165,6 +170,7 @@ void SkPath::swap(SkPath& other) {
SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
SkTSwap<uint8_t>(fConvexity, other.fConvexity);
SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask);
SkTSwap<int>(fLastMoveToIndex, other.fLastMoveToIndex);
GEN_ID_INC;
}
}
@ -184,6 +190,7 @@ void SkPath::reset() {
fBoundsIsDirty = true;
fConvexity = kUnknown_Convexity;
fSegmentMask = 0;
fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
}
void SkPath::rewind() {
@ -195,6 +202,7 @@ void SkPath::rewind() {
fConvexity = kUnknown_Convexity;
fBoundsIsDirty = true;
fSegmentMask = 0;
fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
}
bool SkPath::isEmpty() const {
@ -410,6 +418,9 @@ void SkPath::moveTo(SkScalar x, SkScalar y) {
int vc = fVerbs.count();
SkPoint* pt;
// remember our index
fLastMoveToIndex = fPts.count();
#ifdef SK_OLD_EMPTY_PATH_BEHAVIOR
if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) {
pt = &fPts[fPts.count() - 1];
@ -433,13 +444,25 @@ void SkPath::rMoveTo(SkScalar x, SkScalar y) {
this->moveTo(pt.fX + x, pt.fY + y);
}
void SkPath::injectMoveToIfNeeded() {
if (fLastMoveToIndex < 0) {
SkScalar x, y;
if (fVerbs.count() == 0) {
x = y = 0;
} else {
const SkPoint& pt = fPts[~fLastMoveToIndex];
x = pt.fX;
y = pt.fY;
}
this->moveTo(x, y);
}
}
void SkPath::lineTo(SkScalar x, SkScalar y) {
SkDEBUGCODE(this->validate();)
if (fVerbs.count() == 0) {
fPts.append()->set(0, 0);
*fVerbs.append() = kMove_Verb;
}
this->injectMoveToIfNeeded();
fPts.append()->set(x, y);
*fVerbs.append() = kLine_Verb;
fSegmentMask |= kLine_SegmentMask;
@ -457,10 +480,7 @@ void SkPath::rLineTo(SkScalar x, SkScalar y) {
void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
SkDEBUGCODE(this->validate();)
if (fVerbs.count() == 0) {
fPts.append()->set(0, 0);
*fVerbs.append() = kMove_Verb;
}
this->injectMoveToIfNeeded();
SkPoint* pts = fPts.append(2);
pts[0].set(x1, y1);
@ -482,10 +502,8 @@ void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
SkScalar x3, SkScalar y3) {
SkDEBUGCODE(this->validate();)
if (fVerbs.count() == 0) {
fPts.append()->set(0, 0);
*fVerbs.append() = kMove_Verb;
}
this->injectMoveToIfNeeded();
SkPoint* pts = fPts.append(3);
pts[0].set(x1, y1);
pts[1].set(x2, y2);
@ -525,6 +543,15 @@ void SkPath::close() {
break;
}
}
// signal that we need a moveTo to follow us (unless we're done)
#if 0
if (fLastMoveToIndex >= 0) {
fLastMoveToIndex = ~fLastMoveToIndex;
}
#else
fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
#endif
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -889,12 +889,14 @@ static void test_raw_iter(skiatest::Reporter* reporter) {
// Max of 10 segments, max 3 points per segment
SkRandom rand(9876543);
SkPoint expectedPts[31]; // May have leading moveTo
SkPath::Verb expectedVerbs[11]; // May have leading moveTo
SkPath::Verb expectedVerbs[22]; // May have leading moveTo
SkPath::Verb nextVerb;
for (int i = 0; i < 500; ++i) {
p.reset();
bool lastWasClose = true;
bool haveMoveTo = false;
SkPoint lastMoveToPt = { 0, 0 };
int numPoints = 0;
int numVerbs = (rand.nextU() >> 16) % 10;
int numIterVerbs = 0;
@ -907,13 +909,14 @@ static void test_raw_iter(skiatest::Reporter* reporter) {
case SkPath::kMove_Verb:
expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
p.moveTo(expectedPts[numPoints]);
lastMoveToPt = expectedPts[numPoints];
numPoints += 1;
lastWasClose = false;
haveMoveTo = true;
break;
case SkPath::kLine_Verb:
if (!haveMoveTo) {
expectedPts[numPoints++].set(0, 0);
expectedPts[numPoints++] = lastMoveToPt;
expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
haveMoveTo = true;
}
@ -924,7 +927,7 @@ static void test_raw_iter(skiatest::Reporter* reporter) {
break;
case SkPath::kQuad_Verb:
if (!haveMoveTo) {
expectedPts[numPoints++].set(0, 0);
expectedPts[numPoints++] = lastMoveToPt;
expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
haveMoveTo = true;
}
@ -936,7 +939,7 @@ static void test_raw_iter(skiatest::Reporter* reporter) {
break;
case SkPath::kCubic_Verb:
if (!haveMoveTo) {
expectedPts[numPoints++].set(0, 0);
expectedPts[numPoints++] = lastMoveToPt;
expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
haveMoveTo = true;
}
@ -950,6 +953,7 @@ static void test_raw_iter(skiatest::Reporter* reporter) {
break;
case SkPath::kClose_Verb:
p.close();
haveMoveTo = false;
lastWasClose = true;
break;
default:;