• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace  {
19 
image_matrix(const sk_sp<SkImage> & image,const SkISize & dest_size)20 SkMatrix image_matrix(const sk_sp<SkImage>& image, const SkISize& dest_size) {
21     return image ? SkMatrix::MakeRectToRect(SkRect::Make(image->bounds()),
22                                             SkRect::Make(dest_size),
23                                             SkMatrix::kCenter_ScaleToFit)
24                  : SkMatrix::I();
25 }
26 
27 class ImageAnimator final : public sksg::Animator {
28 public:
ImageAnimator(sk_sp<ImageAsset> asset,sk_sp<sksg::Image> image_node,sk_sp<sksg::Matrix<SkMatrix>> image_transform_node,const SkISize & asset_size,float time_bias,float time_scale)29     ImageAnimator(sk_sp<ImageAsset> asset,
30                   sk_sp<sksg::Image> image_node,
31                   sk_sp<sksg::Matrix<SkMatrix>> image_transform_node,
32                   const SkISize& asset_size,
33                   float time_bias, float time_scale)
34         : fAsset(std::move(asset))
35         , fImageNode(std::move(image_node))
36         , fImageTransformNode(std::move(image_transform_node))
37         , fAssetSize(asset_size)
38         , fTimeBias(time_bias)
39         , fTimeScale(time_scale)
40         , fIsMultiframe(fAsset->isMultiFrame()) {}
41 
onTick(float t)42     void onTick(float t) override {
43         if (!fIsMultiframe && fImageNode->getImage()) {
44             // Single frame already resolved.
45             return;
46         }
47 
48         auto frame = fAsset->getFrame((t + fTimeBias) * fTimeScale);
49         fImageTransformNode->setMatrix(image_matrix(frame, fAssetSize));
50         fImageNode->setImage(std::move(frame));
51     }
52 
53 private:
54     const sk_sp<ImageAsset>             fAsset;
55     const sk_sp<sksg::Image>            fImageNode;
56     const sk_sp<sksg::Matrix<SkMatrix>> fImageTransformNode;
57     const SkISize                       fAssetSize;
58     const float                         fTimeBias,
59                                         fTimeScale;
60     const bool                          fIsMultiframe;
61 };
62 
63 } // namespace
64 
65 const AnimationBuilder::ImageAssetInfo*
loadImageAsset(const skjson::ObjectValue & jimage) const66 AnimationBuilder::loadImageAsset(const skjson::ObjectValue& jimage) const {
67     const skjson::StringValue* name = jimage["p"];
68     const skjson::StringValue* path = jimage["u"];
69     const skjson::StringValue* id   = jimage["id"];
70     if (!name || !path || !id) {
71         return nullptr;
72     }
73 
74     const SkString res_id(id->begin());
75     if (auto* cached_info = fImageAssetCache.find(res_id)) {
76         return cached_info;
77     }
78 
79     auto asset = fResourceProvider->loadImageAsset(path->begin(), name->begin(), id->begin());
80     if (!asset) {
81         this->log(Logger::Level::kError, nullptr, "Could not load image asset: %s/%s (id: '%s').",
82                   path->begin(), name->begin(), id->begin());
83         return nullptr;
84     }
85 
86     const auto size = SkISize::Make(ParseDefault<int>(jimage["w"], 0),
87                                     ParseDefault<int>(jimage["h"], 0));
88     return fImageAssetCache.set(res_id, { std::move(asset), size });
89 }
90 
attachImageAsset(const skjson::ObjectValue & jimage,LayerInfo * layer_info) const91 sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectValue& jimage,
92                                                            LayerInfo* layer_info) const {
93     const auto* asset_info = this->loadImageAsset(jimage);
94     if (!asset_info) {
95         return nullptr;
96     }
97     SkASSERT(asset_info->fAsset);
98 
99     auto image_node = sksg::Image::Make(nullptr);
100     image_node->setQuality(kMedium_SkFilterQuality);
101 
102     // Optional image transform (mapping the intrinsic image size to declared asset size).
103     sk_sp<sksg::Matrix<SkMatrix>> image_transform;
104 
105     const auto requires_animator = (fFlags & Animation::Builder::kDeferImageLoading)
106                                     || asset_info->fAsset->isMultiFrame();
107     if (requires_animator) {
108         // We don't know the intrinsic image size yet (plus, in the general case,
109         // the size may change from frame to frame) -> we always prepare a scaling transform.
110         image_transform = sksg::Matrix<SkMatrix>::Make(SkMatrix::I());
111         fCurrentAnimatorScope->push_back(sk_make_sp<ImageAnimator>(asset_info->fAsset,
112                                                                    image_node,
113                                                                    image_transform,
114                                                                    asset_info->fSize,
115                                                                    -layer_info->fInPoint,
116                                                                    1 / fFrameRate));
117     } else {
118         // No animator needed, resolve the (only) frame upfront.
119         auto frame = asset_info->fAsset->getFrame(0);
120         if (!frame) {
121             this->log(Logger::Level::kError, nullptr, "Could not load single-frame image asset.");
122             return nullptr;
123         }
124 
125         if (frame->bounds().size() != asset_info->fSize) {
126             image_transform = sksg::Matrix<SkMatrix>::Make(image_matrix(frame, asset_info->fSize));
127         }
128 
129         image_node->setImage(std::move(frame));
130     }
131 
132     // Image layers are sized explicitly.
133     layer_info->fSize = SkSize::Make(asset_info->fSize);
134 
135     if (!image_transform) {
136         // No resize needed.
137         return std::move(image_node);
138     }
139 
140     return sksg::TransformEffect::Make(std::move(image_node), std::move(image_transform));
141 }
142 
attachImageLayer(const skjson::ObjectValue & jlayer,LayerInfo * layer_info) const143 sk_sp<sksg::RenderNode> AnimationBuilder::attachImageLayer(const skjson::ObjectValue& jlayer,
144                                                            LayerInfo* layer_info) const {
145     return this->attachAssetRef(jlayer,
146         [this, &layer_info] (const skjson::ObjectValue& jimage) {
147             return this->attachImageAsset(jimage, layer_info);
148         });
149 }
150 
151 } // namespace internal
152 } // namespace skottie
153