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
8#ifndef SkResources_DEFINED
9#define SkResources_DEFINED
10
11#include "include/core/SkData.h"
12#include "include/core/SkRefCnt.h"
13#include "include/core/SkString.h"
14#include "include/core/SkTypeface.h"
15#include "include/core/SkTypes.h"
16#include "include/private/SkMutex.h"
17#include "include/private/SkTHash.h"
18
19#include <memory>
20
21class SkAnimCodecPlayer;
22class SkImage;
23
24namespace skresources {
25
26/**
27 * Image asset proxy interface.
28 */
29class SK_API ImageAsset : public SkRefCnt {
30public:
31 /**
32 * Returns true if the image asset is animated.
33 */
34 virtual bool isMultiFrame() = 0;
35
36 /**
37 * Returns the SkImage for a given frame.
38 *
39 * If the image asset is static, getImage() is only called once, at animation load time.
40 * Otherwise, this gets invoked every time the animation time is adjusted (on every seek).
41 *
42 * Embedders should cache and serve the same SkImage whenever possible, for efficiency.
43 *
44 * @param t Frame time code, in seconds, relative to the image layer timeline origin
45 * (in-point).
46 */
47 virtual sk_sp<SkImage> getFrame(float t) = 0;
48};
49
50class MultiFrameImageAsset final : public ImageAsset {
51public:
52 /**
53 * By default, images are decoded on-the-fly, at rasterization time.
54 * Large images may cause jank as decoding is expensive (and can thrash internal caches).
55 *
56 * Pass |predecode| true to force-decode all images upfront, at the cost of potentially more RAM
57 * and slower animation build times.
58 */
59 static sk_sp<MultiFrameImageAsset> Make(sk_sp<SkData>, bool predecode = false);
60
61 bool isMultiFrame() override;
62
63 sk_sp<SkImage> getFrame(float t) override;
64
65private:
66 explicit MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer>, bool predecode);
67
68 sk_sp<SkImage> generateFrame(float t);
69
70 std::unique_ptr<SkAnimCodecPlayer> fPlayer;
71 sk_sp<SkImage> fCachedFrame;
72 bool fPreDecode;
73
74 using INHERITED = ImageAsset;
75};
76
77/**
78 * External track (e.g. audio playback) interface.
79 *
80 * Used to wrap data payload and playback controllers.
81 */
82class ExternalTrackAsset : public SkRefCnt {
83public:
84 /**
85 * Playback control callback, emitted for each corresponding Animation::seek().
86 *
87 * @param t Frame time code, in seconds, relative to the layer's timeline origin
88 * (in-point).
89 *
90 * Negative |t| values are used to signal off state (stop playback outside layer span).
91 */
92 virtual void seek(float t) = 0;
93};
94
95/**
96 * ResourceProvider is an interface that lets rich-content modules defer loading of external
97 * resources (images, fonts, etc.) to embedding clients.
98 */
99class SK_API ResourceProvider : public SkRefCnt {
100public:
101 /**
102 * Load a generic resource (currently only nested animations) specified by |path| + |name|,
103 * and return as an SkData.
104 */
105 virtual sk_sp<SkData> load(const char[] /* resource_path */,
106 const char[] /* resource_name */) const {
107 return nullptr;
108 }
109
110 /**
111 * Load an image asset specified by |path| + |name|, and returns the corresponding
112 * ImageAsset proxy.
113 */
114 virtual sk_sp<ImageAsset> loadImageAsset(const char[] /* resource_path */,
115 const char[] /* resource_name */,
116 const char[] /* resource_id */) const {
117 return nullptr;
118 }
119
120 /**
121 * Load an external audio track specified by |path|/|name|/|id|.
122 */
123 virtual sk_sp<ExternalTrackAsset> loadAudioAsset(const char[] /* resource_path */,
124 const char[] /* resource_name */,
125 const char[] /* resource_id */) {
126 return nullptr;
127 }
128
129 /**
130 * DEPRECATED: implement loadTypeface() instead.
131 *
132 * Load an external font and return as SkData.
133 *
134 * @param name font name ("fName" Lottie property)
135 * @param url web font URL ("fPath" Lottie property)
136 *
137 * -- Note --
138 *
139 * This mechanism assumes monolithic fonts (single data blob). Some web font providers may
140 * serve multiple font blobs, segmented for various unicode ranges, depending on user agent
141 * capabilities (woff, woff2). In that case, the embedder would need to advertise no user
142 * agent capabilities when fetching the URL, in order to receive full font data.
143 */
144 virtual sk_sp<SkData> loadFont(const char[] /* name */,
145 const char[] /* url */) const {
146 return nullptr;
147 }
148
149 /**
150 * Load an external font and return as SkTypeface.
151 *
152 * @param name font name
153 * @param url web font URL
154 */
155 virtual sk_sp<SkTypeface> loadTypeface(const char[] /* name */,
156 const char[] /* url */) const {
157 return nullptr;
158 }
159};
160
161class FileResourceProvider final : public ResourceProvider {
162public:
163 static sk_sp<FileResourceProvider> Make(SkString base_dir, bool predecode = false);
164
165 sk_sp<SkData> load(const char resource_path[], const char resource_name[]) const override;
166
167 sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
168
169private:
170 FileResourceProvider(SkString, bool);
171
172 const SkString fDir;
173 const bool fPredecode;
174
175 using INHERITED = ResourceProvider;
176};
177
178class ResourceProviderProxyBase : public ResourceProvider {
179protected:
180 explicit ResourceProviderProxyBase(sk_sp<ResourceProvider>);
181
182 sk_sp<SkData> load(const char[], const char[]) const override;
183 sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
184 sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override;
185 sk_sp<SkData> loadFont(const char[], const char[]) const override;
186
187private:
188 const sk_sp<ResourceProvider> fProxy;
189};
190
191class CachingResourceProvider final : public ResourceProviderProxyBase {
192public:
193 static sk_sp<CachingResourceProvider> Make(sk_sp<ResourceProvider> rp) {
194 return rp ? sk_sp<CachingResourceProvider>(new CachingResourceProvider(std::move(rp)))
195 : nullptr;
196 }
197
198private:
199 explicit CachingResourceProvider(sk_sp<ResourceProvider>);
200
201 sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
202
203 mutable SkMutex fMutex;
204 mutable SkTHashMap<SkString, sk_sp<ImageAsset>> fImageCache;
205
206 using INHERITED = ResourceProviderProxyBase;
207};
208
209class DataURIResourceProviderProxy final : public ResourceProviderProxyBase {
210public:
211 static sk_sp<DataURIResourceProviderProxy> Make(sk_sp<ResourceProvider> rp,
212 bool predecode = false);
213
214private:
215 DataURIResourceProviderProxy(sk_sp<ResourceProvider>, bool);
216
217 sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
218 sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override;
219
220 const bool fPredecode;
221
222 using INHERITED = ResourceProviderProxyBase;
223};
224
225} // namespace skresources
226
227#endif // SkResources_DEFINED
228