blob: c92bc34fc75feb64b30b0861c4ee5a6226369b73 [file] [log] [blame]
Florin Malita45dc1f02019-07-01 13:57:43 -04001/*
2 * Copyright 2019 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "modules/skottie/src/SkottiePriv.h"
9
10#include "include/core/SkCanvas.h"
11#include "modules/skottie/src/SkottieJson.h"
12#include "modules/sksg/include/SkSGGroup.h"
13#include "modules/sksg/include/SkSGTransform.h"
14
15#include "src/core/SkMakeUnique.h"
16
17#include <algorithm>
18
19namespace skottie {
20namespace internal {
21
22sk_sp<sksg::RenderNode> AnimationBuilder::attachNestedAnimation(const char* name,
23 AnimatorScope* ascope) const {
24 class SkottieSGAdapter final : public sksg::RenderNode {
25 public:
26 explicit SkottieSGAdapter(sk_sp<Animation> animation)
27 : fAnimation(std::move(animation)) {
28 SkASSERT(fAnimation);
29 }
30
31 protected:
32 SkRect onRevalidate(sksg::InvalidationController*, const SkMatrix&) override {
33 return SkRect::MakeSize(fAnimation->size());
34 }
35
36 const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; }
37
38 void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
39 const auto local_scope =
40 ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(),
41 canvas->getTotalMatrix(),
42 true);
43 fAnimation->render(canvas);
44 }
45
46 private:
47 const sk_sp<Animation> fAnimation;
48 };
49
50 class SkottieAnimatorAdapter final : public sksg::Animator {
51 public:
52 SkottieAnimatorAdapter(sk_sp<Animation> animation, float time_scale)
53 : fAnimation(std::move(animation))
54 , fTimeScale(time_scale) {
55 SkASSERT(fAnimation);
56 }
57
58 protected:
59 void onTick(float t) {
60 // TODO: we prolly need more sophisticated timeline mapping for nested animations.
61 fAnimation->seek(t * fTimeScale);
62 }
63
64 private:
65 const sk_sp<Animation> fAnimation;
66 const float fTimeScale;
67 };
68
69 const auto data = fResourceProvider->load("", name);
70 if (!data) {
71 this->log(Logger::Level::kError, nullptr, "Could not load: %s.", name);
72 return nullptr;
73 }
74
75 auto animation = Animation::Builder()
76 .setResourceProvider(fResourceProvider)
77 .setFontManager(fLazyFontMgr.getMaybeNull())
78 .make(static_cast<const char*>(data->data()), data->size());
79 if (!animation) {
80 this->log(Logger::Level::kError, nullptr, "Could not parse nested animation: %s.", name);
81 return nullptr;
82 }
83
84 ascope->push_back(
85 skstd::make_unique<SkottieAnimatorAdapter>(animation, animation->duration() / fDuration));
86
87 return sk_make_sp<SkottieSGAdapter>(std::move(animation));
88}
89
90sk_sp<sksg::RenderNode> AnimationBuilder::attachAssetRef(
91 const skjson::ObjectValue& jlayer, AnimatorScope* ascope,
92 const std::function<sk_sp<sksg::RenderNode>(const skjson::ObjectValue&,
93 AnimatorScope*)>& func) const {
94
95 const auto refId = ParseDefault<SkString>(jlayer["refId"], SkString());
96 if (refId.isEmpty()) {
97 this->log(Logger::Level::kError, nullptr, "Layer missing refId.");
98 return nullptr;
99 }
100
101 if (refId.startsWith("$")) {
102 return this->attachNestedAnimation(refId.c_str() + 1, ascope);
103 }
104
105 const auto* asset_info = fAssets.find(refId);
106 if (!asset_info) {
107 this->log(Logger::Level::kError, nullptr, "Asset not found: '%s'.", refId.c_str());
108 return nullptr;
109 }
110
111 if (asset_info->fIsAttaching) {
112 this->log(Logger::Level::kError, nullptr,
113 "Asset cycle detected for: '%s'", refId.c_str());
114 return nullptr;
115 }
116
117 asset_info->fIsAttaching = true;
118 auto asset = func(*asset_info->fAsset, ascope);
119 asset_info->fIsAttaching = false;
120
121 return asset;
122}
123
124sk_sp<sksg::RenderNode> AnimationBuilder::attachComposition(const skjson::ObjectValue& jcomp,
125 AnimatorScope* scope) const {
126 const skjson::ArrayValue* jlayers = jcomp["layers"];
127 if (!jlayers) return nullptr;
128
129 std::vector<sk_sp<sksg::RenderNode>> layers;
Florin Malita60c84fd2019-07-02 18:02:54 -0400130 AttachLayerContext layerCtx(*jlayers);
Florin Malita45dc1f02019-07-01 13:57:43 -0400131
132 layers.reserve(jlayers->size());
133 for (const auto& l : *jlayers) {
Florin Malita60c84fd2019-07-02 18:02:54 -0400134 if (auto layer = this->attachLayer(l, scope, &layerCtx)) {
Florin Malita45dc1f02019-07-01 13:57:43 -0400135 layers.push_back(std::move(layer));
136 }
137 }
138
139 if (layers.empty()) {
140 return nullptr;
141 }
142
143 sk_sp<sksg::RenderNode> comp;
144 if (layers.size() == 1) {
145 comp = std::move(layers[0]);
146 } else {
147 // Layers are painted in bottom->top order.
148 std::reverse(layers.begin(), layers.end());
149 layers.shrink_to_fit();
150 comp = sksg::Group::Make(std::move(layers));
151 }
152
153 // Optional camera.
154 if (layerCtx.fCameraTransform) {
155 comp = sksg::TransformEffect::Make(std::move(comp), std::move(layerCtx.fCameraTransform));
156 }
157
158 return comp;
159}
160
161} // namespace internal
162} // namespace skottie