Generalize KeyframeAnimatorBuilder to AnimatorBuilder

Change-Id: I116489a1261a93d9b2513ca53f04e1305e6fd85b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/432957
Reviewed-by: Florin Malita <fmalita@chromium.org>
Reviewed-by: Avinash Parchuri <aparchur@google.com>
Commit-Queue: Avinash Parchuri <aparchur@google.com>
This commit is contained in:
Kyle Carlstrom 2021-07-26 10:12:51 -07:00 committed by SkCQ
parent 6bf9189ae5
commit 6bbd7df9fb
10 changed files with 208 additions and 208 deletions

View File

@ -49,7 +49,7 @@ void AnimatablePropertyContainer::shrink_to_fit() {
bool AnimatablePropertyContainer::bindImpl(const AnimationBuilder& abuilder,
const skjson::ObjectValue* jprop,
KeyframeAnimatorBuilder& builder) {
AnimatorBuilder& builder) {
if (!jprop) {
return false;
}
@ -80,7 +80,7 @@ bool AnimatablePropertyContainer::bindImpl(const AnimationBuilder& abuilder,
sk_sp<KeyframeAnimator> animator;
const skjson::ArrayValue* jkfs = jpropK;
if (jkfs && jkfs->size() > 0) {
animator = builder.make(abuilder, *jkfs);
animator = builder.makeFromKeyframes(abuilder, *jkfs);
}
if (!animator) {

View File

@ -24,7 +24,7 @@ namespace skottie {
namespace internal {
class AnimationBuilder;
class KeyframeAnimatorBuilder;
class AnimatorBuilder;
class Animator : public SkRefCnt {
public:
@ -72,7 +72,7 @@ protected:
private:
StateChanged onSeek(float) final;
bool bindImpl(const AnimationBuilder&, const skjson::ObjectValue*, KeyframeAnimatorBuilder&);
bool bindImpl(const AnimationBuilder&, const skjson::ObjectValue*, AnimatorBuilder&);
std::vector<sk_sp<Animator>> fAnimators;
bool fHasSynced = false;

View File

@ -86,9 +86,9 @@ float KeyframeAnimator::compute_weight(const KFSegment &seg, float t) const {
return w;
}
KeyframeAnimatorBuilder::~KeyframeAnimatorBuilder() = default;
AnimatorBuilder::~AnimatorBuilder() = default;
bool KeyframeAnimatorBuilder::parseKeyframes(const AnimationBuilder& abuilder,
bool AnimatorBuilder::parseKeyframes(const AnimationBuilder& abuilder,
const skjson::ArrayValue& jkfs) {
// Keyframe format:
//
@ -196,7 +196,7 @@ bool KeyframeAnimatorBuilder::parseKeyframes(const AnimationBuilder& abuilder,
return true;
}
uint32_t KeyframeAnimatorBuilder::parseMapping(const skjson::ObjectValue& jkf) {
uint32_t AnimatorBuilder::parseMapping(const skjson::ObjectValue& jkf) {
if (ParseDefault(jkf["h"], false)) {
return Keyframe::kConstantMapping;
}

View File

@ -104,11 +104,11 @@ private:
mutable KFSegment fCurrentSegment = { nullptr, nullptr }; // Cached segment.
};
class KeyframeAnimatorBuilder : public SkNoncopyable {
class AnimatorBuilder : public SkNoncopyable {
public:
virtual ~KeyframeAnimatorBuilder();
virtual ~AnimatorBuilder();
virtual sk_sp<KeyframeAnimator> make(const AnimationBuilder&, const skjson::ArrayValue&) = 0;
virtual sk_sp<KeyframeAnimator> makeFromKeyframes(const AnimationBuilder&, const skjson::ArrayValue&) = 0;
virtual bool parseValue(const AnimationBuilder&, const skjson::Value&) const = 0;

View File

@ -14,14 +14,36 @@ namespace skottie::internal {
namespace {
// Scalar specialization: stores scalar values (floats) inline in keyframes.
// Scalar specialization: stores scalar values (floats) inline in keyframes.
class ScalarKeyframeAnimator final : public KeyframeAnimator {
public:
class Builder final : public KeyframeAnimatorBuilder {
public:
explicit Builder(ScalarValue* target) : fTarget(target) {}
ScalarKeyframeAnimator(std::vector<Keyframe> kfs,
std::vector<SkCubicMap> cms,
ScalarValue* target_value)
: INHERITED(std::move(kfs), std::move(cms))
, fTarget(target_value) {}
sk_sp<KeyframeAnimator> make(const AnimationBuilder& abuilder,
private:
StateChanged onSeek(float t) override {
const auto& lerp_info = this->getLERPInfo(t);
const auto old_value = *fTarget;
*fTarget = Lerp(lerp_info.vrec0.flt, lerp_info.vrec1.flt, lerp_info.weight);
return *fTarget != old_value;
}
ScalarValue* fTarget;
using INHERITED = KeyframeAnimator;
};
class ScalarAnimatorBuilder final : public AnimatorBuilder {
public:
explicit ScalarAnimatorBuilder(ScalarValue* target) : fTarget(target) {}
sk_sp<KeyframeAnimator> makeFromKeyframes(const AnimationBuilder& abuilder,
const skjson::ArrayValue& jkfs) override {
SkASSERT(jkfs.size() > 0);
if (!this->parseKeyframes(abuilder, jkfs)) {
@ -47,34 +69,13 @@ public:
ScalarValue* fTarget;
};
private:
ScalarKeyframeAnimator(std::vector<Keyframe> kfs,
std::vector<SkCubicMap> cms,
ScalarValue* target_value)
: INHERITED(std::move(kfs), std::move(cms))
, fTarget(target_value) {}
StateChanged onSeek(float t) override {
const auto& lerp_info = this->getLERPInfo(t);
const auto old_value = *fTarget;
*fTarget = Lerp(lerp_info.vrec0.flt, lerp_info.vrec1.flt, lerp_info.weight);
return *fTarget != old_value;
}
ScalarValue* fTarget;
using INHERITED = KeyframeAnimator;
};
} // namespace
template <>
bool AnimatablePropertyContainer::bind<ScalarValue>(const AnimationBuilder& abuilder,
const skjson::ObjectValue* jprop,
ScalarValue* v) {
ScalarKeyframeAnimator::Builder builder(v);
ScalarAnimatorBuilder builder(v);
return this->bindImpl(abuilder, jprop, builder);
}

View File

@ -168,7 +168,7 @@ template <>
bool AnimatablePropertyContainer::bind<ShapeValue>(const AnimationBuilder& abuilder,
const skjson::ObjectValue* jprop,
ShapeValue* v) {
VectorKeyframeAnimatorBuilder builder(v, parse_encoding_len, parse_encoding_data);
VectorAnimatorBuilder builder(v, parse_encoding_len, parse_encoding_data);
return this->bindImpl(abuilder, jprop, builder);
}

View File

@ -13,65 +13,15 @@
namespace skottie::internal {
namespace {
class TextKeyframeAnimator final : public KeyframeAnimator {
public:
class Builder final : public KeyframeAnimatorBuilder {
public:
explicit Builder(TextValue* target) : fTarget(target) {}
sk_sp<KeyframeAnimator> make(const AnimationBuilder& abuilder,
const skjson::ArrayValue& jkfs) override {
SkASSERT(jkfs.size() > 0);
fValues.reserve(jkfs.size());
if (!this->parseKeyframes(abuilder, jkfs)) {
return nullptr;
}
fValues.shrink_to_fit();
return sk_sp<TextKeyframeAnimator>(
new TextKeyframeAnimator(std::move(fKFs),
std::move(fCMs),
std::move(fValues),
fTarget));
}
bool parseValue(const AnimationBuilder& abuilder, const skjson::Value& jv) const override {
return Parse(jv, abuilder, fTarget);
}
private:
bool parseKFValue(const AnimationBuilder& abuilder,
const skjson::ObjectValue&,
const skjson::Value& jv,
Keyframe::Value* v) override {
TextValue val;
if (!Parse(jv, abuilder, &val)) {
return false;
}
// TODO: full deduping?
if (fValues.empty() || val != fValues.back()) {
fValues.push_back(std::move(val));
}
v->idx = SkToU32(fValues.size() - 1);
return true;
}
std::vector<TextValue> fValues;
TextValue* fTarget;
};
private:
TextKeyframeAnimator(std::vector<Keyframe> kfs, std::vector<SkCubicMap> cms,
std::vector<TextValue> vs, TextValue* target_value)
: INHERITED(std::move(kfs), std::move(cms))
, fValues(std::move(vs))
, fTarget(target_value) {}
private:
StateChanged onSeek(float t) override {
const auto& lerp_info = this->getLERPInfo(t);
@ -90,13 +40,62 @@ private:
using INHERITED = KeyframeAnimator;
};
class TextAnimatorBuilder final : public AnimatorBuilder {
public:
explicit TextAnimatorBuilder(TextValue* target) : fTarget(target) {}
sk_sp<KeyframeAnimator> makeFromKeyframes(const AnimationBuilder& abuilder,
const skjson::ArrayValue& jkfs) override {
SkASSERT(jkfs.size() > 0);
fValues.reserve(jkfs.size());
if (!this->parseKeyframes(abuilder, jkfs)) {
return nullptr;
}
fValues.shrink_to_fit();
return sk_sp<TextKeyframeAnimator>(
new TextKeyframeAnimator(std::move(fKFs),
std::move(fCMs),
std::move(fValues),
fTarget));
}
bool parseValue(const AnimationBuilder& abuilder, const skjson::Value& jv) const override {
return Parse(jv, abuilder, fTarget);
}
private:
bool parseKFValue(const AnimationBuilder& abuilder,
const skjson::ObjectValue&,
const skjson::Value& jv,
Keyframe::Value* v) override {
TextValue val;
if (!Parse(jv, abuilder, &val)) {
return false;
}
// TODO: full deduping?
if (fValues.empty() || val != fValues.back()) {
fValues.push_back(std::move(val));
}
v->idx = SkToU32(fValues.size() - 1);
return true;
}
std::vector<TextValue> fValues;
TextValue* fTarget;
};
} // namespace
template <>
bool AnimatablePropertyContainer::bind<TextValue>(const AnimationBuilder& abuilder,
const skjson::ObjectValue* jprop,
TextValue* v) {
TextKeyframeAnimator::Builder builder(v);
TextAnimatorBuilder builder(v);
return this->bindImpl(abuilder, jprop, builder);
}

View File

@ -20,119 +20,12 @@ namespace {
// Spatial 2D specialization: stores SkV2s and optional contour interpolators externally.
class Vec2KeyframeAnimator final : public KeyframeAnimator {
public:
struct SpatialValue {
Vec2Value v2;
sk_sp<SkContourMeasure> cmeasure;
};
public:
class Builder final : public KeyframeAnimatorBuilder {
public:
Builder(Vec2Value* vec_target, float* rot_target)
: fVecTarget(vec_target)
, fRotTarget(rot_target) {}
sk_sp<KeyframeAnimator> make(const AnimationBuilder& abuilder,
const skjson::ArrayValue& jkfs) override {
SkASSERT(jkfs.size() > 0);
fValues.reserve(jkfs.size());
if (!this->parseKeyframes(abuilder, jkfs)) {
return nullptr;
}
fValues.shrink_to_fit();
return sk_sp<Vec2KeyframeAnimator>(
new Vec2KeyframeAnimator(std::move(fKFs),
std::move(fCMs),
std::move(fValues),
fVecTarget,
fRotTarget));
}
bool parseValue(const AnimationBuilder&, const skjson::Value& jv) const override {
return Parse(jv, fVecTarget);
}
private:
void backfill_spatial(const SpatialValue& val) {
SkASSERT(!fValues.empty());
auto& prev_val = fValues.back();
SkASSERT(!prev_val.cmeasure);
if (val.v2 == prev_val.v2) {
// spatial interpolation only make sense for noncoincident values
return;
}
// Check whether v0 and v1 have the same direction AND ||v0||>=||v1||
auto check_vecs = [](const SkV2& v0, const SkV2& v1) {
const auto v0_len2 = v0.lengthSquared(),
v1_len2 = v1.lengthSquared();
// check magnitude
if (v0_len2 < v1_len2) {
return false;
}
// v0, v1 have the same direction iff dot(v0,v1) = ||v0||*||v1||
// <=> dot(v0,v1)^2 = ||v0||^2 * ||v1||^2
const auto dot = v0.dot(v1);
return SkScalarNearlyEqual(dot * dot, v0_len2 * v1_len2);
};
if (check_vecs(val.v2 - prev_val.v2, fTo) &&
check_vecs(prev_val.v2 - val.v2, fTi)) {
// Both control points lie on the [prev_val..val] segment
// => we can power-reduce the Bezier "curve" to a straight line.
return;
}
// Finally, this looks like a legitimate spatial keyframe.
SkPathBuilder p;
p.moveTo (prev_val.v2.x , prev_val.v2.y);
p.cubicTo(prev_val.v2.x + fTo.x, prev_val.v2.y + fTo.y,
val.v2.x + fTi.x, val.v2.y + fTi.y,
val.v2.x, val.v2.y);
prev_val.cmeasure = SkContourMeasureIter(p.detach(), false).next();
}
bool parseKFValue(const AnimationBuilder&,
const skjson::ObjectValue& jkf,
const skjson::Value& jv,
Keyframe::Value* v) override {
SpatialValue val;
if (!Parse(jv, &val.v2)) {
return false;
}
if (fPendingSpatial) {
this->backfill_spatial(val);
}
// Track the last keyframe spatial tangents (checked on next parseValue).
fTi = ParseDefault<SkV2>(jkf["ti"], {0,0});
fTo = ParseDefault<SkV2>(jkf["to"], {0,0});
fPendingSpatial = fTi != SkV2{0,0} || fTo != SkV2{0,0};
if (fValues.empty() || val.v2 != fValues.back().v2 || fPendingSpatial) {
fValues.push_back(std::move(val));
}
v->idx = SkToU32(fValues.size() - 1);
return true;
}
std::vector<SpatialValue> fValues;
Vec2Value* fVecTarget; // required
float* fRotTarget; // optional
SkV2 fTi{0,0},
fTo{0,0};
bool fPendingSpatial = false;
};
private:
Vec2KeyframeAnimator(std::vector<Keyframe> kfs, std::vector<SkCubicMap> cms,
std::vector<SpatialValue> vs, Vec2Value* vec_target, float* rot_target)
: INHERITED(std::move(kfs), std::move(cms))
@ -140,6 +33,7 @@ private:
, fVecTarget(vec_target)
, fRotTarget(rot_target) {}
private:
StateChanged update(const Vec2Value& new_vec_value, const Vec2Value& new_tan_value) {
auto changed = (new_vec_value != *fVecTarget);
*fVecTarget = new_vec_value;
@ -200,13 +94,119 @@ private:
return this->update(Lerp(v0.v2, v1.v2, lerp_info.weight), tan);
}
const std::vector<SpatialValue> fValues;
const std::vector<Vec2KeyframeAnimator::SpatialValue> fValues;
Vec2Value* fVecTarget;
float* fRotTarget;
using INHERITED = KeyframeAnimator;
};
class Vec2AnimatorBuilder final : public AnimatorBuilder {
public:
Vec2AnimatorBuilder(Vec2Value* vec_target, float* rot_target)
: fVecTarget(vec_target)
, fRotTarget(rot_target) {}
sk_sp<KeyframeAnimator> makeFromKeyframes(const AnimationBuilder& abuilder,
const skjson::ArrayValue& jkfs) override {
SkASSERT(jkfs.size() > 0);
fValues.reserve(jkfs.size());
if (!this->parseKeyframes(abuilder, jkfs)) {
return nullptr;
}
fValues.shrink_to_fit();
return sk_sp<Vec2KeyframeAnimator>(
new Vec2KeyframeAnimator(std::move(fKFs),
std::move(fCMs),
std::move(fValues),
fVecTarget,
fRotTarget));
}
bool parseValue(const AnimationBuilder&, const skjson::Value& jv) const override {
return Parse(jv, fVecTarget);
}
private:
void backfill_spatial(const Vec2KeyframeAnimator::SpatialValue& val) {
SkASSERT(!fValues.empty());
auto& prev_val = fValues.back();
SkASSERT(!prev_val.cmeasure);
if (val.v2 == prev_val.v2) {
// spatial interpolation only make sense for noncoincident values
return;
}
// Check whether v0 and v1 have the same direction AND ||v0||>=||v1||
auto check_vecs = [](const SkV2& v0, const SkV2& v1) {
const auto v0_len2 = v0.lengthSquared(),
v1_len2 = v1.lengthSquared();
// check magnitude
if (v0_len2 < v1_len2) {
return false;
}
// v0, v1 have the same direction iff dot(v0,v1) = ||v0||*||v1||
// <=> dot(v0,v1)^2 = ||v0||^2 * ||v1||^2
const auto dot = v0.dot(v1);
return SkScalarNearlyEqual(dot * dot, v0_len2 * v1_len2);
};
if (check_vecs(val.v2 - prev_val.v2, fTo) &&
check_vecs(prev_val.v2 - val.v2, fTi)) {
// Both control points lie on the [prev_val..val] segment
// => we can power-reduce the Bezier "curve" to a straight line.
return;
}
// Finally, this looks like a legitimate spatial keyframe.
SkPathBuilder p;
p.moveTo (prev_val.v2.x , prev_val.v2.y);
p.cubicTo(prev_val.v2.x + fTo.x, prev_val.v2.y + fTo.y,
val.v2.x + fTi.x, val.v2.y + fTi.y,
val.v2.x, val.v2.y);
prev_val.cmeasure = SkContourMeasureIter(p.detach(), false).next();
}
bool parseKFValue(const AnimationBuilder&,
const skjson::ObjectValue& jkf,
const skjson::Value& jv,
Keyframe::Value* v) override {
Vec2KeyframeAnimator::SpatialValue val;
if (!Parse(jv, &val.v2)) {
return false;
}
if (fPendingSpatial) {
this->backfill_spatial(val);
}
// Track the last keyframe spatial tangents (checked on next parseValue).
fTi = ParseDefault<SkV2>(jkf["ti"], {0,0});
fTo = ParseDefault<SkV2>(jkf["to"], {0,0});
fPendingSpatial = fTi != SkV2{0,0} || fTo != SkV2{0,0};
if (fValues.empty() || val.v2 != fValues.back().v2 || fPendingSpatial) {
fValues.push_back(std::move(val));
}
v->idx = SkToU32(fValues.size() - 1);
return true;
}
std::vector<Vec2KeyframeAnimator::SpatialValue> fValues;
Vec2Value* fVecTarget; // required
float* fRotTarget; // optional
SkV2 fTi{0,0},
fTo{0,0};
bool fPendingSpatial = false;
};
} // namespace
bool AnimatablePropertyContainer::bindAutoOrientable(const AnimationBuilder& abuilder,
@ -218,7 +218,7 @@ bool AnimatablePropertyContainer::bindAutoOrientable(const AnimationBuilder& abu
if (!ParseDefault<bool>((*jprop)["s"], false)) {
// Regular (static or keyframed) 2D value.
Vec2KeyframeAnimator::Builder builder(v, orientation);
Vec2AnimatorBuilder builder(v, orientation);
return this->bindImpl(abuilder, jprop, builder);
}

View File

@ -142,14 +142,14 @@ private:
} // namespace
VectorKeyframeAnimatorBuilder::VectorKeyframeAnimatorBuilder(std::vector<float>* target,
VectorAnimatorBuilder::VectorAnimatorBuilder(std::vector<float>* target,
VectorLenParser parse_len,
VectorDataParser parse_data)
: fParseLen(parse_len)
, fParseData(parse_data)
, fTarget(target) {}
sk_sp<KeyframeAnimator> VectorKeyframeAnimatorBuilder::make(const AnimationBuilder& abuilder,
sk_sp<KeyframeAnimator> VectorAnimatorBuilder::makeFromKeyframes(const AnimationBuilder& abuilder,
const skjson::ArrayValue& jkfs) {
SkASSERT(jkfs.size() > 0);
@ -186,7 +186,7 @@ sk_sp<KeyframeAnimator> VectorKeyframeAnimatorBuilder::make(const AnimationBuild
fTarget));
}
bool VectorKeyframeAnimatorBuilder::parseValue(const AnimationBuilder&,
bool VectorAnimatorBuilder::parseValue(const AnimationBuilder&,
const skjson::Value& jv) const {
size_t vec_len;
if (!this->fParseLen(jv, &vec_len)) {
@ -197,7 +197,7 @@ bool VectorKeyframeAnimatorBuilder::parseValue(const AnimationBuilder&,
return fParseData(jv, vec_len, fTarget->data());
}
bool VectorKeyframeAnimatorBuilder::parseKFValue(const AnimationBuilder&,
bool VectorAnimatorBuilder::parseKFValue(const AnimationBuilder&,
const skjson::ObjectValue&,
const skjson::Value& jv,
Keyframe::Value* kfv) {
@ -236,7 +236,7 @@ bool AnimatablePropertyContainer::bind<VectorValue>(const AnimationBuilder& abui
if (!ParseDefault<bool>((*jprop)["s"], false)) {
// Regular (static or keyframed) vector value.
VectorKeyframeAnimatorBuilder builder(
VectorAnimatorBuilder builder(
v,
// Len parser.
[](const skjson::Value& jv, size_t* len) -> bool {

View File

@ -14,14 +14,14 @@
namespace skottie::internal {
class VectorKeyframeAnimatorBuilder final : public KeyframeAnimatorBuilder {
class VectorAnimatorBuilder final : public AnimatorBuilder {
public:
using VectorLenParser = bool(*)(const skjson::Value&, size_t*);
using VectorDataParser = bool(*)(const skjson::Value&, size_t, float*);
VectorKeyframeAnimatorBuilder(std::vector<float>*, VectorLenParser, VectorDataParser);
VectorAnimatorBuilder(std::vector<float>*, VectorLenParser, VectorDataParser);
sk_sp<KeyframeAnimator> make(const AnimationBuilder&, const skjson::ArrayValue&) override;
sk_sp<KeyframeAnimator> makeFromKeyframes(const AnimationBuilder&, const skjson::ArrayValue&) override;
private:
bool parseValue(const AnimationBuilder&, const skjson::Value&) const override;