aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental
diff options
context:
space:
mode:
authorGravatar Florin Malita <fmalita@chromium.org>2018-01-09 11:56:09 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-01-09 17:31:27 +0000
commitf959092453eaf0ef8fdc6dff6724b0cdbbe44aaa (patch)
tree3657d50844fcbe00806325a30e13f644052cd087 /experimental
parenta766ca9af12e1175cfb01f4b516802da9197ba78 (diff)
[skotty] Animator scrubbing
Less boilerplate, less template bloat. TBR= Change-Id: Ie0ef4808f4bcd8af9b6cdf89732d214311bc6101 Reviewed-on: https://skia-review.googlesource.com/92701 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Florin Malita <fmalita@chromium.org>
Diffstat (limited to 'experimental')
-rw-r--r--experimental/skotty/Skotty.cpp151
-rw-r--r--experimental/skotty/SkottyAnimator.h114
2 files changed, 141 insertions, 124 deletions
diff --git a/experimental/skotty/Skotty.cpp b/experimental/skotty/Skotty.cpp
index 396e04969c..9d11812765 100644
--- a/experimental/skotty/Skotty.cpp
+++ b/experimental/skotty/Skotty.cpp
@@ -59,9 +59,9 @@ bool LogFail(const Json::Value& json, const char* msg) {
// This is the workhorse for binding properties: depending on whether the property is animated,
// it will either apply immediately or instantiate and attach a keyframe animator.
-template <typename ValueT, typename AttrT, typename NodeT>
-bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp<NodeT>& node,
- typename Animator<ValueT, AttrT, NodeT>::ApplyFuncT&& apply) {
+template <typename ValT, typename NodeT>
+bool BindProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp<NodeT>& node,
+ typename Animator<ValT, NodeT>::ApplyFuncT&& apply) {
if (!jprop.isObject())
return false;
@@ -71,10 +71,10 @@ bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp<No
// Older Json versions don't have an "a" animation marker.
// For those, we attempt to parse both ways.
if (jpropA.isNull() || !ParseBool(jpropA, "false")) {
- ValueT val;
- if (ValueTraits<ValueT>::Parse(jpropK, &val)) {
+ ValT val;
+ if (ValueTraits<ValT>::Parse(jpropK, &val)) {
// Static property.
- apply(node, ValueTraits<ValueT>::template As<AttrT>(val));
+ apply(node.get(), val);
return true;
}
@@ -84,8 +84,8 @@ bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp<No
}
// Keyframe property.
- using AnimatorT = Animator<ValueT, AttrT, NodeT>;
- auto animator = AnimatorT::Make(jpropK, node, std::move(apply));
+ using AnimatorT = Animator<ValT, NodeT>;
+ auto animator = AnimatorT::Make(ParseFrames<ValT>(jpropK), node, std::move(apply));
if (!animator) {
return LogFail(jprop, "Could not parse keyframed property");
@@ -103,28 +103,28 @@ sk_sp<sksg::Matrix> AttachMatrix(const Json::Value& t, AttachContext* ctx,
auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix));
auto composite = sk_make_sp<CompositeTransform>(matrix);
- auto anchor_attached = AttachProperty<VectorValue, SkPoint>(t["a"], ctx, composite,
- [](const sk_sp<CompositeTransform>& node, const SkPoint& a) {
- node->setAnchorPoint(a);
+ auto anchor_attached = BindProperty<VectorValue>(t["a"], ctx, composite,
+ [](CompositeTransform* node, const VectorValue& a) {
+ node->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
});
- auto position_attached = AttachProperty<VectorValue, SkPoint>(t["p"], ctx, composite,
- [](const sk_sp<CompositeTransform>& node, const SkPoint& p) {
- node->setPosition(p);
+ auto position_attached = BindProperty<VectorValue>(t["p"], ctx, composite,
+ [](CompositeTransform* node, const VectorValue& p) {
+ node->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
});
- auto scale_attached = AttachProperty<VectorValue, SkVector>(t["s"], ctx, composite,
- [](const sk_sp<CompositeTransform>& node, const SkVector& s) {
- node->setScale(s);
+ auto scale_attached = BindProperty<VectorValue>(t["s"], ctx, composite,
+ [](CompositeTransform* node, const VectorValue& s) {
+ node->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
});
- auto rotation_attached = AttachProperty<ScalarValue, SkScalar>(t["r"], ctx, composite,
- [](const sk_sp<CompositeTransform>& node, const SkScalar& r) {
+ auto rotation_attached = BindProperty<ScalarValue>(t["r"], ctx, composite,
+ [](CompositeTransform* node, const ScalarValue& r) {
node->setRotation(r);
});
- auto skew_attached = AttachProperty<ScalarValue, SkScalar>(t["sk"], ctx, composite,
- [](const sk_sp<CompositeTransform>& node, const SkScalar& sk) {
+ auto skew_attached = BindProperty<ScalarValue>(t["sk"], ctx, composite,
+ [](CompositeTransform* node, const ScalarValue& sk) {
node->setSkew(sk);
});
- auto skewaxis_attached = AttachProperty<ScalarValue, SkScalar>(t["sa"], ctx, composite,
- [](const sk_sp<CompositeTransform>& node, const SkScalar& sa) {
+ auto skewaxis_attached = BindProperty<ScalarValue>(t["sa"], ctx, composite,
+ [](CompositeTransform* node, const ScalarValue& sa) {
node->setSkewAxis(sa);
});
@@ -157,8 +157,8 @@ sk_sp<sksg::RenderNode> AttachOpacity(const Json::Value& jtransform, AttachConte
}
auto opacityNode = sksg::OpacityEffect::Make(childNode);
- AttachProperty<ScalarValue, SkScalar>(opacity, ctx, opacityNode,
- [](const sk_sp<sksg::OpacityEffect>& node, const SkScalar& o) {
+ BindProperty<ScalarValue>(opacity, ctx, opacityNode,
+ [](sksg::OpacityEffect* node, const ScalarValue& o) {
// BM opacity is [0..100]
node->setOpacity(o * 0.01f);
});
@@ -179,8 +179,8 @@ sk_sp<sksg::GeometryNode> AttachPathGeometry(const Json::Value& jpath, AttachCon
SkASSERT(jpath.isObject());
auto path_node = sksg::Path::Make();
- auto path_attached = AttachProperty<ShapeValue, SkPath>(jpath["ks"], ctx, path_node,
- [](const sk_sp<sksg::Path>& node, const SkPath& p) { node->setPath(p); });
+ auto path_attached = BindProperty<ShapeValue>(jpath["ks"], ctx, path_node,
+ [](sksg::Path* node, const ShapeValue& p) { node->setPath(p); });
if (path_attached)
LOG("** Attached path geometry - verbs: %d\n", path_node->getPath().countVerbs());
@@ -194,14 +194,18 @@ sk_sp<sksg::GeometryNode> AttachRRectGeometry(const Json::Value& jrect, AttachCo
auto rect_node = sksg::RRect::Make();
auto composite = sk_make_sp<CompositeRRect>(rect_node);
- auto p_attached = AttachProperty<VectorValue, SkPoint>(jrect["p"], ctx, composite,
- [](const sk_sp<CompositeRRect>& node, const SkPoint& pos) { node->setPosition(pos); });
- auto s_attached = AttachProperty<VectorValue, SkSize>(jrect["s"], ctx, composite,
- [](const sk_sp<CompositeRRect>& node, const SkSize& sz) { node->setSize(sz); });
- auto r_attached = AttachProperty<ScalarValue, SkScalar>(jrect["r"], ctx, composite,
- [](const sk_sp<CompositeRRect>& node, const SkScalar& radius) {
- node->setRadius(SkSize::Make(radius, radius));
- });
+ auto p_attached = BindProperty<VectorValue>(jrect["p"], ctx, composite,
+ [](CompositeRRect* node, const VectorValue& p) {
+ node->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
+ });
+ auto s_attached = BindProperty<VectorValue>(jrect["s"], ctx, composite,
+ [](CompositeRRect* node, const VectorValue& s) {
+ node->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
+ });
+ auto r_attached = BindProperty<ScalarValue>(jrect["r"], ctx, composite,
+ [](CompositeRRect* node, const ScalarValue& r) {
+ node->setRadius(SkSize::Make(r, r));
+ });
if (!p_attached && !s_attached && !r_attached) {
return nullptr;
@@ -218,13 +222,16 @@ sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const Json::Value& jellipse, Att
auto rect_node = sksg::RRect::Make();
auto composite = sk_make_sp<CompositeRRect>(rect_node);
- auto p_attached = AttachProperty<VectorValue, SkPoint>(jellipse["p"], ctx, composite,
- [](const sk_sp<CompositeRRect>& node, const SkPoint& pos) { node->setPosition(pos); });
- auto s_attached = AttachProperty<VectorValue, SkSize>(jellipse["s"], ctx, composite,
- [](const sk_sp<CompositeRRect>& node, const SkSize& sz) {
- node->setSize(sz);
- node->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
- });
+ auto p_attached = BindProperty<VectorValue>(jellipse["p"], ctx, composite,
+ [](CompositeRRect* node, const VectorValue& p) {
+ node->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
+ });
+ auto s_attached = BindProperty<VectorValue>(jellipse["s"], ctx, composite,
+ [](CompositeRRect* node, const VectorValue& s) {
+ const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
+ node->setSize(sz);
+ node->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
+ });
if (!p_attached && !s_attached) {
return nullptr;
@@ -252,26 +259,34 @@ sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const Json::Value& jstar, Attac
auto path_node = sksg::Path::Make();
auto composite = sk_make_sp<CompositePolyStar>(path_node, gTypes[type]);
- AttachProperty<VectorValue, SkPoint>(jstar["p"], ctx, composite,
- [](const sk_sp<CompositePolyStar>& node, const SkPoint& p) { node->setPosition(p); });
- AttachProperty<ScalarValue, SkScalar>(jstar["pt"], ctx, composite,
- [](const sk_sp<CompositePolyStar>& node, const SkScalar& pt) { node->setPointCount(pt); });
- AttachProperty<ScalarValue, SkScalar>(jstar["ir"], ctx, composite,
- [](const sk_sp<CompositePolyStar>& node, const SkScalar& ir) { node->setInnerRadius(ir); });
- AttachProperty<ScalarValue, SkScalar>(jstar["or"], ctx, composite,
- [](const sk_sp<CompositePolyStar>& node, const SkScalar& otr) {
+ BindProperty<VectorValue>(jstar["p"], ctx, composite,
+ [](CompositePolyStar* node, const VectorValue& p) {
+ node->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
+ });
+ BindProperty<ScalarValue>(jstar["pt"], ctx, composite,
+ [](CompositePolyStar* node, const ScalarValue& pt) {
+ node->setPointCount(pt);
+ });
+ BindProperty<ScalarValue>(jstar["ir"], ctx, composite,
+ [](CompositePolyStar* node, const ScalarValue& ir) {
+ node->setInnerRadius(ir);
+ });
+ BindProperty<ScalarValue>(jstar["or"], ctx, composite,
+ [](CompositePolyStar* node, const ScalarValue& otr) {
node->setOuterRadius(otr);
});
- AttachProperty<ScalarValue, SkScalar>(jstar["is"], ctx, composite,
- [](const sk_sp<CompositePolyStar>& node, const SkScalar& is) {
+ BindProperty<ScalarValue>(jstar["is"], ctx, composite,
+ [](CompositePolyStar* node, const ScalarValue& is) {
node->setInnerRoundness(is);
});
- AttachProperty<ScalarValue, SkScalar>(jstar["os"], ctx, composite,
- [](const sk_sp<CompositePolyStar>& node, const SkScalar& os) {
+ BindProperty<ScalarValue>(jstar["os"], ctx, composite,
+ [](CompositePolyStar* node, const ScalarValue& os) {
node->setOuterRoundness(os);
});
- AttachProperty<ScalarValue, SkScalar>(jstar["r"], ctx, composite,
- [](const sk_sp<CompositePolyStar>& node, const SkScalar& r) { node->setRotation(r); });
+ BindProperty<ScalarValue>(jstar["r"], ctx, composite,
+ [](CompositePolyStar* node, const ScalarValue& r) {
+ node->setRotation(r);
+ });
return path_node;
}
@@ -282,8 +297,10 @@ sk_sp<sksg::Color> AttachColorPaint(const Json::Value& obj, AttachContext* ctx)
auto color_node = sksg::Color::Make(SK_ColorBLACK);
color_node->setAntiAlias(true);
- auto color_attached = AttachProperty<VectorValue, SkColor>(obj["c"], ctx, color_node,
- [](const sk_sp<sksg::Color>& node, const SkColor& c) { node->setColor(c); });
+ auto color_attached = BindProperty<VectorValue>(obj["c"], ctx, color_node,
+ [](sksg::Color* node, const VectorValue& c) {
+ node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
+ });
return color_attached ? color_node : nullptr;
}
@@ -309,8 +326,10 @@ sk_sp<sksg::PaintNode> AttachStrokePaint(const Json::Value& jstroke, AttachConte
stroke_node->setStyle(SkPaint::kStroke_Style);
- auto width_attached = AttachProperty<ScalarValue, SkScalar>(jstroke["w"], ctx, stroke_node,
- [](const sk_sp<sksg::Color>& node, const SkScalar& width) { node->setStrokeWidth(width); });
+ auto width_attached = BindProperty<ScalarValue>(jstroke["w"], ctx, stroke_node,
+ [](sksg::Color* node, const ScalarValue& w) {
+ node->setStrokeWidth(w);
+ });
if (!width_attached)
return nullptr;
@@ -379,17 +398,17 @@ std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
for (const auto& i : inputs) {
const auto trim = sksg::TrimEffect::Make(i);
trimmed.push_back(trim);
- AttachProperty<ScalarValue, SkScalar>(jtrim["s"], ctx, trim,
- [](const sk_sp<sksg::TrimEffect>& node, const SkScalar& s) {
+ BindProperty<ScalarValue>(jtrim["s"], ctx, trim,
+ [](sksg::TrimEffect* node, const ScalarValue& s) {
node->setStart(s * 0.01f);
});
- AttachProperty<ScalarValue, SkScalar>(jtrim["e"], ctx, trim,
- [](const sk_sp<sksg::TrimEffect>& node, const SkScalar& e) {
+ BindProperty<ScalarValue>(jtrim["e"], ctx, trim,
+ [](sksg::TrimEffect* node, const ScalarValue& e) {
node->setEnd(e * 0.01f);
});
// TODO: "offset" doesn't currently work the same as BM - figure out what's going on.
- AttachProperty<ScalarValue, SkScalar>(jtrim["o"], ctx, trim,
- [](const sk_sp<sksg::TrimEffect>& node, const SkScalar& o) {
+ BindProperty<ScalarValue>(jtrim["o"], ctx, trim,
+ [](sksg::TrimEffect* node, const ScalarValue& o) {
node->setOffset(o * 0.01f);
});
}
diff --git a/experimental/skotty/SkottyAnimator.h b/experimental/skotty/SkottyAnimator.h
index 858cebd25c..f21704a3d8 100644
--- a/experimental/skotty/SkottyAnimator.h
+++ b/experimental/skotty/SkottyAnimator.h
@@ -12,10 +12,10 @@
#include "SkMakeUnique.h"
#include "SkottyPriv.h"
#include "SkottyProperties.h"
-#include "SkTArray.h"
#include "SkTypes.h"
#include <memory>
+#include <vector>
namespace skotty {
@@ -62,7 +62,6 @@ private:
template <typename T>
class KeyframeInterval final : public KeyframeIntervalBase {
public:
- // Parse the current interval AND back-fill prev interval t1.
bool parse(const Json::Value& k, KeyframeInterval* prev) {
SkASSERT(k.isObject());
@@ -77,87 +76,86 @@ public:
private:
// Start/end values.
- T fV0,
- fV1;
+ T fV0,
+ fV1;
using INHERITED = KeyframeIntervalBase;
};
-// Binds an animated/keyframed property to a node attribute.
-template <typename ValT, typename AttrT, typename NodeT>
+template <typename T>
+std::vector<KeyframeInterval<T>> ParseFrames(const Json::Value& jframes) {
+ std::vector<KeyframeInterval<T>> frames;
+
+ if (jframes.isArray()) {
+ frames.reserve(jframes.size());
+
+ KeyframeInterval<T>* prev_frame = nullptr;
+ for (const auto& jframe : jframes) {
+ if (!jframe.isObject())
+ continue;
+
+ KeyframeInterval<T> frame;
+ if (frame.parse(jframe, prev_frame)) {
+ frames.push_back(std::move(frame));
+ prev_frame = &frames.back();
+ }
+ }
+ }
+
+ // If we couldn't determine a t1 for the last frame, discard it.
+ if (!frames.empty() && !frames.back().isValid()) {
+ frames.pop_back();
+ }
+
+ return frames;
+}
+
+// Binds an animated/keyframed property to a node attribute setter.
+template <typename ValT, typename NodeT>
class Animator : public AnimatorBase {
public:
- using ApplyFuncT = void(*)(const sk_sp<NodeT>&, const AttrT&);
- static std::unique_ptr<Animator> Make(const Json::Value& frames, sk_sp<NodeT> node,
- ApplyFuncT&& applyFunc);
+ using ApplyFuncT = void(*)(NodeT*, const ValT&);
+ static std::unique_ptr<Animator> Make(std::vector<KeyframeInterval<ValT>>&& frames,
+ sk_sp<NodeT> node,
+ ApplyFuncT&& applyFunc) {
+ return (node && !frames.empty())
+ ? std::unique_ptr<Animator>(new Animator(std::move(frames),
+ std::move(node),
+ std::move(applyFunc)))
+ : nullptr;
+ }
void tick(float t) override {
- const auto& frame = this->findInterval(t);
+ const auto& frame = this->findFrame(t);
ValT val;
frame.lerp(t, &val);
- fFunc(fTarget, ValueTraits<ValT>::template As<AttrT>(val));
+ fFunc(fTarget.get(), val);
}
private:
- Animator(SkTArray<KeyframeInterval<ValT>>&& intervals, sk_sp<NodeT> node,
+ Animator(std::vector<KeyframeInterval<ValT>>&& frames, sk_sp<NodeT> node,
ApplyFuncT&& applyFunc)
- : fIntervals(std::move(intervals))
+ : fFrames(std::move(frames))
, fTarget(std::move(node))
, fFunc(std::move(applyFunc)) {}
- const KeyframeInterval<ValT>& findInterval(float t) const;
+ const KeyframeInterval<ValT>& findFrame(float t) const;
- const SkTArray<KeyframeInterval<ValT>> fIntervals;
- sk_sp<NodeT> fTarget;
- ApplyFuncT fFunc;
+ const std::vector<KeyframeInterval<ValT>> fFrames;
+ sk_sp<NodeT> fTarget;
+ ApplyFuncT fFunc;
};
-template <typename ValT, typename AttrT, typename NodeT>
-std::unique_ptr<Animator<ValT, AttrT, NodeT>>
-Animator<ValT, AttrT, NodeT>::Make(const Json::Value& frames, sk_sp<NodeT> node,
- ApplyFuncT&& applyFunc) {
-
- if (!frames.isArray())
- return nullptr;
-
- SkTArray<KeyframeInterval<ValT>> intervals;
- intervals.reserve(frames.size());
-
- KeyframeInterval<ValT>* prev_interval = nullptr;
- for (const auto& frame : frames) {
- if (!frame.isObject())
- return nullptr;
-
- KeyframeInterval<ValT> curr_interval;
- if (curr_interval.parse(frame, prev_interval)) {
- intervals.push_back(std::move(curr_interval));
- prev_interval = &intervals.back();
- }
- }
-
- // If we couldn't determine a t1 for the last interval, discard it.
- if (!intervals.empty() && !intervals.back().isValid()) {
- intervals.pop_back();
- }
-
- if (intervals.empty()) {
- return nullptr;
- }
-
- return std::unique_ptr<Animator>(
- new Animator(std::move(intervals), node, std::move(applyFunc)));
-}
-
-template <typename ValT, typename AttrT, typename NodeT>
-const KeyframeInterval<ValT>& Animator<ValT, AttrT, NodeT>::findInterval(float t) const {
- SkASSERT(!fIntervals.empty());
+template <typename ValT, typename NodeT>
+const KeyframeInterval<ValT>& Animator<ValT, NodeT>::findFrame(float t) const {
+ SkASSERT(!fFrames.empty());
// TODO: cache last/current frame?
- auto f0 = fIntervals.begin(),
- f1 = fIntervals.end() - 1;
+ auto f0 = fFrames.begin(),
+ f1 = fFrames.end() - 1;
SkASSERT(f0->isValid());
SkASSERT(f1->isValid());