Flutter Engine
The Flutter Engine
SkResources.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2019 Google LLC
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
9
12#include "include/core/SkData.h"
17#include "src/base/SkBase64.h"
18#include "src/core/SkOSFile.h"
19#include "src/utils/SkOSPath.h"
20
21#include <cmath>
22
23#if defined(HAVE_VIDEO_DECODER)
26#endif
27
28namespace skresources {
29namespace {
30
31#if defined(HAVE_VIDEO_DECODER)
32
33class VideoAsset final : public ImageAsset {
34public:
36 auto decoder = std::make_unique<SkVideoDecoder>();
37
38 if (!decoder->loadStream(SkMemoryStream::Make(std::move(data))) ||
39 decoder->duration() <= 0) {
40 return nullptr;
41 }
42
43 return sk_sp<VideoAsset>(new VideoAsset(std::move(decoder)));
44 }
45
46private:
47 explicit VideoAsset(std::unique_ptr<SkVideoDecoder> decoder)
48 : fDecoder(std::move(decoder)) {
49 }
50
51 bool isMultiFrame() override { return true; }
52
53 // Each frame has a presentation timestamp
54 // => the timespan for frame N is [stamp_N .. stamp_N+1)
55 // => we use a two-frame sliding window to track the current interval.
56 void advance() {
57 fWindow[0] = std::move(fWindow[1]);
58 fWindow[1].frame = fDecoder->nextImage(&fWindow[1].stamp);
59 fEof = !fWindow[1].frame;
60 }
61
62 sk_sp<SkImage> getFrame(float t_float) override {
63 const auto t = SkTPin(static_cast<double>(t_float), 0.0, fDecoder->duration());
64
65 if (t < fWindow[0].stamp) {
66 // seeking back requires a full rewind
67 fDecoder->rewind();
68 fWindow[0].stamp = fWindow[1].stamp = 0;
69 fEof = 0;
70 }
71
72 while (!fEof && t >= fWindow[1].stamp) {
73 this->advance();
74 }
75
76 SkASSERT(fWindow[0].stamp <= t && (fEof || t < fWindow[1].stamp));
77
78 return fWindow[0].frame;
79 }
80
81 const std::unique_ptr<SkVideoDecoder> fDecoder;
82
83 struct FrameRec {
85 double stamp = 0;
86 };
87
88 FrameRec fWindow[2];
89 bool fEof = false;
90};
91
92#endif // defined(HAVE_VIDEO_DECODER)
93
94} // namespace
95
97 return nullptr;
98}
99
101 // legacy behavior
102 return {
103 this->getFrame(t),
105 SkMatrix::I(),
107 };
108}
109
111 if (auto codec = SkCodec::MakeFromData(std::move(data))) {
113 std::make_unique<SkAnimCodecPlayer>(std::move(codec)), strat));
114 }
115
116 return nullptr;
117}
118
120 SkASSERT(codec);
122 std::make_unique<SkAnimCodecPlayer>(std::move(codec)), strat));
123}
124
125MultiFrameImageAsset::MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer> player,
127 : fPlayer(std::move(player)), fStrategy(strat) {
128 SkASSERT(fPlayer);
129}
130
132 return fPlayer->duration() > 0;
133}
134
135sk_sp<SkImage> MultiFrameImageAsset::generateFrame(float t) {
136 auto decode = [](sk_sp<SkImage> image) {
138
139 static constexpr size_t kMaxArea = 2048 * 2048;
140 const auto image_area = SkToSizeT(image->width() * image->height());
141
142 if (image_area > kMaxArea) {
143 // When the image is too large, decode and scale down to a reasonable size.
144 const auto scale = std::sqrt(static_cast<float>(kMaxArea) / image_area);
146 scale * image->height());
147 SkBitmap bm;
148 if (bm.tryAllocPixels(info, info.minRowBytes()) &&
153 image = bm.asImage();
154 }
155 } else {
156 // When the image size is OK, just force-decode.
158 }
159
160 return image;
161 };
162
163 fPlayer->seek(static_cast<uint32_t>(t * 1000));
164 auto frame = fPlayer->getFrame();
165
166 if (fStrategy == ImageDecodeStrategy::kPreDecode && frame && frame->isLazyGenerated()) {
167 // The multi-frame decoder should never return lazy images.
168 SkASSERT(!this->isMultiFrame());
169 frame = decode(std::move(frame));
170 }
171
172 return frame;
173}
174
176 // For static images we can reuse the cached frame
177 // (which includes the optional pre-decode step).
178 if (!fCachedFrame || this->isMultiFrame()) {
179 fCachedFrame = this->generateFrame(t);
180 }
181
182 return fCachedFrame;
183}
184
187 std::move(base_dir), strat))
188 : nullptr;
189}
190
191FileResourceProvider::FileResourceProvider(SkString base_dir, ImageDecodeStrategy strat)
192 : fDir(std::move(base_dir)), fStrategy(strat) {}
193
194sk_sp<SkData> FileResourceProvider::load(const char resource_path[],
195 const char resource_name[]) const {
196 const auto full_dir = SkOSPath::Join(fDir.c_str() , resource_path),
197 full_path = SkOSPath::Join(full_dir.c_str(), resource_name);
198 return SkData::MakeFromFileName(full_path.c_str());
199}
200
202 const char resource_name[],
203 const char[]) const {
204 auto data = this->load(resource_path, resource_name);
205
206 if (auto image = MultiFrameImageAsset::Make(data, fStrategy)) {
207 return std::move(image);
208 }
209
210#if defined(HAVE_VIDEO_DECODER)
211 if (auto video = VideoAsset::Make(data)) {
212 return std::move(video);
213 }
214#endif
215
216 return nullptr;
217}
218
220 : fProxy(std::move(rp)) {}
221
223 const char resource_name[]) const {
224 return fProxy ? fProxy->load(resource_path, resource_name)
225 : nullptr;
226}
227
229 const char rname[],
230 const char rid[]) const {
231 return fProxy ? fProxy->loadImageAsset(rpath, rname, rid)
232 : nullptr;
233}
234
236 const char url[]) const {
237 return fProxy ? fProxy->loadTypeface(name, url)
238 : nullptr;
239}
240
241sk_sp<SkData> ResourceProviderProxyBase::loadFont(const char name[], const char url[]) const {
242 return fProxy ? fProxy->loadFont(name, url)
243 : nullptr;
244}
245
247 const char name[],
248 const char id[]) {
249 return fProxy ? fProxy->loadAudioAsset(path, name, id)
250 : nullptr;
251}
252
253CachingResourceProvider::CachingResourceProvider(sk_sp<ResourceProvider> rp)
254 : INHERITED(std::move(rp)) {}
255
256sk_sp<ImageAsset> CachingResourceProvider::loadImageAsset(const char resource_path[],
257 const char resource_name[],
258 const char resource_id[]) const {
259 SkAutoMutexExclusive amx(fMutex);
260
261 const SkString key(resource_id);
262 if (const auto* asset = fImageCache.find(key)) {
263 return *asset;
264 }
265
266 auto asset = this->INHERITED::loadImageAsset(resource_path, resource_name, resource_id);
267 fImageCache.set(key, asset);
268
269 return asset;
270}
271
276 new DataURIResourceProviderProxy(std::move(rp), strat, std::move(mgr)));
277}
278
279DataURIResourceProviderProxy::DataURIResourceProviderProxy(sk_sp<ResourceProvider> rp,
282 : INHERITED(std::move(rp)), fStrategy(strat), fFontMgr(std::move(mgr)) {}
283
284static sk_sp<SkData> decode_datauri(const char prefix[], const char uri[]) {
285 // We only handle B64 encoded image dataURIs: data:image/<type>;base64,<data>
286 // (https://en.wikipedia.org/wiki/Data_URI_scheme)
287 static constexpr char kDataURIEncodingStr[] = ";base64,";
288
289 const size_t prefixLen = strlen(prefix);
290 if (strncmp(uri, prefix, prefixLen) != 0) {
291 return nullptr;
292 }
293
294 const char* encoding = strstr(uri + prefixLen, kDataURIEncodingStr);
295 if (!encoding) {
296 return nullptr;
297 }
298
299 const char* b64Data = encoding + std::size(kDataURIEncodingStr) - 1;
300 size_t b64DataLen = strlen(b64Data);
301 size_t dataLen;
302 if (SkBase64::Decode(b64Data, b64DataLen, nullptr, &dataLen) != SkBase64::kNoError) {
303 return nullptr;
304 }
305
307 void* rawData = data->writable_data();
308 if (SkBase64::Decode(b64Data, b64DataLen, rawData, &dataLen) != SkBase64::kNoError) {
309 return nullptr;
310 }
311
312 return data;
313}
314
315sk_sp<ImageAsset> DataURIResourceProviderProxy::loadImageAsset(const char rpath[],
316 const char rname[],
317 const char rid[]) const {
318 // First try to decode the data as base64 using codecs registered with SkCodecs::Register()
319 if (auto data = decode_datauri("data:image/", rname)) {
320 return MultiFrameImageAsset::Make(std::move(data), fStrategy);
321 }
322 // Fallback to the asking the ProviderProxy to load this image for us.
323 return this->INHERITED::loadImageAsset(rpath, rname, rid);
324}
325
326sk_sp<SkTypeface> DataURIResourceProviderProxy::loadTypeface(const char name[],
327 const char url[]) const {
328 if (fFontMgr) {
329 if (auto data = decode_datauri("data:font/", url)) {
330 return fFontMgr->makeFromData(std::move(data));
331 }
332 }
333
334 return this->INHERITED::loadTypeface(name, url);
335}
336
337} // namespace skresources
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
const TextureProxy * fProxy
Definition: DrawPass.cpp:180
#define SkASSERT(cond)
Definition: SkAssert.h:116
bool sk_isdir(const char *path)
static const char * resource_name(SkPDFResourceType type)
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
sk_sp< SkImage > asImage() const
Definition: SkBitmap.cpp:645
const SkPixmap & pixmap() const
Definition: SkBitmap.h:133
bool tryAllocPixels(const SkImageInfo &info, size_t rowBytes)
Definition: SkBitmap.cpp:271
static std::unique_ptr< SkCodec > MakeFromData(sk_sp< SkData >, SkSpan< const SkCodecs::Decoder > decoders, SkPngChunkReader *=nullptr)
Definition: SkCodec.cpp:241
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition: SkData.cpp:116
static sk_sp< SkData > MakeFromFileName(const char path[])
Definition: SkData.cpp:148
sk_sp< SkTypeface > makeFromData(sk_sp< SkData >, int ttcIndex=0) const
Definition: SkFontMgr.cpp:120
sk_sp< SkImage > makeRasterImage(GrDirectContext *, CachingHint cachingHint=kDisallow_CachingHint) const
Definition: SkImage.cpp:267
virtual bool isLazyGenerated() const =0
int width() const
Definition: SkImage.h:285
int height() const
Definition: SkImage.h:291
@ kDisallow_CachingHint
disallows internally caching decoded and copied pixels
Definition: SkImage.h:465
bool scalePixels(const SkPixmap &dst, const SkSamplingOptions &, CachingHint cachingHint=kAllow_CachingHint) const
Definition: SkImage.cpp:127
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
static std::unique_ptr< SkMemoryStream > Make(sk_sp< SkData > data)
Definition: SkStream.cpp:314
static SkString Join(const char *rootPath, const char *relativePath)
Definition: SkOSPath.cpp:14
const char * c_str() const
Definition: SkString.h:133
static sk_sp< DataURIResourceProviderProxy > Make(sk_sp< ResourceProvider > rp, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode, sk_sp< const SkFontMgr > fontMgr=nullptr)
sk_sp< ImageAsset > loadImageAsset(const char[], const char[], const char[]) const override
sk_sp< SkData > load(const char resource_path[], const char resource_name[]) const override
static sk_sp< FileResourceProvider > Make(SkString base_dir, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode)
virtual sk_sp< SkImage > getFrame(float t)
Definition: SkResources.cpp:96
virtual FrameData getFrameData(float t)
static sk_sp< MultiFrameImageAsset > Make(sk_sp< SkData >, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode)
sk_sp< SkImage > getFrame(float t) override
sk_sp< SkTypeface > loadTypeface(const char[], const char[]) const override
ResourceProviderProxyBase(sk_sp< ResourceProvider >)
sk_sp< ImageAsset > loadImageAsset(const char[], const char[], const char[]) const override
sk_sp< ExternalTrackAsset > loadAudioAsset(const char[], const char[], const char[]) override
const sk_sp< ResourceProvider > fProxy
Definition: SkResources.h:242
sk_sp< SkData > load(const char[], const char[]) const override
sk_sp< SkData > loadFont(const char[], const char[]) const override
double frame
Definition: examples.cpp:31
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
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
SkSamplingOptions(SkFilterMode::kLinear))
skresources::ImageAsset ImageAsset
Definition: Skottie.h:45
static sk_sp< SkData > decode_datauri(const char prefix[], const char uri[])
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
Definition: ref_ptr.h:256
static DecodeResult decode(std::string path)
Definition: png_codec.cpp:124
const Scalar scale
static Error Decode(const void *src, size_t srcLength, void *dst, size_t *dstLength)
Definition: SkBase64.cpp:37
@ kNoError
Definition: SkBase64.h:16
static SkImageInfo MakeN32Premul(int width, int height)
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63