add interp path

Add path methods to determine if a pair of paths
can be interpolated, and to interpolate them.

R=reed@google.com, robertphillips@google.com
BUG=skia:4549
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1703943003

Review URL: https://codereview.chromium.org/1703943003
This commit is contained in:
caryclark 2016-02-18 04:11:48 -08:00 committed by Commit bot
parent c5eddd7d8d
commit 8e7b19d0f0
5 changed files with 106 additions and 0 deletions

View File

@ -37,6 +37,27 @@ public:
return !(a == b);
}
/** Return true if the paths contain an equal array of verbs and weights. Paths
* with equal verb counts can be readily interpolated. If the paths contain one
* or more conics, the conics' weights must also match.
*
* @param compare The path to compare.
*
* @return true if the paths have the same verbs and weights.
*/
bool isInterpolatable(const SkPath& compare) const;
/** Interpolate between two paths with same-sized point arrays.
* The out path contains the verbs and weights of this path.
* The out points are a weighted average of this path and the ending path.
*
* @param ending The path to interpolate between.
* @param weight The weight, from 0 to 1. The output points are set to
* (this->points * weight) + ending->points * (1 - weight).
* @return true if the paths could be interpolated.
*/
bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const;
#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
/** Returns true if the caller is the only owner of the underlying path data */
bool unique() const { return fPathRef->unique(); }

View File

@ -271,6 +271,8 @@ public:
*/
uint32_t writeSize() const;
void interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const;
/**
* Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the
* same ID then they have the same verbs and points. However, two path refs may have the same

View File

@ -193,6 +193,33 @@ void SkPath::swap(SkPath& that) {
}
}
bool SkPath::isInterpolatable(const SkPath& compare) const {
int count = fPathRef->countVerbs();
if (count != compare.fPathRef->countVerbs()) {
return false;
}
if (!count) {
return true;
}
if (memcmp(fPathRef->verbsMemBegin(), compare.fPathRef->verbsMemBegin(),
count)) {
return false;
}
return !SkToBool(memcmp(fPathRef->conicWeights(), compare.fPathRef->conicWeights(),
fPathRef->countWeights() * sizeof(*fPathRef->conicWeights())));
}
bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const {
int verbCount = fPathRef->countVerbs();
if (verbCount != ending.fPathRef->countVerbs()) {
return false;
}
out->reset();
out->addPath(*this);
fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef);
return true;
}
static inline bool check_edge_against_rect(const SkPoint& p0,
const SkPoint& p1,
const SkRect& rect,

View File

@ -299,6 +299,19 @@ void SkPathRef::copy(const SkPathRef& ref,
SkDEBUGCODE(this->validate();)
}
void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const {
const SkScalar* inValues = &ending.getPoints()->fX;
SkScalar* outValues = &out->getPoints()->fX;
int count = out->countPoints() * 2;
for (int index = 0; index < count; ++index) {
outValues[index] = outValues[index] * weight + inValues[index] * (1 - weight);
}
out->fBoundsIsDirty = true;
out->fIsOval = false;
out->fIsRRect = false;
}
SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
int numVbs,
SkScalar** weights) {

View File

@ -3904,6 +3904,49 @@ public:
}
};
static void test_interp(skiatest::Reporter* reporter) {
SkPath p1, p2, out;
REPORTER_ASSERT(reporter, p1.isInterpolatable(p2));
REPORTER_ASSERT(reporter, p1.interpolate(p2, 0, &out));
REPORTER_ASSERT(reporter, p1 == out);
REPORTER_ASSERT(reporter, p1.interpolate(p2, 1, &out));
REPORTER_ASSERT(reporter, p1 == out);
p1.moveTo(0, 2);
p1.lineTo(0, 4);
REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2));
REPORTER_ASSERT(reporter, !p1.interpolate(p2, 1, &out));
p2.moveTo(6, 0);
p2.lineTo(8, 0);
REPORTER_ASSERT(reporter, p1.isInterpolatable(p2));
REPORTER_ASSERT(reporter, p1.interpolate(p2, 0, &out));
REPORTER_ASSERT(reporter, p2 == out);
REPORTER_ASSERT(reporter, p1.interpolate(p2, 1, &out));
REPORTER_ASSERT(reporter, p1 == out);
REPORTER_ASSERT(reporter, p1.interpolate(p2, 0.5f, &out));
REPORTER_ASSERT(reporter, out.getBounds() == SkRect::MakeLTRB(3, 1, 4, 2));
p1.reset();
p1.moveTo(4, 4);
p1.conicTo(5, 4, 5, 5, 1 / SkScalarSqrt(2));
p2.reset();
p2.moveTo(4, 2);
p2.conicTo(7, 2, 7, 5, 1 / SkScalarSqrt(2));
REPORTER_ASSERT(reporter, p1.isInterpolatable(p2));
REPORTER_ASSERT(reporter, p1.interpolate(p2, 0.5f, &out));
REPORTER_ASSERT(reporter, out.getBounds() == SkRect::MakeLTRB(4, 3, 6, 5));
p2.reset();
p2.moveTo(4, 2);
p2.conicTo(6, 3, 6, 5, 1);
REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2));
p2.reset();
p2.moveTo(4, 4);
p2.conicTo(5, 4, 5, 5, 0.5f);
REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2));
}
DEF_TEST(PathInterp, reporter) {
test_interp(reporter);
}
DEF_TEST(PathContains, reporter) {
test_contains(reporter);
}