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[])48     void onEnterNode(const char node_name[]) override {
49         fMgr->fCurrentNode =
50                 fMgr->fCurrentNode.empty() ? node_name : fMgr->fCurrentNode + "." + node_name;
51     }
52 
onLeavingNode(const char node_name[])53     void onLeavingNode(const char node_name[]) override {
54         auto length = strlen(node_name);
55         fMgr->fCurrentNode =
56                 fMgr->fCurrentNode.length() > length
57                         ? fMgr->fCurrentNode.substr(
58                                   0, fMgr->fCurrentNode.length() - strlen(node_name) - 1)
59                         : "";
60     }
61 
62 private:
63     CustomPropertyManager* fMgr;
64 };
65 
66 class CustomPropertyManager::MarkerInterceptor final : public skottie::MarkerObserver {
67 public:
MarkerInterceptor(CustomPropertyManager * mgr)68     explicit MarkerInterceptor(CustomPropertyManager* mgr) : fMgr(mgr) {}
69 
onMarker(const char name[],float t0,float t1)70     void onMarker(const char name[], float t0, float t1) override {
71         // collect all markers
72         fMgr->fMarkers.push_back({ std::string(name), t0, t1 });
73     }
74 
75 private:
76     CustomPropertyManager* fMgr;
77 };
78 
CustomPropertyManager(Mode mode,const char * prefix)79 CustomPropertyManager::CustomPropertyManager(Mode mode, const char* prefix)
80     : fMode(mode)
81     , fPrefix(prefix ? prefix : "$")
82     , fPropertyInterceptor(sk_make_sp<PropertyInterceptor>(this))
83     , fMarkerInterceptor(sk_make_sp<MarkerInterceptor>(this)) {}
84 
85 CustomPropertyManager::~CustomPropertyManager() = default;
86 
acceptKey(const char * name,const char * suffix) const87 std::string CustomPropertyManager::acceptKey(const char* name, const char* suffix) const {
88     if (!SkStrStartsWith(name, fPrefix.c_str())) {
89         return std::string();
90     }
91 
92     return fMode == Mode::kCollapseProperties
93             ? std::string(name)
94             : fCurrentNode + suffix;
95 }
96 
getPropertyObserver() const97 sk_sp<skottie::PropertyObserver> CustomPropertyManager::getPropertyObserver() const {
98     return fPropertyInterceptor;
99 }
100 
getMarkerObserver() const101 sk_sp<skottie::MarkerObserver> CustomPropertyManager::getMarkerObserver() const {
102     return fMarkerInterceptor;
103 }
104 
105 template <typename T>
106 std::vector<CustomPropertyManager::PropKey>
getProps(const PropMap<T> & container) const107 CustomPropertyManager::getProps(const PropMap<T>& container) const {
108     std::vector<PropKey> props;
109 
110     for (const auto& prop_list : container) {
111         SkASSERT(!prop_list.second.empty());
112         props.push_back(prop_list.first);
113     }
114 
115     return props;
116 }
117 
118 template <typename V, typename T>
get(const PropKey & key,const PropMap<T> & container) const119 V CustomPropertyManager::get(const PropKey& key, const PropMap<T>& container) const {
120     auto prop_group = container.find(key);
121 
122     return prop_group == container.end()
123             ? V()
124             : prop_group->second.front()->get();
125 }
126 
127 template <typename V, typename T>
set(const PropKey & key,const V & val,const PropMap<T> & container)128 bool CustomPropertyManager::set(const PropKey& key, const V& val, const PropMap<T>& container) {
129     auto prop_group = container.find(key);
130 
131     if (prop_group == container.end()) {
132         return false;
133     }
134 
135     for (auto& handle : prop_group->second) {
136         handle->set(val);
137     }
138 
139     return true;
140 }
141 
142 std::vector<CustomPropertyManager::PropKey>
getColorProps() const143 CustomPropertyManager::getColorProps() const {
144     return this->getProps(fColorMap);
145 }
146 
getColor(const PropKey & key) const147 skottie::ColorPropertyValue CustomPropertyManager::getColor(const PropKey& key) const {
148     return this->get<skottie::ColorPropertyValue>(key, fColorMap);
149 }
150 
setColor(const PropKey & key,const skottie::ColorPropertyValue & c)151 bool CustomPropertyManager::setColor(const PropKey& key, const skottie::ColorPropertyValue& c) {
152     return this->set(key, c, fColorMap);
153 }
154 
155 std::vector<CustomPropertyManager::PropKey>
getOpacityProps() const156 CustomPropertyManager::getOpacityProps() const {
157     return this->getProps(fOpacityMap);
158 }
159 
getOpacity(const PropKey & key) const160 skottie::OpacityPropertyValue CustomPropertyManager::getOpacity(const PropKey& key) const {
161     return this->get<skottie::OpacityPropertyValue>(key, fOpacityMap);
162 }
163 
setOpacity(const PropKey & key,const skottie::OpacityPropertyValue & o)164 bool CustomPropertyManager::setOpacity(const PropKey& key, const skottie::OpacityPropertyValue& o) {
165     return this->set(key, o, fOpacityMap);
166 }
167 
168 std::vector<CustomPropertyManager::PropKey>
getTransformProps() const169 CustomPropertyManager::getTransformProps() const {
170     return this->getProps(fTransformMap);
171 }
172 
getTransform(const PropKey & key) const173 skottie::TransformPropertyValue CustomPropertyManager::getTransform(const PropKey& key) const {
174     return this->get<skottie::TransformPropertyValue>(key, fTransformMap);
175 }
176 
setTransform(const PropKey & key,const skottie::TransformPropertyValue & t)177 bool CustomPropertyManager::setTransform(const PropKey& key,
178                                          const skottie::TransformPropertyValue& t) {
179     return this->set(key, t, fTransformMap);
180 }
181 
182 std::vector<CustomPropertyManager::PropKey>
getTextProps() const183 CustomPropertyManager::getTextProps() const {
184     return this->getProps(fTextMap);
185 }
186 
getText(const PropKey & key) const187 skottie::TextPropertyValue CustomPropertyManager::getText(const PropKey& key) const {
188     return this->get<skottie::TextPropertyValue>(key, fTextMap);
189 }
190 
setText(const PropKey & key,const skottie::TextPropertyValue & o)191 bool CustomPropertyManager::setText(const PropKey& key, const skottie::TextPropertyValue& o) {
192     return this->set(key, o, fTextMap);
193 }
194 
195 namespace {
196 
197 class ExternalAnimationLayer final : public skottie::ExternalLayer {
198 public:
ExternalAnimationLayer(sk_sp<skottie::Animation> anim,const SkSize & size)199     ExternalAnimationLayer(sk_sp<skottie::Animation> anim, const SkSize& size)
200         : fAnimation(std::move(anim))
201         , fSize(size) {}
202 
203 private:
render(SkCanvas * canvas,double t)204     void render(SkCanvas* canvas, double t) override {
205         fAnimation->seekFrameTime(t);
206 
207         const auto dst_rect = SkRect::MakeSize(fSize);
208         fAnimation->render(canvas, &dst_rect);
209     }
210 
211     const sk_sp<skottie::Animation> fAnimation;
212     const SkSize                    fSize;
213 };
214 
215 } // namespace
216 
ExternalAnimationPrecompInterceptor(sk_sp<skresources::ResourceProvider> rprovider,const char prefixp[])217 ExternalAnimationPrecompInterceptor::ExternalAnimationPrecompInterceptor(
218         sk_sp<skresources::ResourceProvider> rprovider,
219         const char prefixp[])
220     : fResourceProvider(std::move(rprovider))
221     , fPrefix(prefixp) {}
222 
223 ExternalAnimationPrecompInterceptor::~ExternalAnimationPrecompInterceptor() = default;
224 
onLoadPrecomp(const char[],const char name[],const SkSize & size)225 sk_sp<skottie::ExternalLayer> ExternalAnimationPrecompInterceptor::onLoadPrecomp(
226         const char[], const char name[], const SkSize& size) {
227     if (0 != strncmp(name, fPrefix.c_str(), fPrefix.size())) {
228         return nullptr;
229     }
230 
231     auto data = fResourceProvider->load("", name + fPrefix.size());
232     if (!data) {
233         return nullptr;
234     }
235 
236     auto anim = skottie::Animation::Builder()
237                     .setPrecompInterceptor(sk_ref_sp(this))
238                     .make(static_cast<const char*>(data->data()), data->size());
239 
240     return anim ? sk_make_sp<ExternalAnimationLayer>(std::move(anim), size)
241                 : nullptr;
242 }
243 
244 } // namespace skottie_utils
245