xref: /aosp_15_r20/external/skia/modules/skresources/include/SkResources.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkMatrix.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkSamplingOptions.h"
15 #include "include/core/SkString.h"
16 #include "include/core/SkTypeface.h"
17 #include "include/core/SkTypes.h"
18 #include "include/private/base/SkMutex.h"
19 #include "src/core/SkTHash.h"
20 
21 #include <memory>
22 
23 class SkAnimCodecPlayer;
24 class SkCodec;
25 class SkImage;
26 
27 namespace skresources {
28 
29 /**
30  * Image asset proxy interface.
31  */
32 class SK_API ImageAsset : public SkRefCnt {
33 public:
34     /**
35      * Returns true if the image asset is animated.
36      */
37     virtual bool isMultiFrame() = 0;
38 
39     /**
40      * DEPRECATED: override getFrameData() instead.
41      *
42      * Returns the SkImage for a given frame.
43      *
44      * If the image asset is static, getFrame() is only called once, at animation load time.
45      * Otherwise, this gets invoked every time the animation time is adjusted (on every seek).
46      *
47      * Embedders should cache and serve the same SkImage whenever possible, for efficiency.
48      *
49      * @param t   Frame time code, in seconds, relative to the image layer timeline origin
50      *            (in-point).
51      */
52     virtual sk_sp<SkImage> getFrame(float t);
53 
54     // Describes how the frame image is to be scaled to the animation-declared asset size.
55     enum class SizeFit {
56         // See SkMatrix::ScaleToFit
57         kFill   = SkMatrix::kFill_ScaleToFit,
58         kStart  = SkMatrix::kStart_ScaleToFit,
59         kCenter = SkMatrix::kCenter_ScaleToFit,
60         kEnd    = SkMatrix::kEnd_ScaleToFit,
61 
62         // No scaling.
63         kNone,
64     };
65 
66     struct FrameData {
67         // SkImage payload.
68         sk_sp<SkImage>    image;
69         // Resampling parameters.
70         SkSamplingOptions sampling;
71         // Additional image transform to be applied before AE scaling rules.
72         SkMatrix          matrix = SkMatrix::I();
73         // Strategy for image size -> AE asset size scaling.
74         SizeFit           scaling = SizeFit::kCenter;
75     };
76 
77     /**
78      * Returns the payload for a given frame.
79      *
80      * If the image asset is static, getFrameData() is only called once, at animation load time.
81      * Otherwise, this gets invoked every time the animation time is adjusted (on every seek).
82      *
83      * Embedders should cache and serve the same SkImage whenever possible, for efficiency.
84      *
85      * @param t   Frame time code, in seconds, relative to the image layer timeline origin
86      *            (in-point).
87      */
88     virtual FrameData getFrameData(float t);
89 };
90 
91 enum class ImageDecodeStrategy {
92     // Images are decoded on-the-fly, at rasterization time.
93     // Large images may cause jank as decoding is expensive (and can thrash internal caches).
94     kLazyDecode,
95     // Force-decode all images upfront, at the cost of potentially more RAM and slower
96     // animation build times.
97     kPreDecode,
98 };
99 
100 class MultiFrameImageAsset final : public ImageAsset {
101 public:
102     // Clients must call SkCodec::Register() to load the required decoding image codecs before
103     // calling Make. For example:
104     //     SkCodec::Register(SkPngDecoder::Decoder());
105     static sk_sp<MultiFrameImageAsset> Make(sk_sp<SkData>,
106                                             ImageDecodeStrategy = ImageDecodeStrategy::kLazyDecode);
107     // If the client has already decoded the data, they can use this constructor.
108     static sk_sp<MultiFrameImageAsset> Make(std::unique_ptr<SkCodec>,
109                                             ImageDecodeStrategy = ImageDecodeStrategy::kLazyDecode);
110 
111 
112     bool isMultiFrame() override;
113 
114     sk_sp<SkImage> getFrame(float t) override;
115 
116 private:
117     explicit MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer>, ImageDecodeStrategy);
118 
119     sk_sp<SkImage> generateFrame(float t);
120 
121     std::unique_ptr<SkAnimCodecPlayer> fPlayer;
122     sk_sp<SkImage>                     fCachedFrame;
123     ImageDecodeStrategy fStrategy;
124 
125     using INHERITED = ImageAsset;
126 };
127 
128 /**
129  * External track (e.g. audio playback) interface.
130  *
131  * Used to wrap data payload and playback controllers.
132  */
133 class ExternalTrackAsset : public SkRefCnt {
134 public:
135     /**
136      * Playback control callback, emitted for each corresponding Animation::seek().
137      *
138      * @param t  Frame time code, in seconds, relative to the layer's timeline origin
139      *           (in-point).
140      *
141      * Negative |t| values are used to signal off state (stop playback outside layer span).
142      */
143     virtual void seek(float t) = 0;
144 };
145 
146 /**
147  * ResourceProvider is an interface that lets rich-content modules defer loading of external
148  * resources (images, fonts, etc.) to embedding clients.
149  */
150 class SK_API ResourceProvider : public SkRefCnt {
151 public:
152     /**
153      * Load a generic resource (currently only nested animations) specified by |path| + |name|,
154      * and return as an SkData.
155      */
load(const char[],const char[])156     virtual sk_sp<SkData> load(const char[] /* resource_path */,
157                                const char[] /* resource_name */) const {
158         return nullptr;
159     }
160 
161     /**
162      * Load an image asset specified by |path| + |name|, and returns the corresponding
163      * ImageAsset proxy.
164      */
loadImageAsset(const char[],const char[],const char[])165     virtual sk_sp<ImageAsset> loadImageAsset(const char[] /* resource_path */,
166                                              const char[] /* resource_name */,
167                                              const char[] /* resource_id   */) const {
168         return nullptr;
169     }
170 
171     /**
172      * Load an external audio track specified by |path|/|name|/|id|.
173      */
loadAudioAsset(const char[],const char[],const char[])174     virtual sk_sp<ExternalTrackAsset> loadAudioAsset(const char[] /* resource_path */,
175                                                      const char[] /* resource_name */,
176                                                      const char[] /* resource_id   */) {
177         return nullptr;
178     }
179 
180     /**
181      * DEPRECATED: implement loadTypeface() instead.
182      *
183      * Load an external font and return as SkData.
184      *
185      * @param name  font name    ("fName" Lottie property)
186      * @param url   web font URL ("fPath" Lottie property)
187      *
188      * -- Note --
189      *
190      *   This mechanism assumes monolithic fonts (single data blob).  Some web font providers may
191      *   serve multiple font blobs, segmented for various unicode ranges, depending on user agent
192      *   capabilities (woff, woff2).  In that case, the embedder would need to advertise no user
193      *   agent capabilities when fetching the URL, in order to receive full font data.
194      */
loadFont(const char[],const char[])195     virtual sk_sp<SkData> loadFont(const char[] /* name */,
196                                    const char[] /* url  */) const {
197         return nullptr;
198     }
199 
200     /**
201      * Load an external font and return as SkTypeface.
202      *
203      * @param name  font name
204      * @param url   web font URL
205      */
loadTypeface(const char[],const char[])206     virtual sk_sp<SkTypeface> loadTypeface(const char[] /* name */,
207                                            const char[] /* url  */) const {
208         return nullptr;
209     }
210 };
211 
212 class FileResourceProvider final : public ResourceProvider {
213 public:
214     // To decode images, clients must call SkCodecs::Register() before calling Make.
215     static sk_sp<FileResourceProvider> Make(SkString base_dir,
216                                             ImageDecodeStrategy = ImageDecodeStrategy::kLazyDecode);
217 
218     sk_sp<SkData> load(const char resource_path[], const char resource_name[]) const override;
219 
220     sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
221 
222 private:
223     FileResourceProvider(SkString, ImageDecodeStrategy);
224 
225     const SkString fDir;
226     const ImageDecodeStrategy fStrategy;
227 
228     using INHERITED = ResourceProvider;
229 };
230 
231 class ResourceProviderProxyBase : public ResourceProvider {
232 protected:
233     explicit ResourceProviderProxyBase(sk_sp<ResourceProvider>);
234 
235     sk_sp<SkData> load(const char[], const char[]) const override;
236     sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
237     sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override;
238     sk_sp<SkData> loadFont(const char[], const char[]) const override;
239     sk_sp<ExternalTrackAsset> loadAudioAsset(const char[], const char[], const char[]) override;
240 
241 protected:
242     const sk_sp<ResourceProvider> fProxy;
243 };
244 
245 class SK_API CachingResourceProvider final : public ResourceProviderProxyBase {
246 public:
Make(sk_sp<ResourceProvider> rp)247     static sk_sp<CachingResourceProvider> Make(sk_sp<ResourceProvider> rp) {
248         return rp ? sk_sp<CachingResourceProvider>(new CachingResourceProvider(std::move(rp)))
249                   : nullptr;
250     }
251 
252 private:
253     explicit CachingResourceProvider(sk_sp<ResourceProvider>);
254 
255     sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
256 
257     mutable SkMutex                                             fMutex;
258     mutable skia_private::THashMap<SkString, sk_sp<ImageAsset>> fImageCache;
259 
260     using INHERITED = ResourceProviderProxyBase;
261 };
262 
263 class SK_API DataURIResourceProviderProxy final : public ResourceProviderProxyBase {
264 public:
265     // If font data is supplied via base64 encoding, this needs a provided SkFontMgr to process
266     // that font data into an SkTypeface. To decode images, clients must call SkCodecs::Register()
267     // before calling Make.
268     static sk_sp<DataURIResourceProviderProxy> Make(
269             sk_sp<ResourceProvider> rp,
270             ImageDecodeStrategy = ImageDecodeStrategy::kLazyDecode,
271             sk_sp<const SkFontMgr> fontMgr = nullptr);
272 
273 private:
274     DataURIResourceProviderProxy(sk_sp<ResourceProvider>,
275                                  ImageDecodeStrategy,
276                                  sk_sp<const SkFontMgr> fontMgr);
277 
278     sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
279     sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override;
280 
281     const ImageDecodeStrategy fStrategy;
282     sk_sp<const SkFontMgr> fFontMgr;
283 
284     using INHERITED = ResourceProviderProxyBase;
285 };
286 
287 } // namespace skresources
288 
289 #endif // SkResources_DEFINED
290