• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     bool isMultiFrame() override;
112 
113     // Animation duration, in ms.
114     float duration() const;
115 
116     sk_sp<SkImage> getFrame(float t) override;
117 
118 private:
119     explicit MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer>, ImageDecodeStrategy);
120 
121     sk_sp<SkImage> generateFrame(float t);
122 
123     std::unique_ptr<SkAnimCodecPlayer> fPlayer;
124     sk_sp<SkImage>                     fCachedFrame;
125     ImageDecodeStrategy fStrategy;
126 
127     using INHERITED = ImageAsset;
128 };
129 
130 /**
131  * External track (e.g. audio playback) interface.
132  *
133  * Used to wrap data payload and playback controllers.
134  */
135 class ExternalTrackAsset : public SkRefCnt {
136 public:
137     /**
138      * Playback control callback, emitted for each corresponding Animation::seek().
139      *
140      * @param t  Frame time code, in seconds, relative to the layer's timeline origin
141      *           (in-point).
142      *
143      * Negative |t| values are used to signal off state (stop playback outside layer span).
144      */
145     virtual void seek(float t) = 0;
146 };
147 
148 /**
149  * ResourceProvider is an interface that lets rich-content modules defer loading of external
150  * resources (images, fonts, etc.) to embedding clients.
151  */
152 class SK_API ResourceProvider : public SkRefCnt {
153 public:
154     /**
155      * Load a generic resource (currently only nested animations) specified by |path| + |name|,
156      * and return as an SkData.
157      */
load(const char[],const char[])158     virtual sk_sp<SkData> load(const char[] /* resource_path */,
159                                const char[] /* resource_name */) const {
160         return nullptr;
161     }
162 
163     /**
164      * Load an image asset specified by |path| + |name|, and returns the corresponding
165      * ImageAsset proxy.
166      */
loadImageAsset(const char[],const char[],const char[])167     virtual sk_sp<ImageAsset> loadImageAsset(const char[] /* resource_path */,
168                                              const char[] /* resource_name */,
169                                              const char[] /* resource_id   */) const {
170         return nullptr;
171     }
172 
173     /**
174      * Load an external audio track specified by |path|/|name|/|id|.
175      */
loadAudioAsset(const char[],const char[],const char[])176     virtual sk_sp<ExternalTrackAsset> loadAudioAsset(const char[] /* resource_path */,
177                                                      const char[] /* resource_name */,
178                                                      const char[] /* resource_id   */) {
179         return nullptr;
180     }
181 
182     /**
183      * DEPRECATED: implement loadTypeface() instead.
184      *
185      * Load an external font and return as SkData.
186      *
187      * @param name  font name    ("fName" Lottie property)
188      * @param url   web font URL ("fPath" Lottie property)
189      *
190      * -- Note --
191      *
192      *   This mechanism assumes monolithic fonts (single data blob).  Some web font providers may
193      *   serve multiple font blobs, segmented for various unicode ranges, depending on user agent
194      *   capabilities (woff, woff2).  In that case, the embedder would need to advertise no user
195      *   agent capabilities when fetching the URL, in order to receive full font data.
196      */
loadFont(const char[],const char[])197     virtual sk_sp<SkData> loadFont(const char[] /* name */,
198                                    const char[] /* url  */) const {
199         return nullptr;
200     }
201 
202     /**
203      * Load an external font and return as SkTypeface.
204      *
205      * @param name  font name
206      * @param url   web font URL
207      */
loadTypeface(const char[],const char[])208     virtual sk_sp<SkTypeface> loadTypeface(const char[] /* name */,
209                                            const char[] /* url  */) const {
210         return nullptr;
211     }
212 };
213 
214 class FileResourceProvider final : public ResourceProvider {
215 public:
216     // To decode images, clients must call SkCodecs::Register() before calling Make.
217     static sk_sp<FileResourceProvider> Make(SkString base_dir,
218                                             ImageDecodeStrategy = ImageDecodeStrategy::kLazyDecode);
219 
220     sk_sp<SkData> load(const char resource_path[], const char resource_name[]) const override;
221 
222     sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
223 
224 private:
225     FileResourceProvider(SkString, ImageDecodeStrategy);
226 
227     const SkString fDir;
228     const ImageDecodeStrategy fStrategy;
229 
230     using INHERITED = ResourceProvider;
231 };
232 
233 class ResourceProviderProxyBase : public ResourceProvider {
234 protected:
235     explicit ResourceProviderProxyBase(sk_sp<ResourceProvider>);
236 
237     sk_sp<SkData> load(const char[], const char[]) const override;
238     sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
239     sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override;
240     sk_sp<SkData> loadFont(const char[], const char[]) const override;
241     sk_sp<ExternalTrackAsset> loadAudioAsset(const char[], const char[], const char[]) override;
242 
243 protected:
244     const sk_sp<ResourceProvider> fProxy;
245 };
246 
247 class SK_API CachingResourceProvider final : public ResourceProviderProxyBase {
248 public:
Make(sk_sp<ResourceProvider> rp)249     static sk_sp<CachingResourceProvider> Make(sk_sp<ResourceProvider> rp) {
250         return rp ? sk_sp<CachingResourceProvider>(new CachingResourceProvider(std::move(rp)))
251                   : nullptr;
252     }
253 
254 private:
255     explicit CachingResourceProvider(sk_sp<ResourceProvider>);
256 
257     sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
258 
259     mutable SkMutex                                             fMutex;
260     mutable skia_private::THashMap<SkString, sk_sp<ImageAsset>> fImageCache;
261 
262     using INHERITED = ResourceProviderProxyBase;
263 };
264 
265 class SK_API DataURIResourceProviderProxy final : public ResourceProviderProxyBase {
266 public:
267     // If font data is supplied via base64 encoding, this needs a provided SkFontMgr to process
268     // that font data into an SkTypeface. To decode images, clients must call SkCodecs::Register()
269     // before calling Make.
270     static sk_sp<DataURIResourceProviderProxy> Make(
271             sk_sp<ResourceProvider> rp,
272             ImageDecodeStrategy = ImageDecodeStrategy::kLazyDecode,
273             sk_sp<const SkFontMgr> fontMgr = nullptr);
274 
275 private:
276     DataURIResourceProviderProxy(sk_sp<ResourceProvider>,
277                                  ImageDecodeStrategy,
278                                  sk_sp<const SkFontMgr> fontMgr);
279 
280     sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
281     sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override;
282 
283     const ImageDecodeStrategy fStrategy;
284     sk_sp<const SkFontMgr> fFontMgr;
285 
286     using INHERITED = ResourceProviderProxyBase;
287 };
288 
289 } // namespace skresources
290 
291 #endif // SkResources_DEFINED
292