From f959092453eaf0ef8fdc6dff6724b0cdbbe44aaa Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Tue, 9 Jan 2018 11:56:09 -0500 Subject: [skotty] Animator scrubbing Less boilerplate, less template bloat. TBR= Change-Id: Ie0ef4808f4bcd8af9b6cdf89732d214311bc6101 Reviewed-on: https://skia-review.googlesource.com/92701 Reviewed-by: Florin Malita Commit-Queue: Florin Malita --- experimental/skotty/Skotty.cpp | 151 ++++++++++++++++++++--------------- experimental/skotty/SkottyAnimator.h | 114 +++++++++++++------------- 2 files changed, 141 insertions(+), 124 deletions(-) (limited to 'experimental') 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 -bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp& node, - typename Animator::ApplyFuncT&& apply) { +template +bool BindProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp& node, + typename Animator::ApplyFuncT&& apply) { if (!jprop.isObject()) return false; @@ -71,10 +71,10 @@ bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp::Parse(jpropK, &val)) { + ValT val; + if (ValueTraits::Parse(jpropK, &val)) { // Static property. - apply(node, ValueTraits::template As(val)); + apply(node.get(), val); return true; } @@ -84,8 +84,8 @@ bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp; - auto animator = AnimatorT::Make(jpropK, node, std::move(apply)); + using AnimatorT = Animator; + auto animator = AnimatorT::Make(ParseFrames(jpropK), node, std::move(apply)); if (!animator) { return LogFail(jprop, "Could not parse keyframed property"); @@ -103,28 +103,28 @@ sk_sp AttachMatrix(const Json::Value& t, AttachContext* ctx, auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix)); auto composite = sk_make_sp(matrix); - auto anchor_attached = AttachProperty(t["a"], ctx, composite, - [](const sk_sp& node, const SkPoint& a) { - node->setAnchorPoint(a); + auto anchor_attached = BindProperty(t["a"], ctx, composite, + [](CompositeTransform* node, const VectorValue& a) { + node->setAnchorPoint(ValueTraits::As(a)); }); - auto position_attached = AttachProperty(t["p"], ctx, composite, - [](const sk_sp& node, const SkPoint& p) { - node->setPosition(p); + auto position_attached = BindProperty(t["p"], ctx, composite, + [](CompositeTransform* node, const VectorValue& p) { + node->setPosition(ValueTraits::As(p)); }); - auto scale_attached = AttachProperty(t["s"], ctx, composite, - [](const sk_sp& node, const SkVector& s) { - node->setScale(s); + auto scale_attached = BindProperty(t["s"], ctx, composite, + [](CompositeTransform* node, const VectorValue& s) { + node->setScale(ValueTraits::As(s)); }); - auto rotation_attached = AttachProperty(t["r"], ctx, composite, - [](const sk_sp& node, const SkScalar& r) { + auto rotation_attached = BindProperty(t["r"], ctx, composite, + [](CompositeTransform* node, const ScalarValue& r) { node->setRotation(r); }); - auto skew_attached = AttachProperty(t["sk"], ctx, composite, - [](const sk_sp& node, const SkScalar& sk) { + auto skew_attached = BindProperty(t["sk"], ctx, composite, + [](CompositeTransform* node, const ScalarValue& sk) { node->setSkew(sk); }); - auto skewaxis_attached = AttachProperty(t["sa"], ctx, composite, - [](const sk_sp& node, const SkScalar& sa) { + auto skewaxis_attached = BindProperty(t["sa"], ctx, composite, + [](CompositeTransform* node, const ScalarValue& sa) { node->setSkewAxis(sa); }); @@ -157,8 +157,8 @@ sk_sp AttachOpacity(const Json::Value& jtransform, AttachConte } auto opacityNode = sksg::OpacityEffect::Make(childNode); - AttachProperty(opacity, ctx, opacityNode, - [](const sk_sp& node, const SkScalar& o) { + BindProperty(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 AttachPathGeometry(const Json::Value& jpath, AttachCon SkASSERT(jpath.isObject()); auto path_node = sksg::Path::Make(); - auto path_attached = AttachProperty(jpath["ks"], ctx, path_node, - [](const sk_sp& node, const SkPath& p) { node->setPath(p); }); + auto path_attached = BindProperty(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 AttachRRectGeometry(const Json::Value& jrect, AttachCo auto rect_node = sksg::RRect::Make(); auto composite = sk_make_sp(rect_node); - auto p_attached = AttachProperty(jrect["p"], ctx, composite, - [](const sk_sp& node, const SkPoint& pos) { node->setPosition(pos); }); - auto s_attached = AttachProperty(jrect["s"], ctx, composite, - [](const sk_sp& node, const SkSize& sz) { node->setSize(sz); }); - auto r_attached = AttachProperty(jrect["r"], ctx, composite, - [](const sk_sp& node, const SkScalar& radius) { - node->setRadius(SkSize::Make(radius, radius)); - }); + auto p_attached = BindProperty(jrect["p"], ctx, composite, + [](CompositeRRect* node, const VectorValue& p) { + node->setPosition(ValueTraits::As(p)); + }); + auto s_attached = BindProperty(jrect["s"], ctx, composite, + [](CompositeRRect* node, const VectorValue& s) { + node->setSize(ValueTraits::As(s)); + }); + auto r_attached = BindProperty(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 AttachEllipseGeometry(const Json::Value& jellipse, Att auto rect_node = sksg::RRect::Make(); auto composite = sk_make_sp(rect_node); - auto p_attached = AttachProperty(jellipse["p"], ctx, composite, - [](const sk_sp& node, const SkPoint& pos) { node->setPosition(pos); }); - auto s_attached = AttachProperty(jellipse["s"], ctx, composite, - [](const sk_sp& node, const SkSize& sz) { - node->setSize(sz); - node->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2)); - }); + auto p_attached = BindProperty(jellipse["p"], ctx, composite, + [](CompositeRRect* node, const VectorValue& p) { + node->setPosition(ValueTraits::As(p)); + }); + auto s_attached = BindProperty(jellipse["s"], ctx, composite, + [](CompositeRRect* node, const VectorValue& s) { + const auto sz = ValueTraits::As(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 AttachPolystarGeometry(const Json::Value& jstar, Attac auto path_node = sksg::Path::Make(); auto composite = sk_make_sp(path_node, gTypes[type]); - AttachProperty(jstar["p"], ctx, composite, - [](const sk_sp& node, const SkPoint& p) { node->setPosition(p); }); - AttachProperty(jstar["pt"], ctx, composite, - [](const sk_sp& node, const SkScalar& pt) { node->setPointCount(pt); }); - AttachProperty(jstar["ir"], ctx, composite, - [](const sk_sp& node, const SkScalar& ir) { node->setInnerRadius(ir); }); - AttachProperty(jstar["or"], ctx, composite, - [](const sk_sp& node, const SkScalar& otr) { + BindProperty(jstar["p"], ctx, composite, + [](CompositePolyStar* node, const VectorValue& p) { + node->setPosition(ValueTraits::As(p)); + }); + BindProperty(jstar["pt"], ctx, composite, + [](CompositePolyStar* node, const ScalarValue& pt) { + node->setPointCount(pt); + }); + BindProperty(jstar["ir"], ctx, composite, + [](CompositePolyStar* node, const ScalarValue& ir) { + node->setInnerRadius(ir); + }); + BindProperty(jstar["or"], ctx, composite, + [](CompositePolyStar* node, const ScalarValue& otr) { node->setOuterRadius(otr); }); - AttachProperty(jstar["is"], ctx, composite, - [](const sk_sp& node, const SkScalar& is) { + BindProperty(jstar["is"], ctx, composite, + [](CompositePolyStar* node, const ScalarValue& is) { node->setInnerRoundness(is); }); - AttachProperty(jstar["os"], ctx, composite, - [](const sk_sp& node, const SkScalar& os) { + BindProperty(jstar["os"], ctx, composite, + [](CompositePolyStar* node, const ScalarValue& os) { node->setOuterRoundness(os); }); - AttachProperty(jstar["r"], ctx, composite, - [](const sk_sp& node, const SkScalar& r) { node->setRotation(r); }); + BindProperty(jstar["r"], ctx, composite, + [](CompositePolyStar* node, const ScalarValue& r) { + node->setRotation(r); + }); return path_node; } @@ -282,8 +297,10 @@ sk_sp AttachColorPaint(const Json::Value& obj, AttachContext* ctx) auto color_node = sksg::Color::Make(SK_ColorBLACK); color_node->setAntiAlias(true); - auto color_attached = AttachProperty(obj["c"], ctx, color_node, - [](const sk_sp& node, const SkColor& c) { node->setColor(c); }); + auto color_attached = BindProperty(obj["c"], ctx, color_node, + [](sksg::Color* node, const VectorValue& c) { + node->setColor(ValueTraits::As(c)); + }); return color_attached ? color_node : nullptr; } @@ -309,8 +326,10 @@ sk_sp AttachStrokePaint(const Json::Value& jstroke, AttachConte stroke_node->setStyle(SkPaint::kStroke_Style); - auto width_attached = AttachProperty(jstroke["w"], ctx, stroke_node, - [](const sk_sp& node, const SkScalar& width) { node->setStrokeWidth(width); }); + auto width_attached = BindProperty(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> AttachTrimGeometryEffect( for (const auto& i : inputs) { const auto trim = sksg::TrimEffect::Make(i); trimmed.push_back(trim); - AttachProperty(jtrim["s"], ctx, trim, - [](const sk_sp& node, const SkScalar& s) { + BindProperty(jtrim["s"], ctx, trim, + [](sksg::TrimEffect* node, const ScalarValue& s) { node->setStart(s * 0.01f); }); - AttachProperty(jtrim["e"], ctx, trim, - [](const sk_sp& node, const SkScalar& e) { + BindProperty(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(jtrim["o"], ctx, trim, - [](const sk_sp& node, const SkScalar& o) { + BindProperty(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 +#include namespace skotty { @@ -62,7 +62,6 @@ private: template 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 +template +std::vector> ParseFrames(const Json::Value& jframes) { + std::vector> frames; + + if (jframes.isArray()) { + frames.reserve(jframes.size()); + + KeyframeInterval* prev_frame = nullptr; + for (const auto& jframe : jframes) { + if (!jframe.isObject()) + continue; + + KeyframeInterval 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 class Animator : public AnimatorBase { public: - using ApplyFuncT = void(*)(const sk_sp&, const AttrT&); - static std::unique_ptr Make(const Json::Value& frames, sk_sp node, - ApplyFuncT&& applyFunc); + using ApplyFuncT = void(*)(NodeT*, const ValT&); + static std::unique_ptr Make(std::vector>&& frames, + sk_sp node, + ApplyFuncT&& applyFunc) { + return (node && !frames.empty()) + ? std::unique_ptr(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::template As(val)); + fFunc(fTarget.get(), val); } private: - Animator(SkTArray>&& intervals, sk_sp node, + Animator(std::vector>&& frames, sk_sp node, ApplyFuncT&& applyFunc) - : fIntervals(std::move(intervals)) + : fFrames(std::move(frames)) , fTarget(std::move(node)) , fFunc(std::move(applyFunc)) {} - const KeyframeInterval& findInterval(float t) const; + const KeyframeInterval& findFrame(float t) const; - const SkTArray> fIntervals; - sk_sp fTarget; - ApplyFuncT fFunc; + const std::vector> fFrames; + sk_sp fTarget; + ApplyFuncT fFunc; }; -template -std::unique_ptr> -Animator::Make(const Json::Value& frames, sk_sp node, - ApplyFuncT&& applyFunc) { - - if (!frames.isArray()) - return nullptr; - - SkTArray> intervals; - intervals.reserve(frames.size()); - - KeyframeInterval* prev_interval = nullptr; - for (const auto& frame : frames) { - if (!frame.isObject()) - return nullptr; - - KeyframeInterval 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( - new Animator(std::move(intervals), node, std::move(applyFunc))); -} - -template -const KeyframeInterval& Animator::findInterval(float t) const { - SkASSERT(!fIntervals.empty()); +template +const KeyframeInterval& Animator::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()); -- cgit v1.2.3