• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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/utils/SkottieUtils.h"
9 
10 namespace skottie_utils {
11 
12 class CustomPropertyManager::PropertyInterceptor final : public skottie::PropertyObserver {
13 public:
PropertyInterceptor(CustomPropertyManager * mgr)14     explicit PropertyInterceptor(CustomPropertyManager* mgr) : fMgr(mgr) {}
15 
onColorProperty(const char node_name[],const LazyHandle<skottie::ColorPropertyHandle> & c)16     void onColorProperty(const char node_name[],
17                          const LazyHandle<skottie::ColorPropertyHandle>& c) override {
18         const auto key = fMgr->acceptKey(node_name, ".Color");
19         if (!key.empty()) {
20             fMgr->fColorMap[key].push_back(c());
21         }
22     }
23 
onOpacityProperty(const char node_name[],const LazyHandle<skottie::OpacityPropertyHandle> & o)24     void onOpacityProperty(const char node_name[],
25                            const LazyHandle<skottie::OpacityPropertyHandle>& o) override {
26         const auto key = fMgr->acceptKey(node_name, ".Opacity");
27         if (!key.empty()) {
28             fMgr->fOpacityMap[key].push_back(o());
29         }
30     }
31 
onTransformProperty(const char node_name[],const LazyHandle<skottie::TransformPropertyHandle> & t)32     void onTransformProperty(const char node_name[],
33                              const LazyHandle<skottie::TransformPropertyHandle>& t) override {
34         const auto key = fMgr->acceptKey(node_name, ".Transform");
35         if (!key.empty()) {
36             fMgr->fTransformMap[key].push_back(t());
37         }
38     }
39 
onTextProperty(const char node_name[],const LazyHandle<skottie::TextPropertyHandle> & t)40     void onTextProperty(const char node_name[],
41                         const LazyHandle<skottie::TextPropertyHandle>& t) override {
42         const auto key = fMgr->acceptKey(node_name, ".Text");
43         if (!key.empty()) {
44             fMgr->fTextMap[key].push_back(t());
45         }
46     }
47 
onEnterNode(const char node_name[],PropertyObserver::NodeType node_type)48     void onEnterNode(const char node_name[], PropertyObserver::NodeType node_type) override {
49         if (node_name == nullptr) {
50             return;
51         }
52         fMgr->fCurrentNode =
53                 fMgr->fCurrentNode.empty() ? node_name : fMgr->fCurrentNode + "." + node_name;
54     }
55 
onLeavingNode(const char node_name[],PropertyObserver::NodeType node_type)56     void onLeavingNode(const char node_name[], PropertyObserver::NodeType node_type) override {
57         if (node_name == nullptr) {
58             return;
59         }
60         auto length = strlen(node_name);
61         fMgr->fCurrentNode =
62                 fMgr->fCurrentNode.length() > length
63                         ? fMgr->fCurrentNode.substr(
64                                   0, fMgr->fCurrentNode.length() - strlen(node_name) - 1)
65                         : "";
66     }
67 
68 private:
69     CustomPropertyManager* fMgr;
70 };
71 
72 class CustomPropertyManager::MarkerInterceptor final : public skottie::MarkerObserver {
73 public:
MarkerInterceptor(CustomPropertyManager * mgr)74     explicit MarkerInterceptor(CustomPropertyManager* mgr) : fMgr(mgr) {}
75 
onMarker(const char name[],float t0,float t1)76     void onMarker(const char name[], float t0, float t1) override {
77         // collect all markers
78         fMgr->fMarkers.push_back({ std::string(name), t0, t1 });
79     }
80 
81 private:
82     CustomPropertyManager* fMgr;
83 };
84 
CustomPropertyManager(Mode mode,const char * prefix)85 CustomPropertyManager::CustomPropertyManager(Mode mode, const char* prefix)
86     : fMode(mode)
87     , fPrefix(prefix ? prefix : "$")
88     , fPropertyInterceptor(sk_make_sp<PropertyInterceptor>(this))
89     , fMarkerInterceptor(sk_make_sp<MarkerInterceptor>(this)) {}
90 
91 CustomPropertyManager::~CustomPropertyManager() = default;
92 
acceptKey(const char * name,const char * suffix) const93 std::string CustomPropertyManager::acceptKey(const char* name, const char* suffix) const {
94     if (!SkStrStartsWith(name, fPrefix.c_str())) {
95         return std::string();
96     }
97 
98     return fMode == Mode::kCollapseProperties
99             ? std::string(name)
100             : fCurrentNode + suffix;
101 }
102 
getPropertyObserver() const103 sk_sp<skottie::PropertyObserver> CustomPropertyManager::getPropertyObserver() const {
104     return fPropertyInterceptor;
105 }
106 
getMarkerObserver() const107 sk_sp<skottie::MarkerObserver> CustomPropertyManager::getMarkerObserver() const {
108     return fMarkerInterceptor;
109 }
110 
111 template <typename T>
112 std::vector<CustomPropertyManager::PropKey>
getProps(const PropMap<T> & container) const113 CustomPropertyManager::getProps(const PropMap<T>& container) const {
114     std::vector<PropKey> props;
115 
116     for (const auto& prop_list : container) {
117         SkASSERT(!prop_list.second.empty());
118         props.push_back(prop_list.first);
119     }
120 
121     return props;
122 }
123 
124 template <typename V, typename T>
get(const PropKey & key,const PropMap<T> & container) const125 V CustomPropertyManager::get(const PropKey& key, const PropMap<T>& container) const {
126     auto prop_group = container.find(key);
127 
128     return prop_group == container.end()
129             ? V()
130             : prop_group->second.front()->get();
131 }
132 
133 template <typename V, typename T>
set(const PropKey & key,const V & val,const PropMap<T> & container)134 bool CustomPropertyManager::set(const PropKey& key, const V& val, const PropMap<T>& container) {
135     auto prop_group = container.find(key);
136 
137     if (prop_group == container.end()) {
138         return false;
139     }
140 
141     for (auto& handle : prop_group->second) {
142         handle->set(val);
143     }
144 
145     return true;
146 }
147 
148 std::vector<CustomPropertyManager::PropKey>
getColorProps() const149 CustomPropertyManager::getColorProps() const {
150     return this->getProps(fColorMap);
151 }
152 
getColor(const PropKey & key) const153 skottie::ColorPropertyValue CustomPropertyManager::getColor(const PropKey& key) const {
154     return this->get<skottie::ColorPropertyValue>(key, fColorMap);
155 }
156 
setColor(const PropKey & key,const skottie::ColorPropertyValue & c)157 bool CustomPropertyManager::setColor(const PropKey& key, const skottie::ColorPropertyValue& c) {
158     return this->set(key, c, fColorMap);
159 }
160 
161 std::vector<CustomPropertyManager::PropKey>
getOpacityProps() const162 CustomPropertyManager::getOpacityProps() const {
163     return this->getProps(fOpacityMap);
164 }
165 
getOpacity(const PropKey & key) const166 skottie::OpacityPropertyValue CustomPropertyManager::getOpacity(const PropKey& key) const {
167     return this->get<skottie::OpacityPropertyValue>(key, fOpacityMap);
168 }
169 
setOpacity(const PropKey & key,const skottie::OpacityPropertyValue & o)170 bool CustomPropertyManager::setOpacity(const PropKey& key, const skottie::OpacityPropertyValue& o) {
171     return this->set(key, o, fOpacityMap);
172 }
173 
174 std::vector<CustomPropertyManager::PropKey>
getTransformProps() const175 CustomPropertyManager::getTransformProps() const {
176     return this->getProps(fTransformMap);
177 }
178 
getTransform(const PropKey & key) const179 skottie::TransformPropertyValue CustomPropertyManager::getTransform(const PropKey& key) const {
180     return this->get<skottie::TransformPropertyValue>(key, fTransformMap);
181 }
182 
setTransform(const PropKey & key,const skottie::TransformPropertyValue & t)183 bool CustomPropertyManager::setTransform(const PropKey& key,
184                                          const skottie::TransformPropertyValue& t) {
185     return this->set(key, t, fTransformMap);
186 }
187 
188 std::vector<CustomPropertyManager::PropKey>
getTextProps() const189 CustomPropertyManager::getTextProps() const {
190     return this->getProps(fTextMap);
191 }
192 
getText(const PropKey & key) const193 skottie::TextPropertyValue CustomPropertyManager::getText(const PropKey& key) const {
194     return this->get<skottie::TextPropertyValue>(key, fTextMap);
195 }
196 
setText(const PropKey & key,const skottie::TextPropertyValue & o)197 bool CustomPropertyManager::setText(const PropKey& key, const skottie::TextPropertyValue& o) {
198     return this->set(key, o, fTextMap);
199 }
200 
201 namespace {
202 
203 class ExternalAnimationLayer final : public skottie::ExternalLayer {
204 public:
ExternalAnimationLayer(sk_sp<skottie::Animation> anim,const SkSize & size)205     ExternalAnimationLayer(sk_sp<skottie::Animation> anim, const SkSize& size)
206         : fAnimation(std::move(anim))
207         , fSize(size) {}
208 
209 private:
render(SkCanvas * canvas,double t)210     void render(SkCanvas* canvas, double t) override {
211         fAnimation->seekFrameTime(t);
212 
213         // The main animation will layer-isolate if needed - we don't want the nested animation
214         // to override that decision.
215         const auto flags = skottie::Animation::RenderFlag::kSkipTopLevelIsolation;
216         const auto dst_rect = SkRect::MakeSize(fSize);
217         fAnimation->render(canvas, &dst_rect, flags);
218     }
219 
220     const sk_sp<skottie::Animation> fAnimation;
221     const SkSize                    fSize;
222 };
223 
224 } // namespace
225 
ExternalAnimationPrecompInterceptor(sk_sp<skresources::ResourceProvider> rprovider,const char prefixp[])226 ExternalAnimationPrecompInterceptor::ExternalAnimationPrecompInterceptor(
227         sk_sp<skresources::ResourceProvider> rprovider,
228         const char prefixp[])
229     : fResourceProvider(std::move(rprovider))
230     , fPrefix(prefixp) {}
231 
232 ExternalAnimationPrecompInterceptor::~ExternalAnimationPrecompInterceptor() = default;
233 
onLoadPrecomp(const char[],const char name[],const SkSize & size)234 sk_sp<skottie::ExternalLayer> ExternalAnimationPrecompInterceptor::onLoadPrecomp(
235         const char[], const char name[], const SkSize& size) {
236     if (0 != strncmp(name, fPrefix.c_str(), fPrefix.size())) {
237         return nullptr;
238     }
239 
240     auto data = fResourceProvider->load("", name + fPrefix.size());
241     if (!data) {
242         return nullptr;
243     }
244 
245     auto anim = skottie::Animation::Builder()
246                     .setPrecompInterceptor(sk_ref_sp(this))
247                     .setResourceProvider(fResourceProvider)
248                     .make(static_cast<const char*>(data->data()), data->size());
249 
250     return anim ? sk_make_sp<ExternalAnimationLayer>(std::move(anim), size)
251                 : nullptr;
252 }
253 
254 /**
255  * An implementation of ResourceProvider designed for Lottie template asset substitution (images,
256  * audio, etc)
257  */
258 class SlotManager::SlottableResourceProvider final : public skresources::ResourceProvider {
259 public:
SlottableResourceProvider()260     SlottableResourceProvider() {}
261 
loadImageAsset(const char[],const char slot_name[],const char[]) const262     sk_sp<skresources::ImageAsset> loadImageAsset(const char /*resource_path*/[],
263                                                   const char slot_name[],
264                                                   const char /*resource_id*/[]) const override {
265         const auto it = fImageAssetMap.find(slot_name);
266         return it == fImageAssetMap.end() ? nullptr : it->second;
267     }
268 
269 private:
270     std::unordered_map<std::string, sk_sp<skresources::ImageAsset>> fImageAssetMap;
271 
272     friend class SlotManager;
273 };
274 
275 /**
276  * An implementation of PropertyObserver designed for Lottie template property substitution (color,
277  * text, etc)
278  *
279  * PropertyObserver looks for slottable nodes then manipulates their PropertyValue on the fly
280  *
281  */
282 class SlotManager::SlottablePropertyObserver final : public skottie::PropertyObserver {
283 public:
SlottablePropertyObserver()284     SlottablePropertyObserver() {}
285 
onColorProperty(const char node_name[],const LazyHandle<skottie::ColorPropertyHandle> & c)286     void onColorProperty(const char node_name[],
287                          const LazyHandle<skottie::ColorPropertyHandle>& c) override {
288         const auto it = fColorMap.find(node_name);
289         if (it != fColorMap.end()) {
290             c()->set(it->second);
291         }
292     }
293 
onTextProperty(const char node_name[],const LazyHandle<skottie::TextPropertyHandle> & t)294     void onTextProperty(const char node_name[],
295                         const LazyHandle<skottie::TextPropertyHandle>& t) override {
296         const auto it = fTextMap.find(node_name);
297         if (it != fTextMap.end()) {
298             auto value = t()->get();
299             value.fText = it->second;
300             t()->set(value);
301         }
302     }
303 
304     // TODO(jmbetancourt): add support for other PropertyObserver callbacks
305 private:
306     using SlotID = std::string;
307 
308     std::unordered_map<SlotID, skottie::ColorPropertyValue> fColorMap;
309     std::unordered_map<SlotID, SkString>                    fTextMap;
310 
311     friend class SlotManager;
312 };
313 
SlotManager()314 SlotManager::SlotManager() {
315     fResourceProvider = sk_make_sp<SlottableResourceProvider>();
316     fPropertyObserver = sk_make_sp<SlottablePropertyObserver>();
317 }
318 
319 // TODO: consider having a generic setSlotMethod that is overloaded by PropertyValue type
setColorSlot(std::string slotID,SkColor color)320 void SlotManager::setColorSlot(std::string slotID, SkColor color) {
321     fPropertyObserver->fColorMap[slotID] = color;
322 }
323 
setTextStringSlot(std::string slotID,SkString text)324 void SlotManager::setTextStringSlot(std::string slotID, SkString text) {
325     fPropertyObserver->fTextMap[slotID] = std::move(text);
326 }
327 
setImageSlot(std::string slotID,sk_sp<skresources::ImageAsset> img)328 void SlotManager::setImageSlot(std::string slotID, sk_sp<skresources::ImageAsset> img) {
329     fResourceProvider->fImageAssetMap[slotID] = std::move(img);
330 }
331 
getResourceProvider()332 sk_sp<skresources::ResourceProvider> SlotManager::getResourceProvider() {
333     return fResourceProvider;
334 }
335 
getPropertyObserver()336 sk_sp<skottie::PropertyObserver> SlotManager::getPropertyObserver() {
337     return fPropertyObserver;
338 }
339 
340 } // namespace skottie_utils
341