xref: /aosp_15_r20/external/skia/modules/skottie/utils/SkottieUtils.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 #include "include/core/SkData.h"
11 #include "include/core/SkRect.h"
12 #include "include/core/SkSize.h"
13 #include "include/private/base/SkAssert.h"
14 #include "modules/skottie/include/Skottie.h"
15 #include "modules/skresources/include/SkResources.h"
16 
17 #include <cstring>
18 #include <utility>
19 
20 class SkCanvas;
21 
22 namespace skottie_utils {
23 
24 class CustomPropertyManager::PropertyInterceptor final : public skottie::PropertyObserver {
25 public:
PropertyInterceptor(CustomPropertyManager * mgr)26     explicit PropertyInterceptor(CustomPropertyManager* mgr) : fMgr(mgr) {}
27 
onColorProperty(const char node_name[],const LazyHandle<skottie::ColorPropertyHandle> & c)28     void onColorProperty(const char node_name[],
29                          const LazyHandle<skottie::ColorPropertyHandle>& c) override {
30         const auto key = fMgr->acceptKey(node_name, ".Color");
31         if (!key.empty()) {
32             fMgr->fColorMap[key].push_back(c());
33         }
34     }
35 
onOpacityProperty(const char node_name[],const LazyHandle<skottie::OpacityPropertyHandle> & o)36     void onOpacityProperty(const char node_name[],
37                            const LazyHandle<skottie::OpacityPropertyHandle>& o) override {
38         const auto key = fMgr->acceptKey(node_name, ".Opacity");
39         if (!key.empty()) {
40             fMgr->fOpacityMap[key].push_back(o());
41         }
42     }
43 
onTransformProperty(const char node_name[],const LazyHandle<skottie::TransformPropertyHandle> & t)44     void onTransformProperty(const char node_name[],
45                              const LazyHandle<skottie::TransformPropertyHandle>& t) override {
46         const auto key = fMgr->acceptKey(node_name, ".Transform");
47         if (!key.empty()) {
48             fMgr->fTransformMap[key].push_back(t());
49         }
50     }
51 
onTextProperty(const char node_name[],const LazyHandle<skottie::TextPropertyHandle> & t)52     void onTextProperty(const char node_name[],
53                         const LazyHandle<skottie::TextPropertyHandle>& t) override {
54         const auto key = fMgr->acceptKey(node_name, ".Text");
55         if (!key.empty()) {
56             fMgr->fTextMap[key].push_back(t());
57         }
58     }
59 
onEnterNode(const char node_name[],PropertyObserver::NodeType node_type)60     void onEnterNode(const char node_name[], PropertyObserver::NodeType node_type) override {
61         if (node_name == nullptr) {
62             return;
63         }
64         fMgr->fCurrentNode =
65                 fMgr->fCurrentNode.empty() ? node_name : fMgr->fCurrentNode + "." + node_name;
66     }
67 
onLeavingNode(const char node_name[],PropertyObserver::NodeType node_type)68     void onLeavingNode(const char node_name[], PropertyObserver::NodeType node_type) override {
69         if (node_name == nullptr) {
70             return;
71         }
72         auto length = strlen(node_name);
73         fMgr->fCurrentNode =
74                 fMgr->fCurrentNode.length() > length
75                         ? fMgr->fCurrentNode.substr(
76                                   0, fMgr->fCurrentNode.length() - strlen(node_name) - 1)
77                         : "";
78     }
79 
80 private:
81     CustomPropertyManager* fMgr;
82 };
83 
84 class CustomPropertyManager::MarkerInterceptor final : public skottie::MarkerObserver {
85 public:
MarkerInterceptor(CustomPropertyManager * mgr)86     explicit MarkerInterceptor(CustomPropertyManager* mgr) : fMgr(mgr) {}
87 
onMarker(const char name[],float t0,float t1)88     void onMarker(const char name[], float t0, float t1) override {
89         // collect all markers
90         fMgr->fMarkers.push_back({ std::string(name), t0, t1 });
91     }
92 
93 private:
94     CustomPropertyManager* fMgr;
95 };
96 
CustomPropertyManager(Mode mode,const char * prefix)97 CustomPropertyManager::CustomPropertyManager(Mode mode, const char* prefix)
98     : fMode(mode)
99     , fPrefix(prefix ? prefix : "$")
100     , fPropertyInterceptor(sk_make_sp<PropertyInterceptor>(this))
101     , fMarkerInterceptor(sk_make_sp<MarkerInterceptor>(this)) {}
102 
103 CustomPropertyManager::~CustomPropertyManager() = default;
104 
acceptKey(const char * name,const char * suffix) const105 std::string CustomPropertyManager::acceptKey(const char* name, const char* suffix) const {
106     if (!SkStrStartsWith(name, fPrefix.c_str())) {
107         return std::string();
108     }
109 
110     return fMode == Mode::kCollapseProperties
111             ? std::string(name)
112             : fCurrentNode + suffix;
113 }
114 
getPropertyObserver() const115 sk_sp<skottie::PropertyObserver> CustomPropertyManager::getPropertyObserver() const {
116     return fPropertyInterceptor;
117 }
118 
getMarkerObserver() const119 sk_sp<skottie::MarkerObserver> CustomPropertyManager::getMarkerObserver() const {
120     return fMarkerInterceptor;
121 }
122 
123 template <typename T>
124 std::vector<CustomPropertyManager::PropKey>
getProps(const PropMap<T> & container) const125 CustomPropertyManager::getProps(const PropMap<T>& container) const {
126     std::vector<PropKey> props;
127 
128     for (const auto& prop_list : container) {
129         SkASSERT(!prop_list.second.empty());
130         props.push_back(prop_list.first);
131     }
132 
133     return props;
134 }
135 
136 template <typename V, typename T>
get(const PropKey & key,const PropMap<T> & container) const137 V CustomPropertyManager::get(const PropKey& key, const PropMap<T>& container) const {
138     auto prop_group = container.find(key);
139 
140     return prop_group == container.end()
141             ? V()
142             : prop_group->second.front()->get();
143 }
144 
145 template <typename T>
getHandle(const PropKey & key,size_t index,const PropMap<T> & container) const146 std::unique_ptr<T> CustomPropertyManager::getHandle(const PropKey& key,
147                                                     size_t index,
148                                                     const PropMap<T>& container) const {
149     auto prop_group = container.find(key);
150 
151     if (prop_group == container.end() || index >= prop_group->second.size()) {
152         return nullptr;
153     }
154 
155     return std::make_unique<T>(*prop_group->second[index]);
156 }
157 
158 template <typename V, typename T>
set(const PropKey & key,const V & val,const PropMap<T> & container)159 bool CustomPropertyManager::set(const PropKey& key, const V& val, const PropMap<T>& container) {
160     auto prop_group = container.find(key);
161 
162     if (prop_group == container.end()) {
163         return false;
164     }
165 
166     for (auto& handle : prop_group->second) {
167         handle->set(val);
168     }
169 
170     return true;
171 }
172 
173 std::vector<CustomPropertyManager::PropKey>
getColorProps() const174 CustomPropertyManager::getColorProps() const {
175     return this->getProps(fColorMap);
176 }
177 
getColor(const PropKey & key) const178 skottie::ColorPropertyValue CustomPropertyManager::getColor(const PropKey& key) const {
179     return this->get<skottie::ColorPropertyValue>(key, fColorMap);
180 }
181 
182 std::unique_ptr<skottie::ColorPropertyHandle>
getColorHandle(const PropKey & key,size_t index) const183 CustomPropertyManager::getColorHandle(const PropKey& key, size_t index) const {
184     return this->getHandle(key, index, fColorMap);
185 }
186 
setColor(const PropKey & key,const skottie::ColorPropertyValue & c)187 bool CustomPropertyManager::setColor(const PropKey& key, const skottie::ColorPropertyValue& c) {
188     return this->set(key, c, fColorMap);
189 }
190 
191 std::vector<CustomPropertyManager::PropKey>
getOpacityProps() const192 CustomPropertyManager::getOpacityProps() const {
193     return this->getProps(fOpacityMap);
194 }
195 
getOpacity(const PropKey & key) const196 skottie::OpacityPropertyValue CustomPropertyManager::getOpacity(const PropKey& key) const {
197     return this->get<skottie::OpacityPropertyValue>(key, fOpacityMap);
198 }
199 
200 std::unique_ptr<skottie::OpacityPropertyHandle>
getOpacityHandle(const PropKey & key,size_t index) const201 CustomPropertyManager::getOpacityHandle(const PropKey& key, size_t index) const {
202     return this->getHandle(key, index, fOpacityMap);
203 }
204 
setOpacity(const PropKey & key,const skottie::OpacityPropertyValue & o)205 bool CustomPropertyManager::setOpacity(const PropKey& key, const skottie::OpacityPropertyValue& o) {
206     return this->set(key, o, fOpacityMap);
207 }
208 
209 std::vector<CustomPropertyManager::PropKey>
getTransformProps() const210 CustomPropertyManager::getTransformProps() const {
211     return this->getProps(fTransformMap);
212 }
213 
getTransform(const PropKey & key) const214 skottie::TransformPropertyValue CustomPropertyManager::getTransform(const PropKey& key) const {
215     return this->get<skottie::TransformPropertyValue>(key, fTransformMap);
216 }
217 
218 std::unique_ptr<skottie::TransformPropertyHandle>
getTransformHandle(const PropKey & key,size_t index) const219 CustomPropertyManager::getTransformHandle(const PropKey& key, size_t index) const {
220     return this->getHandle(key, index, fTransformMap);
221 }
222 
setTransform(const PropKey & key,const skottie::TransformPropertyValue & t)223 bool CustomPropertyManager::setTransform(const PropKey& key,
224                                          const skottie::TransformPropertyValue& t) {
225     return this->set(key, t, fTransformMap);
226 }
227 
228 std::vector<CustomPropertyManager::PropKey>
getTextProps() const229 CustomPropertyManager::getTextProps() const {
230     return this->getProps(fTextMap);
231 }
232 
getText(const PropKey & key) const233 skottie::TextPropertyValue CustomPropertyManager::getText(const PropKey& key) const {
234     return this->get<skottie::TextPropertyValue>(key, fTextMap);
235 }
236 
237 std::unique_ptr<skottie::TextPropertyHandle>
getTextHandle(const PropKey & key,size_t index) const238 CustomPropertyManager::getTextHandle(const PropKey& key, size_t index) const {
239     return this->getHandle(key, index, fTextMap);
240 }
241 
setText(const PropKey & key,const skottie::TextPropertyValue & o)242 bool CustomPropertyManager::setText(const PropKey& key, const skottie::TextPropertyValue& o) {
243     return this->set(key, o, fTextMap);
244 }
245 
246 namespace {
247 
248 class ExternalAnimationLayer final : public skottie::ExternalLayer {
249 public:
ExternalAnimationLayer(sk_sp<skottie::Animation> anim,const SkSize & size)250     ExternalAnimationLayer(sk_sp<skottie::Animation> anim, const SkSize& size)
251         : fAnimation(std::move(anim))
252         , fSize(size) {}
253 
254 private:
render(SkCanvas * canvas,double t)255     void render(SkCanvas* canvas, double t) override {
256         fAnimation->seekFrameTime(t);
257 
258         // The main animation will layer-isolate if needed - we don't want the nested animation
259         // to override that decision.
260         const auto flags = skottie::Animation::RenderFlag::kSkipTopLevelIsolation;
261         const auto dst_rect = SkRect::MakeSize(fSize);
262         fAnimation->render(canvas, &dst_rect, flags);
263     }
264 
265     const sk_sp<skottie::Animation> fAnimation;
266     const SkSize                    fSize;
267 };
268 
269 } // namespace
270 
ExternalAnimationPrecompInterceptor(sk_sp<skresources::ResourceProvider> rprovider,const char prefixp[])271 ExternalAnimationPrecompInterceptor::ExternalAnimationPrecompInterceptor(
272         sk_sp<skresources::ResourceProvider> rprovider,
273         const char prefixp[])
274     : fResourceProvider(std::move(rprovider))
275     , fPrefix(prefixp) {}
276 
277 ExternalAnimationPrecompInterceptor::~ExternalAnimationPrecompInterceptor() = default;
278 
onLoadPrecomp(const char[],const char name[],const SkSize & size)279 sk_sp<skottie::ExternalLayer> ExternalAnimationPrecompInterceptor::onLoadPrecomp(
280         const char[], const char name[], const SkSize& size) {
281     if (0 != strncmp(name, fPrefix.c_str(), fPrefix.size())) {
282         return nullptr;
283     }
284 
285     auto data = fResourceProvider->load("", name + fPrefix.size());
286     if (!data) {
287         return nullptr;
288     }
289 
290     auto anim = skottie::Animation::Builder()
291                     .setPrecompInterceptor(sk_ref_sp(this))
292                     .setResourceProvider(fResourceProvider)
293                     .make(static_cast<const char*>(data->data()), data->size());
294 
295     return anim ? sk_make_sp<ExternalAnimationLayer>(std::move(anim), size)
296                 : nullptr;
297 }
298 
299 } // namespace skottie_utils
300