1 /*
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/SkImage.h"
11 #include "modules/skottie/src/SkottieJson.h"
12 #include "modules/sksg/include/SkSGImage.h"
13 #include "modules/sksg/include/SkSGTransform.h"
14
15 namespace skottie {
16 namespace internal {
17
18 const AnimationBuilder::ImageAssetInfo*
loadImageAsset(const skjson::ObjectValue & jimage) const19 AnimationBuilder::loadImageAsset(const skjson::ObjectValue& jimage) const {
20 const skjson::StringValue* name = jimage["p"];
21 const skjson::StringValue* path = jimage["u"];
22 const skjson::StringValue* id = jimage["id"];
23 if (!name || !path || !id) {
24 return nullptr;
25 }
26
27 const SkString res_id(id->begin());
28 if (auto* cached_info = fImageAssetCache.find(res_id)) {
29 return cached_info;
30 }
31
32 auto asset = fResourceProvider->loadImageAsset(path->begin(), name->begin(), id->begin());
33 if (!asset) {
34 this->log(Logger::Level::kError, nullptr, "Could not load image asset: %s/%s (id: '%s').",
35 path->begin(), name->begin(), id->begin());
36 return nullptr;
37 }
38
39 const auto size = SkISize::Make(ParseDefault<int>(jimage["w"], 0),
40 ParseDefault<int>(jimage["h"], 0));
41 return fImageAssetCache.set(res_id, { std::move(asset), size });
42 }
43
attachImageAsset(const skjson::ObjectValue & jimage,LayerInfo * layer_info) const44 sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectValue& jimage,
45 LayerInfo* layer_info) const {
46 const auto* asset_info = this->loadImageAsset(jimage);
47 if (!asset_info) {
48 return nullptr;
49 }
50 SkASSERT(asset_info->fAsset);
51
52 auto image = asset_info->fAsset->getFrame(0);
53 if (!image) {
54 this->log(Logger::Level::kError, nullptr, "Could not load first image asset frame.");
55 return nullptr;
56 }
57
58 auto image_node = sksg::Image::Make(image);
59 image_node->setQuality(kMedium_SkFilterQuality);
60
61 if (asset_info->fAsset->isMultiFrame()) {
62 class MultiFrameAnimator final : public sksg::Animator {
63 public:
64 MultiFrameAnimator(sk_sp<ImageAsset> asset, sk_sp<sksg::Image> image_node,
65 float time_bias, float time_scale)
66 : fAsset(std::move(asset))
67 , fImageNode(std::move(image_node))
68 , fTimeBias(time_bias)
69 , fTimeScale(time_scale) {}
70
71 void onTick(float t) override {
72 fImageNode->setImage(fAsset->getFrame((t + fTimeBias) * fTimeScale));
73 }
74
75 private:
76 sk_sp<ImageAsset> fAsset;
77 sk_sp<sksg::Image> fImageNode;
78 float fTimeBias,
79 fTimeScale;
80 };
81
82 fCurrentAnimatorScope->push_back(sk_make_sp<MultiFrameAnimator>(asset_info->fAsset,
83 image_node,
84 -layer_info->fInPoint,
85 1 / fFrameRate));
86 }
87
88 const auto asset_size = SkISize::Make(
89 asset_info->fSize.width() > 0 ? asset_info->fSize.width() : image->width(),
90 asset_info->fSize.height() > 0 ? asset_info->fSize.height() : image->height());
91
92 // Image layers are sized explicitly.
93 layer_info->fSize = asset_size;
94
95 if (asset_size == image->bounds().size()) {
96 // No resize needed.
97 return image_node;
98 }
99
100 return sksg::TransformEffect::Make(std::move(image_node),
101 SkMatrix::MakeRectToRect(SkRect::Make(image->bounds()),
102 SkRect::Make(asset_size),
103 SkMatrix::kCenter_ScaleToFit));
104 }
105
attachImageLayer(const skjson::ObjectValue & jlayer,LayerInfo * layer_info) const106 sk_sp<sksg::RenderNode> AnimationBuilder::attachImageLayer(const skjson::ObjectValue& jlayer,
107 LayerInfo* layer_info) const {
108 return this->attachAssetRef(jlayer,
109 [this, &layer_info] (const skjson::ObjectValue& jimage) {
110 return this->attachImageAsset(jimage, layer_info);
111 });
112 }
113
114 } // namespace internal
115 } // namespace skottie
116