aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Florin Malita <fmalita@chromium.org>2018-02-23 11:10:22 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-02-23 19:15:26 +0000
commit1022f743758b71bcc476e602679282a0acd64ff1 (patch)
tree3380edf89e0458408d7d0b63227e9116839f9b39
parent09f818d2c9c0b82ef5417a7f1d0f2a5ea09fc682 (diff)
[skottie] Nested animation support
Extend composition layers to support referencing external .json animations ("$"<PATH> syntax). This is a custom extension (not supported in BM/Lottie). Also make skottie::Animation ref-counted, to facilitate sharing. TBR= Change-Id: I062d031e5868d759f3930dea9b261f9b3ec81684 Reviewed-on: https://skia-review.googlesource.com/109806 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Florin Malita <fmalita@chromium.org>
-rw-r--r--dm/DMSrcSink.h8
-rw-r--r--experimental/skottie/Skottie.cpp93
-rw-r--r--experimental/skottie/Skottie.h11
-rw-r--r--resources/skotty/skotty_sample_nested.json96
-rw-r--r--tools/viewer/SkottieSlide.cpp6
-rw-r--r--tools/viewer/SkottieSlide.h10
6 files changed, 193 insertions, 31 deletions
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index 157a484d2b..1ad2eca79f 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -274,11 +274,11 @@ public:
private:
// Generates a kTileCount x kTileCount filmstrip with evenly distributed frames.
- static constexpr int kTileCount = 5;
+ static constexpr int kTileCount = 5;
- Name fName;
- SkISize fTileSize = SkISize::MakeEmpty();
- std::unique_ptr<skottie::Animation> fAnimation;
+ Name fName;
+ SkISize fTileSize = SkISize::MakeEmpty();
+ sk_sp<skottie::Animation> fAnimation;
};
#endif
diff --git a/experimental/skottie/Skottie.cpp b/experimental/skottie/Skottie.cpp
index aee121020e..5b0e836997 100644
--- a/experimental/skottie/Skottie.cpp
+++ b/experimental/skottie/Skottie.cpp
@@ -55,6 +55,7 @@ using AssetMap = SkTHashMap<SkString, const Json::Value*>;
struct AttachContext {
const ResourceProvider& fResources;
const AssetMap& fAssets;
+ const float fFrameRate;
sksg::AnimatorList& fAnimators;
};
@@ -676,6 +677,66 @@ sk_sp<sksg::RenderNode> AttachShape(const Json::Value& jshape, AttachShapeContex
return draws.empty() ? nullptr : shape_wrapper;
}
+sk_sp<sksg::RenderNode> AttachNestedAnimation(const char* path, AttachContext* ctx) {
+ class SkottieSGAdapter final : public sksg::RenderNode {
+ public:
+ explicit SkottieSGAdapter(sk_sp<Animation> animation)
+ : fAnimation(std::move(animation)) {
+ SkASSERT(fAnimation);
+ }
+
+ protected:
+ SkRect onRevalidate(sksg::InvalidationController*, const SkMatrix&) override {
+ return SkRect::MakeSize(fAnimation->size());
+ }
+
+ void onRender(SkCanvas* canvas) const override {
+ fAnimation->render(canvas);
+ }
+
+ private:
+ const sk_sp<Animation> fAnimation;
+ };
+
+ class SkottieAnimatorAdapter final : public sksg::Animator {
+ public:
+ SkottieAnimatorAdapter(sk_sp<Animation> animation, float frameRate)
+ : fAnimation(std::move(animation))
+ , fFrameRate(frameRate) {
+ SkASSERT(fAnimation);
+ SkASSERT(fFrameRate > 0);
+ }
+
+ protected:
+ void onTick(float t) {
+ // map back from frame # to ms.
+ const auto t_ms = t * 1000 / fFrameRate;
+ fAnimation->animationTick(t_ms);
+ }
+
+ private:
+ const sk_sp<Animation> fAnimation;
+ const float fFrameRate;
+ };
+
+ const auto resStream = ctx->fResources.openStream(path);
+ if (!resStream || !resStream->hasLength()) {
+ LOG("!! Could not open: %s\n", path);
+ return nullptr;
+ }
+
+ auto animation = Animation::Make(resStream.get(), ctx->fResources);
+ if (!animation) {
+ LOG("!! Could not load nested animation: %s\n", path);
+ return nullptr;
+ }
+
+ ctx->fAnimators.push_back(skstd::make_unique<SkottieAnimatorAdapter>(animation,
+ ctx->fFrameRate));
+
+ return sk_make_sp<SkottieSGAdapter>(std::move(animation));
+}
+
sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& jlayer, AttachContext* ctx,
float* time_bias, float* time_scale) {
SkASSERT(jlayer.isObject());
@@ -686,12 +747,6 @@ sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& jlayer, AttachContext
return nullptr;
}
- const auto* comp = ctx->fAssets.find(refId);
- if (!comp) {
- LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
- return nullptr;
- }
-
const auto start_time = ParseDefault(jlayer["st"], 0.0f),
stretch_time = ParseDefault(jlayer["sr"], 1.0f);
@@ -701,6 +756,16 @@ sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& jlayer, AttachContext
*time_scale = 1;
}
+ if (refId.startsWith("$")) {
+ return AttachNestedAnimation(refId.c_str() + 1, ctx);
+ }
+
+ const auto* comp = ctx->fAssets.find(refId);
+ if (!comp) {
+ LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
+ return nullptr;
+ }
+
// TODO: cycle detection
return AttachComposition(**comp, ctx);
}
@@ -937,8 +1002,10 @@ sk_sp<sksg::RenderNode> AttachLayer(const Json::Value& jlayer,
}
sksg::AnimatorList layer_animators;
- AttachContext local_ctx =
- { layerCtx->fCtx->fResources, layerCtx->fCtx->fAssets, layer_animators};
+ AttachContext local_ctx = { layerCtx->fCtx->fResources,
+ layerCtx->fCtx->fAssets,
+ layerCtx->fCtx->fFrameRate,
+ layer_animators};
// Layer attachers may adjust these.
float time_bias = 0,
@@ -1070,7 +1137,7 @@ sk_sp<sksg::RenderNode> AttachComposition(const Json::Value& comp, AttachContext
} // namespace
-std::unique_ptr<Animation> Animation::Make(SkStream* stream, const ResourceProvider& res) {
+sk_sp<Animation> Animation::Make(SkStream* stream, const ResourceProvider& res) {
if (!stream->hasLength()) {
// TODO: handle explicit buffering?
LOG("!! cannot parse streaming content\n");
@@ -1099,16 +1166,16 @@ std::unique_ptr<Animation> Animation::Make(SkStream* stream, const ResourceProvi
ParseDefault(json["h"], 0.0f));
const auto fps = ParseDefault(json["fr"], -1.0f);
- if (size.isEmpty() || version.isEmpty() || fps < 0) {
+ if (size.isEmpty() || version.isEmpty() || fps <= 0) {
LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
version.c_str(), size.width(), size.height(), fps);
return nullptr;
}
- return std::unique_ptr<Animation>(new Animation(res, std::move(version), size, fps, json));
+ return sk_sp<Animation>(new Animation(res, std::move(version), size, fps, json));
}
-std::unique_ptr<Animation> Animation::MakeFromFile(const char path[], const ResourceProvider* res) {
+sk_sp<Animation> Animation::MakeFromFile(const char path[], const ResourceProvider* res) {
class DirectoryResourceProvider final : public ResourceProvider {
public:
explicit DirectoryResourceProvider(SkString dir) : fDir(std::move(dir)) {}
@@ -1152,7 +1219,7 @@ Animation::Animation(const ResourceProvider& resources,
}
sksg::AnimatorList animators;
- AttachContext ctx = { resources, assets, animators };
+ AttachContext ctx = { resources, assets, fFrameRate, animators };
auto root = AttachComposition(json, &ctx);
LOG("** Attached %d animators\n", animators.size());
diff --git a/experimental/skottie/Skottie.h b/experimental/skottie/Skottie.h
index d2486dc946..f14c4dc42b 100644
--- a/experimental/skottie/Skottie.h
+++ b/experimental/skottie/Skottie.h
@@ -34,13 +34,12 @@ public:
virtual std::unique_ptr<SkStream> openStream(const char resource[]) const = 0;
};
-class Animation : public SkNoncopyable {
+class Animation : public SkRefCnt {
public:
- static std::unique_ptr<Animation> Make(SkStream*, const ResourceProvider&);
- static std::unique_ptr<Animation> MakeFromFile(const char path[],
- const ResourceProvider* = nullptr);
+ static sk_sp<Animation> Make(SkStream*, const ResourceProvider&);
+ static sk_sp<Animation> MakeFromFile(const char path[], const ResourceProvider* = nullptr);
- ~Animation();
+ ~Animation() override;
void render(SkCanvas*, const SkRect* dst = nullptr) const;
@@ -67,7 +66,7 @@ private:
std::unique_ptr<sksg::Scene> fScene;
- typedef SkNoncopyable INHERITED;
+ typedef SkRefCnt INHERITED;
};
} // namespace skottie
diff --git a/resources/skotty/skotty_sample_nested.json b/resources/skotty/skotty_sample_nested.json
new file mode 100644
index 0000000000..8059b76ba9
--- /dev/null
+++ b/resources/skotty/skotty_sample_nested.json
@@ -0,0 +1,96 @@
+{
+ "v":"4.6.9",
+ "fr":60,
+ "ip":0,
+ "op":200,
+ "w":800,
+ "h":600,
+ "nm":"External Animation",
+ "ddd":0,
+
+
+ "layers":[
+ {
+ "ddd":0,
+ "ind":2,
+ "ty": 0,
+ "nm":"External Animation Ref",
+ "refId": "$skotty_sample_1.json",
+ "ao": 0,
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "sr": 1,
+ "bm": 0,
+ "ks": {
+ "o": { "a":0, "k":100 },
+ "r": { "a":1, "k":[
+ { "s": 0, "e": 360, "t": 0 },
+ { "t": 200 }
+ ]},
+ "p": { "a":0, "k":[ 400, 300, 0 ] },
+ "a": { "a":0, "k":[ 200, 100, 0 ] },
+ "s": { "a":1, "k":[
+ { "s": [ 50, 50, 50 ], "e": [ 200, 200, 200 ], "i": { "x":0, "y":0.5 }, "o": { "x":0.5, "y":0.8 }, "t": 0 },
+ { "s": [ 200, 200, 200 ], "e": [ 50, 50, 50 ], "i": { "x":0.5, "y":0.2 }, "o": { "x":1, "y":0.5 }, "t": 100 },
+ { "t": 200 }
+ ] }
+ }
+ },
+
+ {
+ "ddd":0,
+ "ind":1,
+ "ty":4,
+ "nm":"Custom Path 1",
+ "ao": 0,
+ "ip": 0,
+ "op": 300,
+ "st": 0,
+ "sr": 1,
+ "bm": 0,
+ "ks": {
+ "o": { "a":0, "k":100 },
+ "r": { "a":0, "k":0 },
+ "p": { "a":0, "k":[ 400, 300, 0 ] },
+ "a": { "a":0, "k":[ 0, 0, 0 ] },
+ "s": { "a":1, "k":[
+ { "s": [ 50, 50, 50 ], "e": [ 200, 200, 200 ], "i": { "x":0, "y":0.5 }, "o": { "x":0.5, "y":0.8 }, "t": 0 },
+ { "s": [ 200, 200, 200 ], "e": [ 50, 50, 50 ], "i": { "x":0.5, "y":0.2 }, "o": { "x":1, "y":0.5 }, "t": 100 },
+ { "t": 200 }
+ ] }
+ },
+
+ "shapes":[
+ {
+ "ty":"gr",
+ "it":[
+ {
+ "ty": "el",
+ "nm": "Ellipse",
+ "p" : { "a": 0, "k": [ 0, 0 ] },
+ "s" : { "a": 0, "k": [ 400, 400 ] }
+ },
+
+ {
+ "ty": "fl",
+ "nm": "Fill",
+ "o" : { "a": 0, "k": 30 },
+ "c" : { "a": 0, "k": [ 0.5, 0.5, 1 ] }
+ },
+
+ {
+ "ty":"tr",
+ "p" : { "a":0, "k":[ 0, 0 ] },
+ "a" : { "a":0, "k":[ 0, 0 ] },
+ "s" : { "a":0, "k":[ 100, 100 ] },
+ "r" : { "a":0, "k": 0 },
+ "o" : { "a":0, "k":100 },
+ "nm": "Transform"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tools/viewer/SkottieSlide.cpp b/tools/viewer/SkottieSlide.cpp
index aef28e1efc..77501d8a9f 100644
--- a/tools/viewer/SkottieSlide.cpp
+++ b/tools/viewer/SkottieSlide.cpp
@@ -17,9 +17,9 @@ SkottieSlide::SkottieSlide(const SkString& name, const SkString& path)
}
void SkottieSlide::load(SkScalar w, SkScalar h) {
- fAnimation = skottie::Animation::MakeFromFile(fPath.c_str());
- fWinSize = SkSize::Make(w, h);
- fTimeBase = 0; // force a time reset
+ fAnimation = skottie::Animation::MakeFromFile(fPath.c_str());
+ fWinSize = SkSize::Make(w, h);
+ fTimeBase = 0; // force a time reset
if (fAnimation) {
fAnimation->setShowInval(fShowAnimationInval);
diff --git a/tools/viewer/SkottieSlide.h b/tools/viewer/SkottieSlide.h
index ac26d2f3c1..b5770a0cf8 100644
--- a/tools/viewer/SkottieSlide.h
+++ b/tools/viewer/SkottieSlide.h
@@ -30,11 +30,11 @@ public:
bool onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState, uint32_t modifiers) override;
private:
- SkString fPath;
- std::unique_ptr<skottie::Animation> fAnimation;
- SkSize fWinSize = SkSize::MakeEmpty();
- SkMSec fTimeBase = 0;
- bool fShowAnimationInval = false;
+ SkString fPath;
+ sk_sp<skottie::Animation> fAnimation;
+ SkSize fWinSize = SkSize::MakeEmpty();
+ SkMSec fTimeBase = 0;
+ bool fShowAnimationInval = false;
typedef Slide INHERITED;
};