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