1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2019 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skresources/include/SkResources.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodec.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontMgr.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTPin.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skresources/src/SkAnimCodecPlayer.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkBase64.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkOSFile.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkOSPath.h"
20*c8dee2aaSAndroid Build Coastguard Worker
21*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
22*c8dee2aaSAndroid Build Coastguard Worker
23*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_DECODER)
24*c8dee2aaSAndroid Build Coastguard Worker #include "experimental/ffmpeg/SkVideoDecoder.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
26*c8dee2aaSAndroid Build Coastguard Worker #endif
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker namespace skresources {
29*c8dee2aaSAndroid Build Coastguard Worker namespace {
30*c8dee2aaSAndroid Build Coastguard Worker
31*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_DECODER)
32*c8dee2aaSAndroid Build Coastguard Worker
33*c8dee2aaSAndroid Build Coastguard Worker class VideoAsset final : public ImageAsset {
34*c8dee2aaSAndroid Build Coastguard Worker public:
Make(sk_sp<SkData> data)35*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<VideoAsset> Make(sk_sp<SkData> data) {
36*c8dee2aaSAndroid Build Coastguard Worker auto decoder = std::make_unique<SkVideoDecoder>();
37*c8dee2aaSAndroid Build Coastguard Worker
38*c8dee2aaSAndroid Build Coastguard Worker if (!decoder->loadStream(SkMemoryStream::Make(std::move(data))) ||
39*c8dee2aaSAndroid Build Coastguard Worker decoder->duration() <= 0) {
40*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
41*c8dee2aaSAndroid Build Coastguard Worker }
42*c8dee2aaSAndroid Build Coastguard Worker
43*c8dee2aaSAndroid Build Coastguard Worker return sk_sp<VideoAsset>(new VideoAsset(std::move(decoder)));
44*c8dee2aaSAndroid Build Coastguard Worker }
45*c8dee2aaSAndroid Build Coastguard Worker
46*c8dee2aaSAndroid Build Coastguard Worker private:
VideoAsset(std::unique_ptr<SkVideoDecoder> decoder)47*c8dee2aaSAndroid Build Coastguard Worker explicit VideoAsset(std::unique_ptr<SkVideoDecoder> decoder)
48*c8dee2aaSAndroid Build Coastguard Worker : fDecoder(std::move(decoder)) {
49*c8dee2aaSAndroid Build Coastguard Worker }
50*c8dee2aaSAndroid Build Coastguard Worker
isMultiFrame()51*c8dee2aaSAndroid Build Coastguard Worker bool isMultiFrame() override { return true; }
52*c8dee2aaSAndroid Build Coastguard Worker
53*c8dee2aaSAndroid Build Coastguard Worker // Each frame has a presentation timestamp
54*c8dee2aaSAndroid Build Coastguard Worker // => the timespan for frame N is [stamp_N .. stamp_N+1)
55*c8dee2aaSAndroid Build Coastguard Worker // => we use a two-frame sliding window to track the current interval.
advance()56*c8dee2aaSAndroid Build Coastguard Worker void advance() {
57*c8dee2aaSAndroid Build Coastguard Worker fWindow[0] = std::move(fWindow[1]);
58*c8dee2aaSAndroid Build Coastguard Worker fWindow[1].frame = fDecoder->nextImage(&fWindow[1].stamp);
59*c8dee2aaSAndroid Build Coastguard Worker fEof = !fWindow[1].frame;
60*c8dee2aaSAndroid Build Coastguard Worker }
61*c8dee2aaSAndroid Build Coastguard Worker
getFrame(float t_float)62*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> getFrame(float t_float) override {
63*c8dee2aaSAndroid Build Coastguard Worker const auto t = SkTPin(static_cast<double>(t_float), 0.0, fDecoder->duration());
64*c8dee2aaSAndroid Build Coastguard Worker
65*c8dee2aaSAndroid Build Coastguard Worker if (t < fWindow[0].stamp) {
66*c8dee2aaSAndroid Build Coastguard Worker // seeking back requires a full rewind
67*c8dee2aaSAndroid Build Coastguard Worker fDecoder->rewind();
68*c8dee2aaSAndroid Build Coastguard Worker fWindow[0].stamp = fWindow[1].stamp = 0;
69*c8dee2aaSAndroid Build Coastguard Worker fEof = 0;
70*c8dee2aaSAndroid Build Coastguard Worker }
71*c8dee2aaSAndroid Build Coastguard Worker
72*c8dee2aaSAndroid Build Coastguard Worker while (!fEof && t >= fWindow[1].stamp) {
73*c8dee2aaSAndroid Build Coastguard Worker this->advance();
74*c8dee2aaSAndroid Build Coastguard Worker }
75*c8dee2aaSAndroid Build Coastguard Worker
76*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fWindow[0].stamp <= t && (fEof || t < fWindow[1].stamp));
77*c8dee2aaSAndroid Build Coastguard Worker
78*c8dee2aaSAndroid Build Coastguard Worker return fWindow[0].frame;
79*c8dee2aaSAndroid Build Coastguard Worker }
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker const std::unique_ptr<SkVideoDecoder> fDecoder;
82*c8dee2aaSAndroid Build Coastguard Worker
83*c8dee2aaSAndroid Build Coastguard Worker struct FrameRec {
84*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> frame;
85*c8dee2aaSAndroid Build Coastguard Worker double stamp = 0;
86*c8dee2aaSAndroid Build Coastguard Worker };
87*c8dee2aaSAndroid Build Coastguard Worker
88*c8dee2aaSAndroid Build Coastguard Worker FrameRec fWindow[2];
89*c8dee2aaSAndroid Build Coastguard Worker bool fEof = false;
90*c8dee2aaSAndroid Build Coastguard Worker };
91*c8dee2aaSAndroid Build Coastguard Worker
92*c8dee2aaSAndroid Build Coastguard Worker #endif // defined(HAVE_VIDEO_DECODER)
93*c8dee2aaSAndroid Build Coastguard Worker
94*c8dee2aaSAndroid Build Coastguard Worker } // namespace
95*c8dee2aaSAndroid Build Coastguard Worker
getFrame(float t)96*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> ImageAsset::getFrame(float t) {
97*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
98*c8dee2aaSAndroid Build Coastguard Worker }
99*c8dee2aaSAndroid Build Coastguard Worker
getFrameData(float t)100*c8dee2aaSAndroid Build Coastguard Worker ImageAsset::FrameData ImageAsset::getFrameData(float t) {
101*c8dee2aaSAndroid Build Coastguard Worker // legacy behavior
102*c8dee2aaSAndroid Build Coastguard Worker return {
103*c8dee2aaSAndroid Build Coastguard Worker this->getFrame(t),
104*c8dee2aaSAndroid Build Coastguard Worker SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest),
105*c8dee2aaSAndroid Build Coastguard Worker SkMatrix::I(),
106*c8dee2aaSAndroid Build Coastguard Worker SizeFit::kCenter,
107*c8dee2aaSAndroid Build Coastguard Worker };
108*c8dee2aaSAndroid Build Coastguard Worker }
109*c8dee2aaSAndroid Build Coastguard Worker
Make(sk_sp<SkData> data,ImageDecodeStrategy strat)110*c8dee2aaSAndroid Build Coastguard Worker sk_sp<MultiFrameImageAsset> MultiFrameImageAsset::Make(sk_sp<SkData> data, ImageDecodeStrategy strat) {
111*c8dee2aaSAndroid Build Coastguard Worker if (auto codec = SkCodec::MakeFromData(std::move(data))) {
112*c8dee2aaSAndroid Build Coastguard Worker return sk_sp<MultiFrameImageAsset>(new MultiFrameImageAsset(
113*c8dee2aaSAndroid Build Coastguard Worker std::make_unique<SkAnimCodecPlayer>(std::move(codec)), strat));
114*c8dee2aaSAndroid Build Coastguard Worker }
115*c8dee2aaSAndroid Build Coastguard Worker
116*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
117*c8dee2aaSAndroid Build Coastguard Worker }
118*c8dee2aaSAndroid Build Coastguard Worker
Make(std::unique_ptr<SkCodec> codec,ImageDecodeStrategy strat)119*c8dee2aaSAndroid Build Coastguard Worker sk_sp<MultiFrameImageAsset> MultiFrameImageAsset::Make(std::unique_ptr<SkCodec> codec, ImageDecodeStrategy strat) {
120*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(codec);
121*c8dee2aaSAndroid Build Coastguard Worker return sk_sp<MultiFrameImageAsset>(new MultiFrameImageAsset(
122*c8dee2aaSAndroid Build Coastguard Worker std::make_unique<SkAnimCodecPlayer>(std::move(codec)), strat));
123*c8dee2aaSAndroid Build Coastguard Worker }
124*c8dee2aaSAndroid Build Coastguard Worker
MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer> player,ImageDecodeStrategy strat)125*c8dee2aaSAndroid Build Coastguard Worker MultiFrameImageAsset::MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer> player,
126*c8dee2aaSAndroid Build Coastguard Worker ImageDecodeStrategy strat)
127*c8dee2aaSAndroid Build Coastguard Worker : fPlayer(std::move(player)), fStrategy(strat) {
128*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fPlayer);
129*c8dee2aaSAndroid Build Coastguard Worker }
130*c8dee2aaSAndroid Build Coastguard Worker
isMultiFrame()131*c8dee2aaSAndroid Build Coastguard Worker bool MultiFrameImageAsset::isMultiFrame() {
132*c8dee2aaSAndroid Build Coastguard Worker return fPlayer->duration() > 0;
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker
generateFrame(float t)135*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> MultiFrameImageAsset::generateFrame(float t) {
136*c8dee2aaSAndroid Build Coastguard Worker auto decode = [](sk_sp<SkImage> image) {
137*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(image->isLazyGenerated());
138*c8dee2aaSAndroid Build Coastguard Worker
139*c8dee2aaSAndroid Build Coastguard Worker static constexpr size_t kMaxArea = 2048 * 2048;
140*c8dee2aaSAndroid Build Coastguard Worker const auto image_area = SkToSizeT(image->width() * image->height());
141*c8dee2aaSAndroid Build Coastguard Worker
142*c8dee2aaSAndroid Build Coastguard Worker if (image_area > kMaxArea) {
143*c8dee2aaSAndroid Build Coastguard Worker // When the image is too large, decode and scale down to a reasonable size.
144*c8dee2aaSAndroid Build Coastguard Worker const auto scale = std::sqrt(static_cast<float>(kMaxArea) / image_area);
145*c8dee2aaSAndroid Build Coastguard Worker const auto info = SkImageInfo::MakeN32Premul(scale * image->width(),
146*c8dee2aaSAndroid Build Coastguard Worker scale * image->height());
147*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bm;
148*c8dee2aaSAndroid Build Coastguard Worker if (bm.tryAllocPixels(info, info.minRowBytes()) &&
149*c8dee2aaSAndroid Build Coastguard Worker image->scalePixels(bm.pixmap(),
150*c8dee2aaSAndroid Build Coastguard Worker SkSamplingOptions(SkFilterMode::kLinear,
151*c8dee2aaSAndroid Build Coastguard Worker SkMipmapMode::kNearest),
152*c8dee2aaSAndroid Build Coastguard Worker SkImage::kDisallow_CachingHint)) {
153*c8dee2aaSAndroid Build Coastguard Worker image = bm.asImage();
154*c8dee2aaSAndroid Build Coastguard Worker }
155*c8dee2aaSAndroid Build Coastguard Worker } else {
156*c8dee2aaSAndroid Build Coastguard Worker // When the image size is OK, just force-decode.
157*c8dee2aaSAndroid Build Coastguard Worker image = image->makeRasterImage();
158*c8dee2aaSAndroid Build Coastguard Worker }
159*c8dee2aaSAndroid Build Coastguard Worker
160*c8dee2aaSAndroid Build Coastguard Worker return image;
161*c8dee2aaSAndroid Build Coastguard Worker };
162*c8dee2aaSAndroid Build Coastguard Worker
163*c8dee2aaSAndroid Build Coastguard Worker fPlayer->seek(static_cast<uint32_t>(t * 1000));
164*c8dee2aaSAndroid Build Coastguard Worker auto frame = fPlayer->getFrame();
165*c8dee2aaSAndroid Build Coastguard Worker
166*c8dee2aaSAndroid Build Coastguard Worker if (fStrategy == ImageDecodeStrategy::kPreDecode && frame && frame->isLazyGenerated()) {
167*c8dee2aaSAndroid Build Coastguard Worker // The multi-frame decoder should never return lazy images.
168*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!this->isMultiFrame());
169*c8dee2aaSAndroid Build Coastguard Worker frame = decode(std::move(frame));
170*c8dee2aaSAndroid Build Coastguard Worker }
171*c8dee2aaSAndroid Build Coastguard Worker
172*c8dee2aaSAndroid Build Coastguard Worker return frame;
173*c8dee2aaSAndroid Build Coastguard Worker }
174*c8dee2aaSAndroid Build Coastguard Worker
getFrame(float t)175*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> MultiFrameImageAsset::getFrame(float t) {
176*c8dee2aaSAndroid Build Coastguard Worker // For static images we can reuse the cached frame
177*c8dee2aaSAndroid Build Coastguard Worker // (which includes the optional pre-decode step).
178*c8dee2aaSAndroid Build Coastguard Worker if (!fCachedFrame || this->isMultiFrame()) {
179*c8dee2aaSAndroid Build Coastguard Worker fCachedFrame = this->generateFrame(t);
180*c8dee2aaSAndroid Build Coastguard Worker }
181*c8dee2aaSAndroid Build Coastguard Worker
182*c8dee2aaSAndroid Build Coastguard Worker return fCachedFrame;
183*c8dee2aaSAndroid Build Coastguard Worker }
184*c8dee2aaSAndroid Build Coastguard Worker
Make(SkString base_dir,ImageDecodeStrategy strat)185*c8dee2aaSAndroid Build Coastguard Worker sk_sp<FileResourceProvider> FileResourceProvider::Make(SkString base_dir, ImageDecodeStrategy strat) {
186*c8dee2aaSAndroid Build Coastguard Worker return sk_isdir(base_dir.c_str()) ? sk_sp<FileResourceProvider>(new FileResourceProvider(
187*c8dee2aaSAndroid Build Coastguard Worker std::move(base_dir), strat))
188*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
189*c8dee2aaSAndroid Build Coastguard Worker }
190*c8dee2aaSAndroid Build Coastguard Worker
FileResourceProvider(SkString base_dir,ImageDecodeStrategy strat)191*c8dee2aaSAndroid Build Coastguard Worker FileResourceProvider::FileResourceProvider(SkString base_dir, ImageDecodeStrategy strat)
192*c8dee2aaSAndroid Build Coastguard Worker : fDir(std::move(base_dir)), fStrategy(strat) {}
193*c8dee2aaSAndroid Build Coastguard Worker
load(const char resource_path[],const char resource_name[]) const194*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> FileResourceProvider::load(const char resource_path[],
195*c8dee2aaSAndroid Build Coastguard Worker const char resource_name[]) const {
196*c8dee2aaSAndroid Build Coastguard Worker const auto full_dir = SkOSPath::Join(fDir.c_str() , resource_path),
197*c8dee2aaSAndroid Build Coastguard Worker full_path = SkOSPath::Join(full_dir.c_str(), resource_name);
198*c8dee2aaSAndroid Build Coastguard Worker return SkData::MakeFromFileName(full_path.c_str());
199*c8dee2aaSAndroid Build Coastguard Worker }
200*c8dee2aaSAndroid Build Coastguard Worker
loadImageAsset(const char resource_path[],const char resource_name[],const char[]) const201*c8dee2aaSAndroid Build Coastguard Worker sk_sp<ImageAsset> FileResourceProvider::loadImageAsset(const char resource_path[],
202*c8dee2aaSAndroid Build Coastguard Worker const char resource_name[],
203*c8dee2aaSAndroid Build Coastguard Worker const char[]) const {
204*c8dee2aaSAndroid Build Coastguard Worker auto data = this->load(resource_path, resource_name);
205*c8dee2aaSAndroid Build Coastguard Worker
206*c8dee2aaSAndroid Build Coastguard Worker if (auto image = MultiFrameImageAsset::Make(data, fStrategy)) {
207*c8dee2aaSAndroid Build Coastguard Worker return std::move(image);
208*c8dee2aaSAndroid Build Coastguard Worker }
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker #if defined(HAVE_VIDEO_DECODER)
211*c8dee2aaSAndroid Build Coastguard Worker if (auto video = VideoAsset::Make(data)) {
212*c8dee2aaSAndroid Build Coastguard Worker return std::move(video);
213*c8dee2aaSAndroid Build Coastguard Worker }
214*c8dee2aaSAndroid Build Coastguard Worker #endif
215*c8dee2aaSAndroid Build Coastguard Worker
216*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
217*c8dee2aaSAndroid Build Coastguard Worker }
218*c8dee2aaSAndroid Build Coastguard Worker
ResourceProviderProxyBase(sk_sp<ResourceProvider> rp)219*c8dee2aaSAndroid Build Coastguard Worker ResourceProviderProxyBase::ResourceProviderProxyBase(sk_sp<ResourceProvider> rp)
220*c8dee2aaSAndroid Build Coastguard Worker : fProxy(std::move(rp)) {}
221*c8dee2aaSAndroid Build Coastguard Worker
load(const char resource_path[],const char resource_name[]) const222*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> ResourceProviderProxyBase::load(const char resource_path[],
223*c8dee2aaSAndroid Build Coastguard Worker const char resource_name[]) const {
224*c8dee2aaSAndroid Build Coastguard Worker return fProxy ? fProxy->load(resource_path, resource_name)
225*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
226*c8dee2aaSAndroid Build Coastguard Worker }
227*c8dee2aaSAndroid Build Coastguard Worker
loadImageAsset(const char rpath[],const char rname[],const char rid[]) const228*c8dee2aaSAndroid Build Coastguard Worker sk_sp<ImageAsset> ResourceProviderProxyBase::loadImageAsset(const char rpath[],
229*c8dee2aaSAndroid Build Coastguard Worker const char rname[],
230*c8dee2aaSAndroid Build Coastguard Worker const char rid[]) const {
231*c8dee2aaSAndroid Build Coastguard Worker return fProxy ? fProxy->loadImageAsset(rpath, rname, rid)
232*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
233*c8dee2aaSAndroid Build Coastguard Worker }
234*c8dee2aaSAndroid Build Coastguard Worker
loadTypeface(const char name[],const char url[]) const235*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkTypeface> ResourceProviderProxyBase::loadTypeface(const char name[],
236*c8dee2aaSAndroid Build Coastguard Worker const char url[]) const {
237*c8dee2aaSAndroid Build Coastguard Worker return fProxy ? fProxy->loadTypeface(name, url)
238*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker
loadFont(const char name[],const char url[]) const241*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> ResourceProviderProxyBase::loadFont(const char name[], const char url[]) const {
242*c8dee2aaSAndroid Build Coastguard Worker return fProxy ? fProxy->loadFont(name, url)
243*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
244*c8dee2aaSAndroid Build Coastguard Worker }
245*c8dee2aaSAndroid Build Coastguard Worker
loadAudioAsset(const char path[],const char name[],const char id[])246*c8dee2aaSAndroid Build Coastguard Worker sk_sp<ExternalTrackAsset> ResourceProviderProxyBase::loadAudioAsset(const char path[],
247*c8dee2aaSAndroid Build Coastguard Worker const char name[],
248*c8dee2aaSAndroid Build Coastguard Worker const char id[]) {
249*c8dee2aaSAndroid Build Coastguard Worker return fProxy ? fProxy->loadAudioAsset(path, name, id)
250*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
251*c8dee2aaSAndroid Build Coastguard Worker }
252*c8dee2aaSAndroid Build Coastguard Worker
CachingResourceProvider(sk_sp<ResourceProvider> rp)253*c8dee2aaSAndroid Build Coastguard Worker CachingResourceProvider::CachingResourceProvider(sk_sp<ResourceProvider> rp)
254*c8dee2aaSAndroid Build Coastguard Worker : INHERITED(std::move(rp)) {}
255*c8dee2aaSAndroid Build Coastguard Worker
loadImageAsset(const char resource_path[],const char resource_name[],const char resource_id[]) const256*c8dee2aaSAndroid Build Coastguard Worker sk_sp<ImageAsset> CachingResourceProvider::loadImageAsset(const char resource_path[],
257*c8dee2aaSAndroid Build Coastguard Worker const char resource_name[],
258*c8dee2aaSAndroid Build Coastguard Worker const char resource_id[]) const {
259*c8dee2aaSAndroid Build Coastguard Worker SkAutoMutexExclusive amx(fMutex);
260*c8dee2aaSAndroid Build Coastguard Worker
261*c8dee2aaSAndroid Build Coastguard Worker const SkString key(resource_id);
262*c8dee2aaSAndroid Build Coastguard Worker if (const auto* asset = fImageCache.find(key)) {
263*c8dee2aaSAndroid Build Coastguard Worker return *asset;
264*c8dee2aaSAndroid Build Coastguard Worker }
265*c8dee2aaSAndroid Build Coastguard Worker
266*c8dee2aaSAndroid Build Coastguard Worker auto asset = this->INHERITED::loadImageAsset(resource_path, resource_name, resource_id);
267*c8dee2aaSAndroid Build Coastguard Worker fImageCache.set(key, asset);
268*c8dee2aaSAndroid Build Coastguard Worker
269*c8dee2aaSAndroid Build Coastguard Worker return asset;
270*c8dee2aaSAndroid Build Coastguard Worker }
271*c8dee2aaSAndroid Build Coastguard Worker
Make(sk_sp<ResourceProvider> rp,ImageDecodeStrategy strat,sk_sp<const SkFontMgr> mgr)272*c8dee2aaSAndroid Build Coastguard Worker sk_sp<DataURIResourceProviderProxy> DataURIResourceProviderProxy::Make(sk_sp<ResourceProvider> rp,
273*c8dee2aaSAndroid Build Coastguard Worker ImageDecodeStrategy strat,
274*c8dee2aaSAndroid Build Coastguard Worker sk_sp<const SkFontMgr> mgr) {
275*c8dee2aaSAndroid Build Coastguard Worker return sk_sp<DataURIResourceProviderProxy>(
276*c8dee2aaSAndroid Build Coastguard Worker new DataURIResourceProviderProxy(std::move(rp), strat, std::move(mgr)));
277*c8dee2aaSAndroid Build Coastguard Worker }
278*c8dee2aaSAndroid Build Coastguard Worker
DataURIResourceProviderProxy(sk_sp<ResourceProvider> rp,ImageDecodeStrategy strat,sk_sp<const SkFontMgr> mgr)279*c8dee2aaSAndroid Build Coastguard Worker DataURIResourceProviderProxy::DataURIResourceProviderProxy(sk_sp<ResourceProvider> rp,
280*c8dee2aaSAndroid Build Coastguard Worker ImageDecodeStrategy strat,
281*c8dee2aaSAndroid Build Coastguard Worker sk_sp<const SkFontMgr> mgr)
282*c8dee2aaSAndroid Build Coastguard Worker : INHERITED(std::move(rp)), fStrategy(strat), fFontMgr(std::move(mgr)) {}
283*c8dee2aaSAndroid Build Coastguard Worker
decode_datauri(const char prefix[],const char uri[])284*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkData> decode_datauri(const char prefix[], const char uri[]) {
285*c8dee2aaSAndroid Build Coastguard Worker // We only handle B64 encoded image dataURIs: data:image/<type>;base64,<data>
286*c8dee2aaSAndroid Build Coastguard Worker // (https://en.wikipedia.org/wiki/Data_URI_scheme)
287*c8dee2aaSAndroid Build Coastguard Worker static constexpr char kDataURIEncodingStr[] = ";base64,";
288*c8dee2aaSAndroid Build Coastguard Worker
289*c8dee2aaSAndroid Build Coastguard Worker const size_t prefixLen = strlen(prefix);
290*c8dee2aaSAndroid Build Coastguard Worker if (strncmp(uri, prefix, prefixLen) != 0) {
291*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
292*c8dee2aaSAndroid Build Coastguard Worker }
293*c8dee2aaSAndroid Build Coastguard Worker
294*c8dee2aaSAndroid Build Coastguard Worker const char* encoding = strstr(uri + prefixLen, kDataURIEncodingStr);
295*c8dee2aaSAndroid Build Coastguard Worker if (!encoding) {
296*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
297*c8dee2aaSAndroid Build Coastguard Worker }
298*c8dee2aaSAndroid Build Coastguard Worker
299*c8dee2aaSAndroid Build Coastguard Worker const char* b64Data = encoding + std::size(kDataURIEncodingStr) - 1;
300*c8dee2aaSAndroid Build Coastguard Worker size_t b64DataLen = strlen(b64Data);
301*c8dee2aaSAndroid Build Coastguard Worker size_t dataLen;
302*c8dee2aaSAndroid Build Coastguard Worker if (SkBase64::Decode(b64Data, b64DataLen, nullptr, &dataLen) != SkBase64::kNoError) {
303*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
304*c8dee2aaSAndroid Build Coastguard Worker }
305*c8dee2aaSAndroid Build Coastguard Worker
306*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> data = SkData::MakeUninitialized(dataLen);
307*c8dee2aaSAndroid Build Coastguard Worker void* rawData = data->writable_data();
308*c8dee2aaSAndroid Build Coastguard Worker if (SkBase64::Decode(b64Data, b64DataLen, rawData, &dataLen) != SkBase64::kNoError) {
309*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
310*c8dee2aaSAndroid Build Coastguard Worker }
311*c8dee2aaSAndroid Build Coastguard Worker
312*c8dee2aaSAndroid Build Coastguard Worker return data;
313*c8dee2aaSAndroid Build Coastguard Worker }
314*c8dee2aaSAndroid Build Coastguard Worker
loadImageAsset(const char rpath[],const char rname[],const char rid[]) const315*c8dee2aaSAndroid Build Coastguard Worker sk_sp<ImageAsset> DataURIResourceProviderProxy::loadImageAsset(const char rpath[],
316*c8dee2aaSAndroid Build Coastguard Worker const char rname[],
317*c8dee2aaSAndroid Build Coastguard Worker const char rid[]) const {
318*c8dee2aaSAndroid Build Coastguard Worker // First try to decode the data as base64 using codecs registered with SkCodecs::Register()
319*c8dee2aaSAndroid Build Coastguard Worker if (auto data = decode_datauri("data:image/", rname)) {
320*c8dee2aaSAndroid Build Coastguard Worker return MultiFrameImageAsset::Make(std::move(data), fStrategy);
321*c8dee2aaSAndroid Build Coastguard Worker }
322*c8dee2aaSAndroid Build Coastguard Worker // Fallback to the asking the ProviderProxy to load this image for us.
323*c8dee2aaSAndroid Build Coastguard Worker return this->INHERITED::loadImageAsset(rpath, rname, rid);
324*c8dee2aaSAndroid Build Coastguard Worker }
325*c8dee2aaSAndroid Build Coastguard Worker
loadTypeface(const char name[],const char url[]) const326*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkTypeface> DataURIResourceProviderProxy::loadTypeface(const char name[],
327*c8dee2aaSAndroid Build Coastguard Worker const char url[]) const {
328*c8dee2aaSAndroid Build Coastguard Worker if (fFontMgr) {
329*c8dee2aaSAndroid Build Coastguard Worker if (auto data = decode_datauri("data:font/", url)) {
330*c8dee2aaSAndroid Build Coastguard Worker return fFontMgr->makeFromData(std::move(data));
331*c8dee2aaSAndroid Build Coastguard Worker }
332*c8dee2aaSAndroid Build Coastguard Worker }
333*c8dee2aaSAndroid Build Coastguard Worker
334*c8dee2aaSAndroid Build Coastguard Worker return this->INHERITED::loadTypeface(name, url);
335*c8dee2aaSAndroid Build Coastguard Worker }
336*c8dee2aaSAndroid Build Coastguard Worker
337*c8dee2aaSAndroid Build Coastguard Worker } // namespace skresources
338