Flutter Engine
The Flutter Engine
FootageLayer.cpp
Go to the documentation of this file.
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
10#include "include/core/SkRect.h"
13#include "include/core/SkSize.h"
25#include "src/core/SkTHash.h"
26#include "src/utils/SkJSON.h"
27
28#include <utility>
29
30namespace skottie {
31namespace internal {
32
33namespace {
34
35SkMatrix image_matrix(const ImageAsset::FrameData& frame_data, const SkISize& dest_size) {
36 if (!frame_data.image) {
37 return SkMatrix::I();
38 }
39
40 const auto size_fit_matrix = frame_data.scaling == ImageAsset::SizeFit::kNone
41 ? SkMatrix::I()
42 : SkMatrix::RectToRect(SkRect::Make(frame_data.image->bounds()),
43 SkRect::Make(dest_size),
44 static_cast<SkMatrix::ScaleToFit>(frame_data.scaling));
45
46 return frame_data.matrix * size_fit_matrix;
47}
48
49class FootageAnimator final : public Animator {
50public:
51 FootageAnimator(sk_sp<ImageAsset> asset,
52 sk_sp<sksg::Image> image_node,
53 sk_sp<sksg::Matrix<SkMatrix>> image_transform_node,
54 const SkISize& asset_size,
55 float time_bias, float time_scale)
56 : fAsset(std::move(asset))
57 , fImageNode(std::move(image_node))
58 , fImageTransformNode(std::move(image_transform_node))
59 , fAssetSize(asset_size)
60 , fTimeBias(time_bias)
61 , fTimeScale(time_scale)
62 , fIsMultiframe(fAsset->isMultiFrame()) {}
63
64 StateChanged onSeek(float t) override {
65 if (!fIsMultiframe && fImageNode->getImage()) {
66 // Single frame already resolved.
67 return false;
68 }
69
70 auto frame_data = fAsset->getFrameData((t + fTimeBias) * fTimeScale);
71 const auto m = image_matrix(frame_data, fAssetSize);
72 if (frame_data.image != fImageNode->getImage() ||
73 frame_data.sampling != fImageNode->getSamplingOptions() ||
74 m != fImageTransformNode->getMatrix()) {
75
76 fImageNode->setImage(std::move(frame_data.image));
77 fImageNode->setSamplingOptions(frame_data.sampling);
78 fImageTransformNode->setMatrix(m);
79 return true;
80 }
81
82 return false;
83 }
84
85private:
86 const sk_sp<ImageAsset> fAsset;
87 const sk_sp<sksg::Image> fImageNode;
88 const sk_sp<sksg::Matrix<SkMatrix>> fImageTransformNode;
89 const SkISize fAssetSize;
90 const float fTimeBias,
91 fTimeScale;
92 const bool fIsMultiframe;
93};
94
95} // namespace
96
97const AnimationBuilder::FootageAssetInfo*
98AnimationBuilder::loadFootageAsset(const skjson::ObjectValue& defaultJImage) const {
99 const skjson::ObjectValue* jimage = &defaultJImage;
100 const skjson::StringValue* slotID = defaultJImage["sid"];
101 if (slotID) {
102 if (!(fSlotsRoot)) {
103 this->log(Logger::Level::kWarning, nullptr,
104 "Slotid found but no slots were found in the json. Using default asset.");
105 } else {
106 const skjson::ObjectValue* slot = (*(fSlotsRoot))[slotID->begin()];
107 if (!slot) {
108 this->log(Logger::Level::kWarning, nullptr,
109 "Specified slotID not found in 'slots'. Using default asset.");
110 } else {
111 jimage = (*slot)["p"];
112 }
113 }
114 }
115
116 const skjson::StringValue* name = (*jimage)["p"];
117 const skjson::StringValue* path = (*jimage)["u"];
118 const skjson::StringValue* id = (*jimage)["id"];
119 if (!name || !path || !id) {
120 return nullptr;
121 }
122
123 const SkString res_id(id->begin());
124 if (auto* cached_info = fImageAssetCache.find(res_id)) {
125 return cached_info;
126 }
127
128 auto asset = fResourceProvider->loadImageAsset(path->begin(), name->begin(), id->begin());
129 if (!asset && !slotID) {
130 this->log(Logger::Level::kError, nullptr, "Could not load image asset: %s/%s (id: '%s').",
131 path->begin(), name->begin(), id->begin());
132 return nullptr;
133 }
134
135 if (slotID) {
136 asset = fSlotManager->trackImageValue(SkString(slotID->begin()), std::move(asset));
137 }
138 const auto size = SkISize::Make(ParseDefault<int>((*jimage)["w"], 0),
139 ParseDefault<int>((*jimage)["h"], 0));
140 return fImageAssetCache.set(res_id, { std::move(asset), size });
141}
142
143sk_sp<sksg::RenderNode> AnimationBuilder::attachFootageAsset(const skjson::ObjectValue& jimage,
144 LayerInfo* layer_info) const {
145 const auto* asset_info = this->loadFootageAsset(jimage);
146 if (!asset_info) {
147 return nullptr;
148 }
149 SkASSERT(asset_info->fAsset);
150
151 auto image_node = sksg::Image::Make(nullptr);
152
153 // Optional image transform (mapping the intrinsic image size to declared asset size).
154 sk_sp<sksg::Matrix<SkMatrix>> image_transform;
155
156 const auto requires_animator = (fFlags & Animation::Builder::kDeferImageLoading)
157 || asset_info->fAsset->isMultiFrame();
158 if (requires_animator) {
159 // We don't know the intrinsic image size yet (plus, in the general case,
160 // the size may change from frame to frame) -> we always prepare a scaling transform.
161 image_transform = sksg::Matrix<SkMatrix>::Make(SkMatrix::I());
162 fCurrentAnimatorScope->push_back(sk_make_sp<FootageAnimator>(asset_info->fAsset,
163 image_node,
164 image_transform,
165 asset_info->fSize,
166 -layer_info->fInPoint,
167 1 / fFrameRate));
168 } else {
169 // No animator needed, resolve the (only) frame upfront.
170 auto frame_data = asset_info->fAsset->getFrameData(0);
171 if (!frame_data.image) {
172 this->log(Logger::Level::kError, nullptr, "Could not load single-frame image asset.");
173 return nullptr;
174 }
175
176 const auto m = image_matrix(frame_data, asset_info->fSize);
177 if (!m.isIdentity()) {
178 image_transform = sksg::Matrix<SkMatrix>::Make(m);
179 }
180
181 image_node->setImage(std::move(frame_data.image));
182 image_node->setSamplingOptions(frame_data.sampling);
183 }
184
185 // Image layers are sized explicitly.
186 layer_info->fSize = SkSize::Make(asset_info->fSize);
187
188 if (!image_transform) {
189 // No resize needed.
190 return image_node;
191 }
192
193 return sksg::TransformEffect::Make(std::move(image_node), std::move(image_transform));
194}
195
196sk_sp<sksg::RenderNode> AnimationBuilder::attachFootageLayer(const skjson::ObjectValue& jlayer,
197 LayerInfo* layer_info) const {
198 const ScopedAssetRef footage_asset(this, jlayer);
199
200 return footage_asset
201 ? this->attachFootageAsset(*footage_asset, layer_info)
202 : nullptr;
203}
204
205} // namespace internal
206} // namespace skottie
#define SkASSERT(cond)
Definition: SkAssert.h:116
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
V * find(const K &key) const
Definition: SkTHash.h:494
V * set(K key, V val)
Definition: SkTHash.h:487
const char * begin() const
Definition: SkJSON.h:315
void log(Logger::Level, const skjson::Value *, const char fmt[],...) const SK_PRINTF_LIKE(4
Definition: Skottie.cpp:71
static sk_sp< Image > Make(sk_sp< SkImage > image)
Definition: SkSGImage.h:33
static sk_sp< Matrix > Make(const T &m)
Definition: SkSGTransform.h:70
static sk_sp< TransformEffect > Make(sk_sp< RenderNode > child, sk_sp< Transform > transform)
Definition: SkSGTransform.h:97
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkImage > image
Definition: SkRecords.h:269
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
Definition: ref_ptr.h:256
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
static constexpr SkSize Make(SkScalar w, SkScalar h)
Definition: SkSize.h:56
const uintptr_t id