blob: f7209b9919791e2478bea220a10509f9418cccfd [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
Florin Malita45dc1f02019-07-01 13:57:43 -040015#include <algorithm>
16
17namespace skottie {
18namespace internal {
19
20sk_sp<sksg::RenderNode> AnimationBuilder::attachNestedAnimation(const char* name,
21 AnimatorScope* ascope) const {
22 class SkottieSGAdapter final : public sksg::RenderNode {
23 public:
24 explicit SkottieSGAdapter(sk_sp<Animation> animation)
25 : fAnimation(std::move(animation)) {
26 SkASSERT(fAnimation);
27 }
28
29 protected:
30 SkRect onRevalidate(sksg::InvalidationController*, const SkMatrix&) override {
31 return SkRect::MakeSize(fAnimation->size());
32 }
33
34 const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; }
35
36 void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
37 const auto local_scope =
38 ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(),
39 canvas->getTotalMatrix(),
40 true);
41 fAnimation->render(canvas);
42 }
43
44 private:
45 const sk_sp<Animation> fAnimation;
46 };
47
48 class SkottieAnimatorAdapter final : public sksg::Animator {
49 public:
50 SkottieAnimatorAdapter(sk_sp<Animation> animation, float time_scale)
51 : fAnimation(std::move(animation))
52 , fTimeScale(time_scale) {
53 SkASSERT(fAnimation);
54 }
55
56 protected:
57 void onTick(float t) {
58 // TODO: we prolly need more sophisticated timeline mapping for nested animations.
59 fAnimation->seek(t * fTimeScale);
60 }
61
62 private:
63 const sk_sp<Animation> fAnimation;
64 const float fTimeScale;
65 };
66
67 const auto data = fResourceProvider->load("", name);
68 if (!data) {
69 this->log(Logger::Level::kError, nullptr, "Could not load: %s.", name);
70 return nullptr;
71 }
72
73 auto animation = Animation::Builder()
74 .setResourceProvider(fResourceProvider)
75 .setFontManager(fLazyFontMgr.getMaybeNull())
76 .make(static_cast<const char*>(data->data()), data->size());
77 if (!animation) {
78 this->log(Logger::Level::kError, nullptr, "Could not parse nested animation: %s.", name);
79 return nullptr;
80 }
81
Florin Malita5f240182019-07-23 17:28:53 -040082 ascope->push_back(sk_make_sp<SkottieAnimatorAdapter>(animation,
83 animation->duration() / fDuration));
Florin Malita45dc1f02019-07-01 13:57:43 -040084
85 return sk_make_sp<SkottieSGAdapter>(std::move(animation));
86}
87
88sk_sp<sksg::RenderNode> AnimationBuilder::attachAssetRef(
89 const skjson::ObjectValue& jlayer, AnimatorScope* ascope,
90 const std::function<sk_sp<sksg::RenderNode>(const skjson::ObjectValue&,
91 AnimatorScope*)>& func) const {
92
93 const auto refId = ParseDefault<SkString>(jlayer["refId"], SkString());
94 if (refId.isEmpty()) {
95 this->log(Logger::Level::kError, nullptr, "Layer missing refId.");
96 return nullptr;
97 }
98
99 if (refId.startsWith("$")) {
100 return this->attachNestedAnimation(refId.c_str() + 1, ascope);
101 }
102
103 const auto* asset_info = fAssets.find(refId);
104 if (!asset_info) {
105 this->log(Logger::Level::kError, nullptr, "Asset not found: '%s'.", refId.c_str());
106 return nullptr;
107 }
108
109 if (asset_info->fIsAttaching) {
110 this->log(Logger::Level::kError, nullptr,
111 "Asset cycle detected for: '%s'", refId.c_str());
112 return nullptr;
113 }
114
115 asset_info->fIsAttaching = true;
116 auto asset = func(*asset_info->fAsset, ascope);
117 asset_info->fIsAttaching = false;
118
119 return asset;
120}
121
122sk_sp<sksg::RenderNode> AnimationBuilder::attachComposition(const skjson::ObjectValue& jcomp,
123 AnimatorScope* scope) const {
124 const skjson::ArrayValue* jlayers = jcomp["layers"];
125 if (!jlayers) return nullptr;
126
127 std::vector<sk_sp<sksg::RenderNode>> layers;
Florin Malita60c84fd2019-07-02 18:02:54 -0400128 AttachLayerContext layerCtx(*jlayers);
Florin Malita45dc1f02019-07-01 13:57:43 -0400129
Florin Malita5f1108c2019-07-03 10:09:31 -0400130 // Optional motion blur params.
131 if (const skjson::ObjectValue* jmb = jcomp["mb"]) {
132 static constexpr size_t kMaxSamplesPerFrame = 64;
133 layerCtx.fMotionBlurSamples = std::min(ParseDefault<size_t>((*jmb)["spf"], 1ul),
134 kMaxSamplesPerFrame);
135 layerCtx.fMotionBlurAngle = SkTPin(ParseDefault((*jmb)["sa"], 0.0f), 0.0f, 720.0f);
136 layerCtx.fMotionBlurPhase = SkTPin(ParseDefault((*jmb)["sp"], 0.0f), -360.0f, 360.0f);
137 }
138
Florin Malita45dc1f02019-07-01 13:57:43 -0400139 layers.reserve(jlayers->size());
140 for (const auto& l : *jlayers) {
Florin Malita60c84fd2019-07-02 18:02:54 -0400141 if (auto layer = this->attachLayer(l, scope, &layerCtx)) {
Florin Malita45dc1f02019-07-01 13:57:43 -0400142 layers.push_back(std::move(layer));
143 }
144 }
145
146 if (layers.empty()) {
147 return nullptr;
148 }
149
150 sk_sp<sksg::RenderNode> comp;
151 if (layers.size() == 1) {
152 comp = std::move(layers[0]);
153 } else {
154 // Layers are painted in bottom->top order.
155 std::reverse(layers.begin(), layers.end());
156 layers.shrink_to_fit();
157 comp = sksg::Group::Make(std::move(layers));
158 }
159
160 // Optional camera.
161 if (layerCtx.fCameraTransform) {
162 comp = sksg::TransformEffect::Make(std::move(comp), std::move(layerCtx.fCameraTransform));
163 }
164
165 return comp;
166}
167
168} // namespace internal
169} // namespace skottie