@amazon-devices/expo-image 2.0.1758683737
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +2 -0
- package/CHANGELOG.md +285 -0
- package/CMakeLists.txt +91 -0
- package/LICENSE +7 -0
- package/README.kepler.md +123 -0
- package/README.md +54 -0
- package/THIRD_PARTY_NOTICES.txt +24 -0
- package/android/build.gradle +124 -0
- package/android/proguard-rules.pro +28 -0
- package/android/src/main/AndroidManifest.xml +15 -0
- package/android/src/main/java/expo/modules/image/Exceptions.kt +9 -0
- package/android/src/main/java/expo/modules/image/ExpoImageAppGlideModule.kt +19 -0
- package/android/src/main/java/expo/modules/image/ExpoImageModule.kt +192 -0
- package/android/src/main/java/expo/modules/image/ExpoImageView.kt +220 -0
- package/android/src/main/java/expo/modules/image/ExpoImageViewWrapper.kt +652 -0
- package/android/src/main/java/expo/modules/image/GlideModel.kt +73 -0
- package/android/src/main/java/expo/modules/image/ImageUtils.kt +50 -0
- package/android/src/main/java/expo/modules/image/ImageViewWrapperTarget.kt +313 -0
- package/android/src/main/java/expo/modules/image/ResourceIdHelper.kt +47 -0
- package/android/src/main/java/expo/modules/image/ThumbnailRequestCoordinatorExtension.kt +21 -0
- package/android/src/main/java/expo/modules/image/YogaUtils.kt +20 -0
- package/android/src/main/java/expo/modules/image/blurhash/BlurHashFetcher.kt +32 -0
- package/android/src/main/java/expo/modules/image/blurhash/BlurhashDecoder.kt +189 -0
- package/android/src/main/java/expo/modules/image/blurhash/BlurhashModelLoader.kt +30 -0
- package/android/src/main/java/expo/modules/image/blurhash/BlurhashModelLoaderFactory.kt +14 -0
- package/android/src/main/java/expo/modules/image/blurhash/BlurhashModule.kt +17 -0
- package/android/src/main/java/expo/modules/image/dataurls/Base64DataFetcher.kt +27 -0
- package/android/src/main/java/expo/modules/image/dataurls/Base64ModelLoader.kt +24 -0
- package/android/src/main/java/expo/modules/image/dataurls/Base64ModelLoaderFactory.kt +12 -0
- package/android/src/main/java/expo/modules/image/dataurls/Base64Module.kt +16 -0
- package/android/src/main/java/expo/modules/image/drawing/OutlineProvider.kt +201 -0
- package/android/src/main/java/expo/modules/image/enums/ContentFit.kt +89 -0
- package/android/src/main/java/expo/modules/image/enums/ImageCacheType.kt +14 -0
- package/android/src/main/java/expo/modules/image/enums/Priority.kt +15 -0
- package/android/src/main/java/expo/modules/image/events/GlideRequestListener.kt +64 -0
- package/android/src/main/java/expo/modules/image/events/OkHttpProgressListener.kt +26 -0
- package/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt +80 -0
- package/android/src/main/java/expo/modules/image/okhttp/GlideUrlWrapperLoader.kt +55 -0
- package/android/src/main/java/expo/modules/image/records/CachePolicy.kt +10 -0
- package/android/src/main/java/expo/modules/image/records/ContentPosition.kt +89 -0
- package/android/src/main/java/expo/modules/image/records/ImageTransition.kt +8 -0
- package/android/src/main/java/expo/modules/image/records/SourceMap.kt +160 -0
- package/android/src/main/java/expo/modules/image/records/events.kt +25 -0
- package/android/src/main/java/expo/modules/image/svg/SVGDecoder.kt +33 -0
- package/android/src/main/java/expo/modules/image/svg/SVGDrawableTranscoder.kt +37 -0
- package/android/src/main/java/expo/modules/image/svg/SVGModule.kt +26 -0
- package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashDecoder.kt +365 -0
- package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashFetcher.kt +31 -0
- package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModelLoader.kt +26 -0
- package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModelLoaderFactory.kt +14 -0
- package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModule.kt +17 -0
- package/build/ExpoImage.d.ts +15 -0
- package/build/ExpoImage.d.ts.map +1 -0
- package/build/ExpoImage.js +75 -0
- package/build/ExpoImage.js.map +1 -0
- package/build/ExpoImage.kepler.d.ts +23 -0
- package/build/ExpoImage.kepler.d.ts.map +1 -0
- package/build/ExpoImage.kepler.js +71 -0
- package/build/ExpoImage.kepler.js.map +1 -0
- package/build/ExpoImage.web.d.ts +9 -0
- package/build/ExpoImage.web.d.ts.map +1 -0
- package/build/ExpoImage.web.js +90 -0
- package/build/ExpoImage.web.js.map +1 -0
- package/build/ExpoImageModules.d.ts +3 -0
- package/build/ExpoImageModules.d.ts.map +1 -0
- package/build/ExpoImageModules.js +4 -0
- package/build/ExpoImageModules.js.map +1 -0
- package/build/ExpoImageModules.kepler.d.ts +6 -0
- package/build/ExpoImageModules.kepler.d.ts.map +1 -0
- package/build/ExpoImageModules.kepler.js +5 -0
- package/build/ExpoImageModules.kepler.js.map +1 -0
- package/build/Image.d.ts +30 -0
- package/build/Image.d.ts.map +1 -0
- package/build/Image.js +49 -0
- package/build/Image.js.map +1 -0
- package/build/Image.types.d.ts +374 -0
- package/build/Image.types.d.ts.map +1 -0
- package/build/Image.types.js +2 -0
- package/build/Image.types.js.map +1 -0
- package/build/ImageBackground.d.ts +6 -0
- package/build/ImageBackground.d.ts.map +1 -0
- package/build/ImageBackground.js +13 -0
- package/build/ImageBackground.js.map +1 -0
- package/build/ImageViewNativeComponent.d.ts +20 -0
- package/build/ImageViewNativeComponent.d.ts.map +1 -0
- package/build/ImageViewNativeComponent.js +42 -0
- package/build/ImageViewNativeComponent.js.map +1 -0
- package/build/NativeImageLoaderKepler.d.ts +18 -0
- package/build/NativeImageLoaderKepler.d.ts.map +1 -0
- package/build/NativeImageLoaderKepler.js +3 -0
- package/build/NativeImageLoaderKepler.js.map +1 -0
- package/build/fabric/registerNativeComponent.d.ts +15 -0
- package/build/fabric/registerNativeComponent.d.ts.map +1 -0
- package/build/fabric/registerNativeComponent.js +47 -0
- package/build/fabric/registerNativeComponent.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +4 -0
- package/build/index.js.map +1 -0
- package/build/utils/AssetSourceResolver.web.d.ts +24 -0
- package/build/utils/AssetSourceResolver.web.d.ts.map +1 -0
- package/build/utils/AssetSourceResolver.web.js +67 -0
- package/build/utils/AssetSourceResolver.web.js.map +1 -0
- package/build/utils/blurhash/base83.d.ts +3 -0
- package/build/utils/blurhash/base83.d.ts.map +1 -0
- package/build/utils/blurhash/base83.js +103 -0
- package/build/utils/blurhash/base83.js.map +1 -0
- package/build/utils/blurhash/decode.d.ts +7 -0
- package/build/utils/blurhash/decode.d.ts.map +1 -0
- package/build/utils/blurhash/decode.js +92 -0
- package/build/utils/blurhash/decode.js.map +1 -0
- package/build/utils/blurhash/error.d.ts +4 -0
- package/build/utils/blurhash/error.d.ts.map +1 -0
- package/build/utils/blurhash/error.js +8 -0
- package/build/utils/blurhash/error.js.map +1 -0
- package/build/utils/blurhash/useBlurhash.d.ts +8 -0
- package/build/utils/blurhash/useBlurhash.d.ts.map +1 -0
- package/build/utils/blurhash/useBlurhash.js +64 -0
- package/build/utils/blurhash/useBlurhash.js.map +1 -0
- package/build/utils/blurhash/utils.d.ts +5 -0
- package/build/utils/blurhash/utils.d.ts.map +1 -0
- package/build/utils/blurhash/utils.js +21 -0
- package/build/utils/blurhash/utils.js.map +1 -0
- package/build/utils/resolveAssetSource.d.ts +3 -0
- package/build/utils/resolveAssetSource.d.ts.map +1 -0
- package/build/utils/resolveAssetSource.js +3 -0
- package/build/utils/resolveAssetSource.js.map +1 -0
- package/build/utils/resolveAssetSource.web.d.ts +9 -0
- package/build/utils/resolveAssetSource.web.d.ts.map +1 -0
- package/build/utils/resolveAssetSource.web.js +31 -0
- package/build/utils/resolveAssetSource.web.js.map +1 -0
- package/build/utils/resolveHashString.d.ts +14 -0
- package/build/utils/resolveHashString.d.ts.map +1 -0
- package/build/utils/resolveHashString.js +31 -0
- package/build/utils/resolveHashString.js.map +1 -0
- package/build/utils/resolveHashString.web.d.ts +20 -0
- package/build/utils/resolveHashString.web.d.ts.map +1 -0
- package/build/utils/resolveHashString.web.js +31 -0
- package/build/utils/resolveHashString.web.js.map +1 -0
- package/build/utils/resolveSources.d.ts +8 -0
- package/build/utils/resolveSources.d.ts.map +1 -0
- package/build/utils/resolveSources.js +45 -0
- package/build/utils/resolveSources.js.map +1 -0
- package/build/utils/thumbhash/thumbhash.d.ts +66 -0
- package/build/utils/thumbhash/thumbhash.d.ts.map +1 -0
- package/build/utils/thumbhash/thumbhash.js +338 -0
- package/build/utils/thumbhash/thumbhash.js.map +1 -0
- package/build/utils.d.ts +17 -0
- package/build/utils.d.ts.map +1 -0
- package/build/utils.js +85 -0
- package/build/utils.js.map +1 -0
- package/build/web/AnimationManager.d.ts +37 -0
- package/build/web/AnimationManager.d.ts.map +1 -0
- package/build/web/AnimationManager.js +155 -0
- package/build/web/AnimationManager.js.map +1 -0
- package/build/web/ColorTintFilter.d.ts +12 -0
- package/build/web/ColorTintFilter.d.ts.map +1 -0
- package/build/web/ColorTintFilter.js +30 -0
- package/build/web/ColorTintFilter.js.map +1 -0
- package/build/web/ImageWrapper.d.ts +5 -0
- package/build/web/ImageWrapper.d.ts.map +1 -0
- package/build/web/ImageWrapper.js +44 -0
- package/build/web/ImageWrapper.js.map +1 -0
- package/build/web/ImageWrapper.types.d.ts +29 -0
- package/build/web/ImageWrapper.types.d.ts.map +1 -0
- package/build/web/ImageWrapper.types.js +2 -0
- package/build/web/ImageWrapper.types.js.map +1 -0
- package/build/web/getImageWrapperEventHandler.d.ts +8 -0
- package/build/web/getImageWrapperEventHandler.d.ts.map +1 -0
- package/build/web/getImageWrapperEventHandler.js +25 -0
- package/build/web/getImageWrapperEventHandler.js.map +1 -0
- package/build/web/hooks.d.ts +13 -0
- package/build/web/hooks.d.ts.map +1 -0
- package/build/web/hooks.js +58 -0
- package/build/web/hooks.js.map +1 -0
- package/build/web/imageStyles.d.ts +2 -0
- package/build/web/imageStyles.d.ts.map +1 -0
- package/build/web/imageStyles.js +137 -0
- package/build/web/imageStyles.js.map +1 -0
- package/build/web/positioning.d.ts +11 -0
- package/build/web/positioning.d.ts.map +1 -0
- package/build/web/positioning.js +35 -0
- package/build/web/positioning.js.map +1 -0
- package/build/web/useSourceSelection.d.ts +14 -0
- package/build/web/useSourceSelection.d.ts.map +1 -0
- package/build/web/useSourceSelection.js +98 -0
- package/build/web/useSourceSelection.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/Blurhash.swift +326 -0
- package/ios/BlurhashLoader.swift +52 -0
- package/ios/ContentFit.swift +55 -0
- package/ios/ContentPosition.swift +108 -0
- package/ios/ExpoImage.podspec +30 -0
- package/ios/ImageCachePolicy.swift +24 -0
- package/ios/ImageCacheType.swift +21 -0
- package/ios/ImageModule.swift +127 -0
- package/ios/ImagePriority.swift +22 -0
- package/ios/ImageSource.swift +44 -0
- package/ios/ImageTransition.swift +67 -0
- package/ios/ImageUtils.swift +227 -0
- package/ios/ImageView.swift +469 -0
- package/ios/Loaders/PhotoLibraryAssetLoader.swift +146 -0
- package/ios/Thumbhash.swift +550 -0
- package/ios/ThumbhashLoader.swift +53 -0
- package/jest.config.js +3 -0
- package/kepler/ExpoImage.cpp +63 -0
- package/kepler/ExpoImage.h +29 -0
- package/kepler/ExpoImageCacheHandler.cpp +15 -0
- package/kepler/ExpoImageCacheHandler.h +28 -0
- package/kepler/ExpoImageComponent.cpp +579 -0
- package/kepler/ExpoImageComponent.h +199 -0
- package/kepler/ImageWrapper/BlurhashImageWrapper.cpp +37 -0
- package/kepler/ImageWrapper/BlurhashImageWrapper.h +23 -0
- package/kepler/ImageWrapper/CacheImageWrapper.cpp +80 -0
- package/kepler/ImageWrapper/CacheImageWrapper.h +41 -0
- package/kepler/ImageWrapper/HashImageWrapper.cpp +21 -0
- package/kepler/ImageWrapper/HashImageWrapper.h +22 -0
- package/kepler/ImageWrapper/ImageWrapper.cpp +24 -0
- package/kepler/ImageWrapper/ImageWrapper.h +33 -0
- package/kepler/ImageWrapper/LocalImageWrapper.cpp +61 -0
- package/kepler/ImageWrapper/LocalImageWrapper.h +30 -0
- package/kepler/ImageWrapper/ThumbhashImageWrapper.cpp +33 -0
- package/kepler/ImageWrapper/ThumbhashImageWrapper.h +20 -0
- package/kepler/KeplerImageViewComponent.h +64 -0
- package/kepler/KeplerInit.cpp +35 -0
- package/kepler/blurhash/common.h +32 -0
- package/kepler/blurhash/decode.cpp +187 -0
- package/kepler/blurhash/decode.h +58 -0
- package/kepler/contentposition/ImageContentPosition.cpp +86 -0
- package/kepler/contentposition/ImageContentPosition.h +48 -0
- package/kepler/enums/ImageContentFit.h +17 -0
- package/kepler/enums/ImagePriority.h +17 -0
- package/kepler/managers/ExpoImageManager.cpp +38 -0
- package/kepler/managers/ExpoImageManager.h +23 -0
- package/kepler/test/ExpoImage.cpp +73 -0
- package/kepler/test/ExpoImageComponent.cpp +290 -0
- package/kepler/test/blurhash/decode.cpp +39 -0
- package/kepler/test/contentposition/ImageContentPosition.cpp +100 -0
- package/kepler/test/managers/ExpoImageManager.cpp +39 -0
- package/kepler/test/thumbhash/decode.cpp +18 -0
- package/kepler/test/utils/LoaderDelegate.cpp +130 -0
- package/kepler/thumbhash/decode.cpp +166 -0
- package/kepler/thumbhash/decode.h +22 -0
- package/kepler/utils/PlaceholderImageLoaderDelegate.cpp +26 -0
- package/kepler/utils/PlaceholderImageLoaderDelegate.h +26 -0
- package/kepler/utils/PrefetchImageLoaderDelegate.cpp +55 -0
- package/kepler/utils/PrefetchImageLoaderDelegate.h +35 -0
- package/kepler/utils/SourceImageLoaderDelegate.cpp +47 -0
- package/kepler/utils/SourceImageLoaderDelegate.h +42 -0
- package/kepler/utils/SourceImageLoaderDelegateEmitter.cpp +15 -0
- package/kepler/utils/SourceImageLoaderDelegateEmitter.h +28 -0
- package/kepler-compatibility.json +8 -0
- package/package.json +1 -0
- package/react-native.config.js +22 -0
- package/src/ExpoImage.kepler.tsx +119 -0
- package/src/ExpoImage.tsx +119 -0
- package/src/ExpoImage.web.tsx +155 -0
- package/src/ExpoImageModules.kepler.ts +7 -0
- package/src/ExpoImageModules.ts +4 -0
- package/src/Image.tsx +82 -0
- package/src/Image.types.ts +450 -0
- package/src/ImageBackground.tsx +18 -0
- package/src/ImageViewNativeComponent.ts +59 -0
- package/src/NativeImageLoaderKepler.ts +22 -0
- package/src/fabric/registerNativeComponent.ts +70 -0
- package/src/index.ts +3 -0
- package/src/utils/AssetSourceResolver.web.ts +88 -0
- package/src/utils/blurhash/base83.ts +104 -0
- package/src/utils/blurhash/decode.ts +114 -0
- package/src/utils/blurhash/error.ts +7 -0
- package/src/utils/blurhash/useBlurhash.tsx +81 -0
- package/src/utils/blurhash/utils.ts +21 -0
- package/src/utils/resolveAssetSource.tsx +3 -0
- package/src/utils/resolveAssetSource.web.ts +40 -0
- package/src/utils/resolveHashString.tsx +36 -0
- package/src/utils/resolveHashString.web.tsx +33 -0
- package/src/utils/resolveSources.tsx +48 -0
- package/src/utils/thumbhash/thumbhash.ts +387 -0
- package/src/utils.ts +109 -0
- package/src/web/AnimationManager.tsx +224 -0
- package/src/web/ColorTintFilter.tsx +36 -0
- package/src/web/ImageWrapper.tsx +85 -0
- package/src/web/ImageWrapper.types.ts +36 -0
- package/src/web/getImageWrapperEventHandler.ts +29 -0
- package/src/web/hooks.ts +72 -0
- package/src/web/imageStyles.tsx +136 -0
- package/src/web/positioning.ts +48 -0
- package/src/web/useSourceSelection.ts +151 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Copyright 2022-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesCore
|
|
4
|
+
import SDWebImage
|
|
5
|
+
import SDWebImageWebPCoder
|
|
6
|
+
import SDWebImageAVIFCoder
|
|
7
|
+
import SDWebImageSVGCoder
|
|
8
|
+
|
|
9
|
+
public final class ImageModule: Module {
|
|
10
|
+
lazy var prefetcher = SDWebImagePrefetcher.shared
|
|
11
|
+
|
|
12
|
+
public func definition() -> ModuleDefinition {
|
|
13
|
+
Name("ExpoImage")
|
|
14
|
+
|
|
15
|
+
OnCreate {
|
|
16
|
+
ImageModule.registerCoders()
|
|
17
|
+
ImageModule.registerLoaders()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
View(ImageView.self) {
|
|
21
|
+
Events(
|
|
22
|
+
"onLoadStart",
|
|
23
|
+
"onProgress",
|
|
24
|
+
"onError",
|
|
25
|
+
"onLoad"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
Prop("source") { (view, sources: [ImageSource]?) in
|
|
29
|
+
view.sources = sources
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Prop("placeholder") { (view, placeholders: [ImageSource]?) in
|
|
33
|
+
view.placeholderSources = placeholders ?? []
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
Prop("contentFit") { (view, contentFit: ContentFit?) in
|
|
37
|
+
view.contentFit = contentFit ?? .cover
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Prop("placeholderContentFit") { (view, placeholderContentFit: ContentFit?) in
|
|
41
|
+
view.placeholderContentFit = placeholderContentFit ?? .scaleDown
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Prop("contentPosition") { (view, contentPosition: ContentPosition?) in
|
|
45
|
+
view.contentPosition = contentPosition ?? .center
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Prop("transition") { (view, transition: ImageTransition?) in
|
|
49
|
+
view.transition = transition
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Prop("blurRadius") { (view, blurRadius: Double?) in
|
|
53
|
+
view.blurRadius = blurRadius ?? .zero
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
Prop("tintColor") { (view, tintColor: UIColor?) in
|
|
57
|
+
view.imageTintColor = tintColor
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Prop("priority") { (view, priority: ImagePriority?) in
|
|
61
|
+
view.loadingOptions.remove([.lowPriority, .highPriority])
|
|
62
|
+
|
|
63
|
+
if let priority = priority?.toSDWebImageOptions() {
|
|
64
|
+
view.loadingOptions.insert(priority)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Prop("cachePolicy") { (view, cachePolicy: ImageCachePolicy?) in
|
|
69
|
+
view.cachePolicy = cachePolicy ?? .disk
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Prop("enableLiveTextInteraction") { (view, enableLiveTextInteraction: Bool?) in
|
|
73
|
+
view.enableLiveTextInteraction = enableLiveTextInteraction ?? false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Prop("accessible") { (view, accessible: Bool?) in
|
|
77
|
+
view.sdImageView.isAccessibilityElement = accessible ?? false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Prop("accessibilityLabel") { (view, label: String?) in
|
|
81
|
+
view.sdImageView.accessibilityLabel = label
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Prop("recyclingKey") { (view, key: String?) in
|
|
85
|
+
view.recyclingKey = key
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
OnViewDidUpdateProps { view in
|
|
89
|
+
view.reload()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Function("prefetch") { (urls: [URL]) in
|
|
94
|
+
SDWebImagePrefetcher.shared.prefetchURLs(urls)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
AsyncFunction("clearMemoryCache") { () -> Bool in
|
|
98
|
+
SDImageCache.shared.clearMemory()
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
AsyncFunction("clearDiskCache") { (promise: Promise) in
|
|
103
|
+
SDImageCache.shared.clearDisk {
|
|
104
|
+
promise.resolve(true)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static func registerCoders() {
|
|
110
|
+
if #available(iOS 14.0, *) {
|
|
111
|
+
// By default Animated WebP is not supported
|
|
112
|
+
SDImageCodersManager.shared.addCoder(SDImageAWebPCoder.shared)
|
|
113
|
+
} else {
|
|
114
|
+
// This coder is much slower, but it's the only one that works in iOS 13
|
|
115
|
+
SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared)
|
|
116
|
+
}
|
|
117
|
+
SDImageCodersManager.shared.addCoder(SDImageAVIFCoder.shared)
|
|
118
|
+
SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared)
|
|
119
|
+
SDImageCodersManager.shared.addCoder(SDImageHEICCoder.shared)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static func registerLoaders() {
|
|
123
|
+
SDImageLoadersManager.shared.addLoader(BlurhashLoader())
|
|
124
|
+
SDImageLoadersManager.shared.addLoader(ThumbhashLoader())
|
|
125
|
+
SDImageLoadersManager.shared.addLoader(PhotoLibraryAssetLoader())
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import SDWebImage
|
|
3
|
+
|
|
4
|
+
enum ImagePriority: String, Enumerable {
|
|
5
|
+
case low
|
|
6
|
+
case normal
|
|
7
|
+
case high
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
Maps the priority to `SDWebImageOptions` which is a bitmask thus has only low and high priority options.
|
|
11
|
+
*/
|
|
12
|
+
func toSDWebImageOptions() -> SDWebImageOptions? {
|
|
13
|
+
switch self {
|
|
14
|
+
case .low:
|
|
15
|
+
return .lowPriority
|
|
16
|
+
case .high:
|
|
17
|
+
return .highPriority
|
|
18
|
+
default:
|
|
19
|
+
return nil
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Copyright 2022-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesCore
|
|
4
|
+
|
|
5
|
+
struct ImageSource: Record {
|
|
6
|
+
@Field
|
|
7
|
+
var width: Double = 0.0
|
|
8
|
+
|
|
9
|
+
@Field
|
|
10
|
+
var height: Double = 0.0
|
|
11
|
+
|
|
12
|
+
@Field
|
|
13
|
+
var uri: URL? = nil
|
|
14
|
+
|
|
15
|
+
@Field
|
|
16
|
+
var scale: Double = 1.0
|
|
17
|
+
|
|
18
|
+
@Field
|
|
19
|
+
var headers: [String: String]?
|
|
20
|
+
|
|
21
|
+
@Field
|
|
22
|
+
var cacheKey: String?
|
|
23
|
+
|
|
24
|
+
var pixelCount: Double {
|
|
25
|
+
return width * height * scale * scale
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var isBlurhash: Bool {
|
|
29
|
+
return uri?.scheme == "blurhash"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var isThumbhash: Bool {
|
|
33
|
+
return uri?.scheme == "thumbhash"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var isPhotoLibraryAsset: Bool {
|
|
37
|
+
return isPhotoLibraryAssetUrl(uri)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var isCachingAllowed: Bool {
|
|
41
|
+
// TODO: Don't cache other non-network requests (e.g. data URIs, local files)
|
|
42
|
+
return !isPhotoLibraryAsset
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Copyright 2022-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesCore
|
|
4
|
+
|
|
5
|
+
enum ImageTransitionTiming: String, Enumerable {
|
|
6
|
+
case easeInOut = "ease-in-out"
|
|
7
|
+
case easeIn = "ease-in"
|
|
8
|
+
case easeOut = "ease-out"
|
|
9
|
+
case linear = "linear"
|
|
10
|
+
|
|
11
|
+
func toAnimationOption() -> UIView.AnimationOptions {
|
|
12
|
+
switch self {
|
|
13
|
+
case .easeInOut:
|
|
14
|
+
return .curveEaseInOut
|
|
15
|
+
case .easeIn:
|
|
16
|
+
return .curveEaseIn
|
|
17
|
+
case .easeOut:
|
|
18
|
+
return .curveEaseOut
|
|
19
|
+
case .linear:
|
|
20
|
+
return .curveLinear
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
enum ImageTransitionEffect: String, Enumerable {
|
|
26
|
+
case crossDissolve = "cross-dissolve"
|
|
27
|
+
case flipFromTop = "flip-from-top"
|
|
28
|
+
case flipFromRight = "flip-from-right"
|
|
29
|
+
case flipFromBottom = "flip-from-bottom"
|
|
30
|
+
case flipFromLeft = "flip-from-left"
|
|
31
|
+
case curlUp = "curl-up"
|
|
32
|
+
case curlDown = "curl-down"
|
|
33
|
+
|
|
34
|
+
func toAnimationOption() -> UIView.AnimationOptions {
|
|
35
|
+
switch self {
|
|
36
|
+
case .crossDissolve:
|
|
37
|
+
return .transitionCrossDissolve
|
|
38
|
+
case .flipFromLeft:
|
|
39
|
+
return .transitionFlipFromLeft
|
|
40
|
+
case .flipFromRight:
|
|
41
|
+
return .transitionFlipFromRight
|
|
42
|
+
case .flipFromTop:
|
|
43
|
+
return .transitionFlipFromTop
|
|
44
|
+
case .flipFromBottom:
|
|
45
|
+
return .transitionFlipFromBottom
|
|
46
|
+
case .curlUp:
|
|
47
|
+
return .transitionCurlUp
|
|
48
|
+
case .curlDown:
|
|
49
|
+
return .transitionCurlDown
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
struct ImageTransition: Record {
|
|
55
|
+
@Field
|
|
56
|
+
var duration: Double = 100
|
|
57
|
+
|
|
58
|
+
@Field
|
|
59
|
+
var timing: ImageTransitionTiming = .easeInOut
|
|
60
|
+
|
|
61
|
+
@Field
|
|
62
|
+
var effect: ImageTransitionEffect = .crossDissolve
|
|
63
|
+
|
|
64
|
+
func toAnimationOptions() -> UIView.AnimationOptions {
|
|
65
|
+
return [timing.toAnimationOption(), effect.toAnimationOption()]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// Copyright 2022-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import SDWebImage
|
|
4
|
+
import ExpoModulesCore
|
|
5
|
+
|
|
6
|
+
func cacheTypeToString(_ cacheType: SDImageCacheType) -> String {
|
|
7
|
+
switch cacheType {
|
|
8
|
+
case .none:
|
|
9
|
+
return "none"
|
|
10
|
+
case .disk:
|
|
11
|
+
return "disk"
|
|
12
|
+
case .memory, .all:
|
|
13
|
+
// `all` doesn't make much sense, so we treat it as `memory`.
|
|
14
|
+
return "memory"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func imageFormatToMediaType(_ format: SDImageFormat) -> String? {
|
|
19
|
+
switch format {
|
|
20
|
+
case .undefined:
|
|
21
|
+
return nil
|
|
22
|
+
case .JPEG:
|
|
23
|
+
return "image/jpeg"
|
|
24
|
+
case .PNG:
|
|
25
|
+
return "image/png"
|
|
26
|
+
case .GIF:
|
|
27
|
+
return "image/gif"
|
|
28
|
+
case .TIFF:
|
|
29
|
+
return "image/tiff"
|
|
30
|
+
case .webP:
|
|
31
|
+
return "image/webp"
|
|
32
|
+
case .HEIC:
|
|
33
|
+
return "image/heic"
|
|
34
|
+
case .HEIF:
|
|
35
|
+
return "image/heif"
|
|
36
|
+
case .PDF:
|
|
37
|
+
return "application/pdf"
|
|
38
|
+
case .SVG:
|
|
39
|
+
return "image/svg+xml"
|
|
40
|
+
default:
|
|
41
|
+
// On one hand we could remove this clause and always ensure that we have handled
|
|
42
|
+
// all supported formats (by erroring compilation otherwise).
|
|
43
|
+
// On the other hand, we do support overriding SDWebImage version,
|
|
44
|
+
// so we shouldn't fail to compile on SDWebImage versions with.
|
|
45
|
+
return nil
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
Calculates the ideal size that fills in the container size while maintaining the source aspect ratio.
|
|
51
|
+
*/
|
|
52
|
+
func idealSize(contentPixelSize: CGSize, containerSize: CGSize, scale: Double, contentFit: ContentFit) -> CGSize {
|
|
53
|
+
switch contentFit {
|
|
54
|
+
case .contain:
|
|
55
|
+
let aspectRatio = min(containerSize.width / contentPixelSize.width, containerSize.height / contentPixelSize.height)
|
|
56
|
+
return contentPixelSize * aspectRatio
|
|
57
|
+
case .cover:
|
|
58
|
+
let aspectRatio = max(containerSize.width / contentPixelSize.width, containerSize.height / contentPixelSize.height)
|
|
59
|
+
return contentPixelSize * aspectRatio
|
|
60
|
+
case .fill:
|
|
61
|
+
return containerSize
|
|
62
|
+
case .scaleDown:
|
|
63
|
+
if containerSize.width < contentPixelSize.width / scale || containerSize.height < contentPixelSize.height / scale {
|
|
64
|
+
// The container is smaller than the image — scale it down and behave like `contain`
|
|
65
|
+
let aspectRatio = min(containerSize.width / contentPixelSize.width, containerSize.height / contentPixelSize.height)
|
|
66
|
+
return contentPixelSize * aspectRatio
|
|
67
|
+
} else {
|
|
68
|
+
// The container is bigger than the image — don't scale it and behave like `none`
|
|
69
|
+
return contentPixelSize / scale
|
|
70
|
+
}
|
|
71
|
+
case .none:
|
|
72
|
+
return contentPixelSize / scale
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
Returns a bool whether the image should be downscaled to the given size.
|
|
78
|
+
*/
|
|
79
|
+
func shouldDownscale(image: UIImage, toSize size: CGSize, scale: Double) -> Bool {
|
|
80
|
+
if size.width <= 0 || size.height <= 0 {
|
|
81
|
+
// View is invisible, so no reason to keep the image in memory.
|
|
82
|
+
// This already ensures that we won't be diving by zero in ratio calculations.
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
if size.width.isInfinite || size.height.isInfinite {
|
|
86
|
+
// Keep the image unscaled for infinite sizes.
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
let imageSize = image.size * image.scale
|
|
90
|
+
return imageSize.width > (size.width * scale) && imageSize.height > (size.height * scale)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
Resizes the animated image to fit in the given size and scale.
|
|
95
|
+
*/
|
|
96
|
+
func resize(animatedImage image: UIImage, toSize size: CGSize, scale: Double) async -> UIImage {
|
|
97
|
+
// For animated images, the `images` member is non-nil and represents an array of animation frames.
|
|
98
|
+
if let images = image.images {
|
|
99
|
+
// Resize each animation frame separately.
|
|
100
|
+
let resizedImages = await concurrentMap(images) { image in
|
|
101
|
+
return resize(image: image, toSize: size, scale: scale)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// `animatedImage(with:duration:)` can return `nil`, probably when scales are not the same
|
|
105
|
+
// so it should never happen in our case, but let's handle it gracefully.
|
|
106
|
+
if let animatedImage = UIImage.animatedImage(with: resizedImages, duration: image.duration) {
|
|
107
|
+
return animatedImage
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return resize(image: image, toSize: size, scale: scale)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
Resizes a still image to fit in the given size and scale.
|
|
115
|
+
*/
|
|
116
|
+
func resize(image: UIImage, toSize size: CGSize, scale: Double) -> UIImage {
|
|
117
|
+
let format = UIGraphicsImageRendererFormat()
|
|
118
|
+
format.scale = scale
|
|
119
|
+
|
|
120
|
+
return UIGraphicsImageRenderer(size: size, format: format).image { _ in
|
|
121
|
+
image.draw(in: CGRect(origin: .zero, size: size))
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
The image source that fits best into the given size, that is the one with the closest number of pixels.
|
|
127
|
+
May be `nil` if there are no sources available or the size is zero.
|
|
128
|
+
*/
|
|
129
|
+
func getBestSource(from sources: [ImageSource]?, forSize size: CGSize, scale: Double = 1.0) -> ImageSource? {
|
|
130
|
+
guard let sources = sources, !sources.isEmpty else {
|
|
131
|
+
return nil
|
|
132
|
+
}
|
|
133
|
+
if size.width <= 0 || size.height <= 0 {
|
|
134
|
+
return nil
|
|
135
|
+
}
|
|
136
|
+
if sources.count == 1 {
|
|
137
|
+
return sources.first
|
|
138
|
+
}
|
|
139
|
+
var bestSource: ImageSource?
|
|
140
|
+
var bestFit = Double.infinity
|
|
141
|
+
let targetPixelCount = size.width * size.height * scale * scale
|
|
142
|
+
|
|
143
|
+
for source in sources {
|
|
144
|
+
let fit = abs(1 - (source.pixelCount / targetPixelCount))
|
|
145
|
+
|
|
146
|
+
if fit < bestFit {
|
|
147
|
+
bestSource = source
|
|
148
|
+
bestFit = fit
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return bestSource
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
Creates the cache key filter that returns the specific string.
|
|
156
|
+
*/
|
|
157
|
+
func createCacheKeyFilter(_ cacheKey: String?) -> SDWebImageCacheKeyFilter? {
|
|
158
|
+
guard let cacheKey = cacheKey else {
|
|
159
|
+
return nil
|
|
160
|
+
}
|
|
161
|
+
return SDWebImageCacheKeyFilter { _ in
|
|
162
|
+
return cacheKey
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
extension CGSize {
|
|
167
|
+
/**
|
|
168
|
+
Multiplies a size with a scalar.
|
|
169
|
+
*/
|
|
170
|
+
static func * (size: CGSize, scalar: Double) -> CGSize {
|
|
171
|
+
return CGSize(width: size.width * scalar, height: size.height * scalar)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
Divides a size with a scalar.
|
|
176
|
+
*/
|
|
177
|
+
static func / (size: CGSize, scalar: Double) -> CGSize {
|
|
178
|
+
return CGSize(width: size.width / scalar, height: size.height / scalar)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
Returns a new CGSize with width and height rounded to an integral value using the specified rounding rule.
|
|
183
|
+
*/
|
|
184
|
+
func rounded(_ rule: FloatingPointRoundingRule) -> CGSize {
|
|
185
|
+
return CGSize(width: width.rounded(rule), height: height.rounded(rule))
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
func makeNSError(description: String) -> NSError {
|
|
190
|
+
let userInfo = [NSLocalizedDescriptionKey: description]
|
|
191
|
+
return NSError(domain: "expo.modules.image", code: 0, userInfo: userInfo)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// MARK: - Async helpers
|
|
195
|
+
// TODO: Add helpers like these to the modules core eventually
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
Asynchronously maps the given sequence (sequentially).
|
|
199
|
+
*/
|
|
200
|
+
func asyncMap<ItemsType: Sequence, ResultType>(
|
|
201
|
+
_ items: ItemsType,
|
|
202
|
+
_ transform: (ItemsType.Element) async throws -> ResultType
|
|
203
|
+
) async rethrows -> [ResultType] {
|
|
204
|
+
var values = [ResultType]()
|
|
205
|
+
|
|
206
|
+
for item in items {
|
|
207
|
+
try await values.append(transform(item))
|
|
208
|
+
}
|
|
209
|
+
return values
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
Concurrently maps the given sequence.
|
|
214
|
+
*/
|
|
215
|
+
func concurrentMap<ItemsType: Sequence, ResultType>(
|
|
216
|
+
_ items: ItemsType,
|
|
217
|
+
_ transform: @escaping (ItemsType.Element) async throws -> ResultType
|
|
218
|
+
) async rethrows -> [ResultType] {
|
|
219
|
+
let tasks = items.map { item in
|
|
220
|
+
Task {
|
|
221
|
+
try await transform(item)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return try await asyncMap(tasks) { task in
|
|
225
|
+
try await task.value
|
|
226
|
+
}
|
|
227
|
+
}
|