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