@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,189 @@
|
|
|
1
|
+
package expo.modules.image.blurhash
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import kotlin.math.cos
|
|
6
|
+
import kotlin.math.pow
|
|
7
|
+
import kotlin.math.withSign
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Copied from https://github.com/woltapp/blurhash.
|
|
11
|
+
*/
|
|
12
|
+
object BlurhashDecoder {
|
|
13
|
+
|
|
14
|
+
// cache Math.cos() calculations to improve performance.
|
|
15
|
+
// The number of calculations can be huge for many bitmaps: width * height * numCompX * numCompY * 2 * nBitmaps
|
|
16
|
+
// the cache is enabled by default, it is recommended to disable it only when just a few images are displayed
|
|
17
|
+
private val cacheCosinesX = HashMap<Int, DoubleArray>()
|
|
18
|
+
private val cacheCosinesY = HashMap<Int, DoubleArray>()
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Clear calculations stored in memory cache.
|
|
22
|
+
* The cache is not big, but will increase when many image sizes are used,
|
|
23
|
+
* if the app needs memory it is recommended to clear it.
|
|
24
|
+
*/
|
|
25
|
+
fun clearCache() {
|
|
26
|
+
cacheCosinesX.clear()
|
|
27
|
+
cacheCosinesY.clear()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Decode a blur hash into a new bitmap.
|
|
32
|
+
*
|
|
33
|
+
* @param useCache use in memory cache for the calculated math, reused by images with same size.
|
|
34
|
+
* if the cache does not exist yet it will be created and populated with new calculations.
|
|
35
|
+
* By default it is true.
|
|
36
|
+
*/
|
|
37
|
+
fun decode(blurHash: String?, width: Int, height: Int, punch: Float = 1f, useCache: Boolean = true): Bitmap? {
|
|
38
|
+
if (blurHash == null || blurHash.length < 6) {
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
val numCompEnc = decode83(blurHash, 0, 1)
|
|
42
|
+
val numCompX = (numCompEnc % 9) + 1
|
|
43
|
+
val numCompY = (numCompEnc / 9) + 1
|
|
44
|
+
if (blurHash.length != 4 + 2 * numCompX * numCompY) {
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
val maxAcEnc = decode83(blurHash, 1, 2)
|
|
48
|
+
val maxAc = (maxAcEnc + 1) / 166f
|
|
49
|
+
val colors = Array(numCompX * numCompY) { i ->
|
|
50
|
+
if (i == 0) {
|
|
51
|
+
val colorEnc = decode83(blurHash, 2, 6)
|
|
52
|
+
decodeDc(colorEnc)
|
|
53
|
+
} else {
|
|
54
|
+
val from = 4 + i * 2
|
|
55
|
+
val colorEnc = decode83(blurHash, from, from + 2)
|
|
56
|
+
decodeAc(colorEnc, maxAc * punch)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return composeBitmap(width, height, numCompX, numCompY, colors, useCache)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private fun decode83(str: String, from: Int = 0, to: Int = str.length): Int {
|
|
63
|
+
var result = 0
|
|
64
|
+
for (i in from until to) {
|
|
65
|
+
val index = charMap[str[i]] ?: -1
|
|
66
|
+
if (index != -1) {
|
|
67
|
+
result = result * 83 + index
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private fun decodeDc(colorEnc: Int): FloatArray {
|
|
74
|
+
val r = colorEnc shr 16
|
|
75
|
+
val g = (colorEnc shr 8) and 255
|
|
76
|
+
val b = colorEnc and 255
|
|
77
|
+
return floatArrayOf(srgbToLinear(r), srgbToLinear(g), srgbToLinear(b))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun srgbToLinear(colorEnc: Int): Float {
|
|
81
|
+
val v = colorEnc / 255f
|
|
82
|
+
return if (v <= 0.04045f) {
|
|
83
|
+
(v / 12.92f)
|
|
84
|
+
} else {
|
|
85
|
+
((v + 0.055f) / 1.055f).pow(2.4f)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private fun decodeAc(value: Int, maxAc: Float): FloatArray {
|
|
90
|
+
val r = value / (19 * 19)
|
|
91
|
+
val g = (value / 19) % 19
|
|
92
|
+
val b = value % 19
|
|
93
|
+
return floatArrayOf(
|
|
94
|
+
signedPow2((r - 9) / 9.0f) * maxAc,
|
|
95
|
+
signedPow2((g - 9) / 9.0f) * maxAc,
|
|
96
|
+
signedPow2((b - 9) / 9.0f) * maxAc
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private fun signedPow2(value: Float) = value.pow(2f).withSign(value)
|
|
101
|
+
|
|
102
|
+
private fun composeBitmap(
|
|
103
|
+
width: Int,
|
|
104
|
+
height: Int,
|
|
105
|
+
numCompX: Int,
|
|
106
|
+
numCompY: Int,
|
|
107
|
+
colors: Array<FloatArray>,
|
|
108
|
+
useCache: Boolean
|
|
109
|
+
): Bitmap {
|
|
110
|
+
// use an array for better performance when writing pixel colors
|
|
111
|
+
val imageArray = IntArray(width * height)
|
|
112
|
+
val calculateCosX = !useCache || !cacheCosinesX.containsKey(width * numCompX)
|
|
113
|
+
val cosinesX = getArrayForCosinesX(calculateCosX, width, numCompX)
|
|
114
|
+
val calculateCosY = !useCache || !cacheCosinesY.containsKey(height * numCompY)
|
|
115
|
+
val cosinesY = getArrayForCosinesY(calculateCosY, height, numCompY)
|
|
116
|
+
for (y in 0 until height) {
|
|
117
|
+
for (x in 0 until width) {
|
|
118
|
+
var r = 0f
|
|
119
|
+
var g = 0f
|
|
120
|
+
var b = 0f
|
|
121
|
+
for (j in 0 until numCompY) {
|
|
122
|
+
for (i in 0 until numCompX) {
|
|
123
|
+
val cosX = cosinesX.getCos(calculateCosX, i, numCompX, x, width)
|
|
124
|
+
val cosY = cosinesY.getCos(calculateCosY, j, numCompY, y, height)
|
|
125
|
+
val basis = (cosX * cosY).toFloat()
|
|
126
|
+
val color = colors[j * numCompX + i]
|
|
127
|
+
r += color[0] * basis
|
|
128
|
+
g += color[1] * basis
|
|
129
|
+
b += color[2] * basis
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
imageArray[x + width * y] = Color.rgb(linearToSrgb(r), linearToSrgb(g), linearToSrgb(b))
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return Bitmap.createBitmap(imageArray, width, height, Bitmap.Config.ARGB_8888)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private fun getArrayForCosinesY(calculate: Boolean, height: Int, numCompY: Int) = when {
|
|
139
|
+
calculate -> {
|
|
140
|
+
DoubleArray(height * numCompY).also {
|
|
141
|
+
cacheCosinesY[height * numCompY] = it
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else -> {
|
|
145
|
+
cacheCosinesY[height * numCompY]!!
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private fun getArrayForCosinesX(calculate: Boolean, width: Int, numCompX: Int) = when {
|
|
150
|
+
calculate -> {
|
|
151
|
+
DoubleArray(width * numCompX).also {
|
|
152
|
+
cacheCosinesX[width * numCompX] = it
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else -> cacheCosinesX[width * numCompX]!!
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private fun DoubleArray.getCos(
|
|
159
|
+
calculate: Boolean,
|
|
160
|
+
x: Int,
|
|
161
|
+
numComp: Int,
|
|
162
|
+
y: Int,
|
|
163
|
+
size: Int
|
|
164
|
+
): Double {
|
|
165
|
+
if (calculate) {
|
|
166
|
+
this[x + numComp * y] = cos(Math.PI * y * x / size)
|
|
167
|
+
}
|
|
168
|
+
return this[x + numComp * y]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private fun linearToSrgb(value: Float): Int {
|
|
172
|
+
val v = value.coerceIn(0f, 1f)
|
|
173
|
+
return if (v <= 0.0031308f) {
|
|
174
|
+
(v * 12.92f * 255f + 0.5f).toInt()
|
|
175
|
+
} else {
|
|
176
|
+
((1.055f * v.pow(1 / 2.4f) - 0.055f) * 255 + 0.5f).toInt()
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private val charMap = listOf(
|
|
181
|
+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
182
|
+
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
|
183
|
+
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
184
|
+
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '#', '$', '%', '*', '+', ',',
|
|
185
|
+
'-', '.', ':', ';', '=', '?', '@', '[', ']', '^', '_', '{', '|', '}', '~'
|
|
186
|
+
)
|
|
187
|
+
.mapIndexed { i, c -> c to i }
|
|
188
|
+
.toMap()
|
|
189
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package expo.modules.image.blurhash
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import com.bumptech.glide.load.Options
|
|
6
|
+
import com.bumptech.glide.load.model.ModelLoader
|
|
7
|
+
import com.bumptech.glide.signature.ObjectKey
|
|
8
|
+
import expo.modules.image.GlideBlurhashModel
|
|
9
|
+
|
|
10
|
+
class BlurhashModelLoader : ModelLoader<GlideBlurhashModel, Bitmap> {
|
|
11
|
+
override fun handles(model: GlideBlurhashModel): Boolean = true
|
|
12
|
+
|
|
13
|
+
override fun buildLoadData(
|
|
14
|
+
model: GlideBlurhashModel,
|
|
15
|
+
width: Int,
|
|
16
|
+
height: Int,
|
|
17
|
+
options: Options
|
|
18
|
+
): ModelLoader.LoadData<Bitmap> {
|
|
19
|
+
val blurhash = getPath(model.uri, 0, null) { it }
|
|
20
|
+
return ModelLoader.LoadData(
|
|
21
|
+
ObjectKey(model),
|
|
22
|
+
BlurHashFetcher(blurhash, model.width, model.height, 1f)
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private fun <T> getPath(uri: Uri, index: Int, default: T, converter: (String) -> T): T {
|
|
27
|
+
val value = uri.pathSegments.getOrNull(index) ?: return default
|
|
28
|
+
return converter(value)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package expo.modules.image.blurhash
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import com.bumptech.glide.load.model.ModelLoader
|
|
5
|
+
import com.bumptech.glide.load.model.ModelLoaderFactory
|
|
6
|
+
import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
|
7
|
+
import expo.modules.image.GlideBlurhashModel
|
|
8
|
+
|
|
9
|
+
class BlurhashModelLoaderFactory : ModelLoaderFactory<GlideBlurhashModel, Bitmap> {
|
|
10
|
+
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<GlideBlurhashModel, Bitmap> =
|
|
11
|
+
BlurhashModelLoader()
|
|
12
|
+
|
|
13
|
+
override fun teardown() = Unit
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package expo.modules.image.blurhash
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Bitmap
|
|
5
|
+
import com.bumptech.glide.Glide
|
|
6
|
+
import com.bumptech.glide.Registry
|
|
7
|
+
import com.bumptech.glide.annotation.GlideModule
|
|
8
|
+
import com.bumptech.glide.module.LibraryGlideModule
|
|
9
|
+
import expo.modules.image.GlideBlurhashModel
|
|
10
|
+
|
|
11
|
+
@GlideModule
|
|
12
|
+
class BlurhashModule : LibraryGlideModule() {
|
|
13
|
+
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
|
14
|
+
super.registerComponents(context, glide, registry)
|
|
15
|
+
registry.prepend(GlideBlurhashModel::class.java, Bitmap::class.java, BlurhashModelLoaderFactory())
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package expo.modules.image.dataurls
|
|
2
|
+
|
|
3
|
+
import android.util.Base64
|
|
4
|
+
import com.bumptech.glide.Priority
|
|
5
|
+
import com.bumptech.glide.load.DataSource
|
|
6
|
+
import com.bumptech.glide.load.data.DataFetcher
|
|
7
|
+
import java.nio.ByteBuffer
|
|
8
|
+
|
|
9
|
+
class Base64DataFetcher(private val data: String) : DataFetcher<ByteBuffer> {
|
|
10
|
+
override fun cleanup() = Unit
|
|
11
|
+
override fun cancel() = Unit
|
|
12
|
+
override fun getDataClass(): Class<ByteBuffer> = ByteBuffer::class.java
|
|
13
|
+
override fun getDataSource(): DataSource = DataSource.LOCAL
|
|
14
|
+
|
|
15
|
+
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in ByteBuffer>) {
|
|
16
|
+
val base64Section = getBase64Section()
|
|
17
|
+
val data = Base64.decode(base64Section, Base64.DEFAULT)
|
|
18
|
+
val byteBuffer = ByteBuffer.wrap(data)
|
|
19
|
+
callback.onDataReady(byteBuffer)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private fun getBase64Section(): String {
|
|
23
|
+
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs.
|
|
24
|
+
val startOfBase64Section = data.indexOf(',')
|
|
25
|
+
return data.substring(startOfBase64Section + 1)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
package expo.modules.image.dataurls
|
|
2
|
+
|
|
3
|
+
import com.bumptech.glide.load.Options
|
|
4
|
+
import com.bumptech.glide.load.model.ModelLoader
|
|
5
|
+
import com.bumptech.glide.signature.ObjectKey
|
|
6
|
+
import java.nio.ByteBuffer
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Loads an [java.io.InputStream] from a Base 64 encoded String.
|
|
10
|
+
*/
|
|
11
|
+
class Base64ModelLoader : ModelLoader<String, ByteBuffer> {
|
|
12
|
+
override fun handles(model: String): Boolean {
|
|
13
|
+
return model.startsWith("data:")
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
override fun buildLoadData(
|
|
17
|
+
model: String,
|
|
18
|
+
width: Int,
|
|
19
|
+
height: Int,
|
|
20
|
+
options: Options
|
|
21
|
+
): ModelLoader.LoadData<ByteBuffer> {
|
|
22
|
+
return ModelLoader.LoadData(ObjectKey(model), Base64DataFetcher(model))
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
package expo.modules.image.dataurls
|
|
2
|
+
|
|
3
|
+
import com.bumptech.glide.load.model.ModelLoader
|
|
4
|
+
import com.bumptech.glide.load.model.ModelLoaderFactory
|
|
5
|
+
import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
|
6
|
+
import java.nio.ByteBuffer
|
|
7
|
+
|
|
8
|
+
class Base64ModelLoaderFactory : ModelLoaderFactory<String, ByteBuffer> {
|
|
9
|
+
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<String, ByteBuffer> =
|
|
10
|
+
Base64ModelLoader()
|
|
11
|
+
override fun teardown() = Unit
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
package expo.modules.image.dataurls
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import com.bumptech.glide.Glide
|
|
5
|
+
import com.bumptech.glide.Registry
|
|
6
|
+
import com.bumptech.glide.annotation.GlideModule
|
|
7
|
+
import com.bumptech.glide.module.LibraryGlideModule
|
|
8
|
+
import java.nio.ByteBuffer
|
|
9
|
+
|
|
10
|
+
@GlideModule
|
|
11
|
+
class Base64Module : LibraryGlideModule() {
|
|
12
|
+
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
|
13
|
+
super.registerComponents(context, glide, registry)
|
|
14
|
+
registry.prepend(String::class.java, ByteBuffer::class.java, Base64ModelLoaderFactory())
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
package expo.modules.image.drawing
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Canvas
|
|
5
|
+
import android.graphics.Outline
|
|
6
|
+
import android.graphics.Path
|
|
7
|
+
import android.graphics.RectF
|
|
8
|
+
import android.view.View
|
|
9
|
+
import android.view.ViewOutlineProvider
|
|
10
|
+
import com.facebook.react.modules.i18nmanager.I18nUtil
|
|
11
|
+
import com.facebook.react.uimanager.FloatUtil
|
|
12
|
+
import com.facebook.react.uimanager.PixelUtil
|
|
13
|
+
import com.facebook.yoga.YogaConstants
|
|
14
|
+
import expo.modules.image.ifYogaUndefinedUse
|
|
15
|
+
|
|
16
|
+
class OutlineProvider(private val mContext: Context) : ViewOutlineProvider() {
|
|
17
|
+
enum class BorderRadiusConfig {
|
|
18
|
+
ALL, TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, TOP_START, TOP_END, BOTTOM_START, BOTTOM_END
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
enum class CornerRadius {
|
|
22
|
+
TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private var mLayoutDirection = View.LAYOUT_DIRECTION_LTR
|
|
26
|
+
private val mBounds = RectF()
|
|
27
|
+
val borderRadiiConfig = FloatArray(9) { YogaConstants.UNDEFINED }
|
|
28
|
+
private val mCornerRadii = FloatArray(4)
|
|
29
|
+
private var mCornerRadiiInvalidated = true
|
|
30
|
+
private val mConvexPath = Path()
|
|
31
|
+
private var mConvexPathInvalidated = true
|
|
32
|
+
|
|
33
|
+
init {
|
|
34
|
+
updateCornerRadiiIfNeeded()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private fun updateCornerRadiiIfNeeded() {
|
|
38
|
+
if (!mCornerRadiiInvalidated) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
val isRTL = mLayoutDirection == View.LAYOUT_DIRECTION_RTL
|
|
43
|
+
val isRTLSwap = I18nUtil.getInstance().doLeftAndRightSwapInRTL(mContext)
|
|
44
|
+
updateCornerRadius(
|
|
45
|
+
CornerRadius.TOP_LEFT,
|
|
46
|
+
BorderRadiusConfig.TOP_LEFT,
|
|
47
|
+
BorderRadiusConfig.TOP_RIGHT,
|
|
48
|
+
BorderRadiusConfig.TOP_START,
|
|
49
|
+
BorderRadiusConfig.TOP_END,
|
|
50
|
+
isRTL,
|
|
51
|
+
isRTLSwap
|
|
52
|
+
)
|
|
53
|
+
updateCornerRadius(
|
|
54
|
+
CornerRadius.TOP_RIGHT,
|
|
55
|
+
BorderRadiusConfig.TOP_RIGHT,
|
|
56
|
+
BorderRadiusConfig.TOP_LEFT,
|
|
57
|
+
BorderRadiusConfig.TOP_END,
|
|
58
|
+
BorderRadiusConfig.TOP_START,
|
|
59
|
+
isRTL,
|
|
60
|
+
isRTLSwap
|
|
61
|
+
)
|
|
62
|
+
updateCornerRadius(
|
|
63
|
+
CornerRadius.BOTTOM_LEFT,
|
|
64
|
+
BorderRadiusConfig.BOTTOM_LEFT,
|
|
65
|
+
BorderRadiusConfig.BOTTOM_RIGHT,
|
|
66
|
+
BorderRadiusConfig.BOTTOM_START,
|
|
67
|
+
BorderRadiusConfig.BOTTOM_END,
|
|
68
|
+
isRTL,
|
|
69
|
+
isRTLSwap
|
|
70
|
+
)
|
|
71
|
+
updateCornerRadius(
|
|
72
|
+
CornerRadius.BOTTOM_RIGHT,
|
|
73
|
+
BorderRadiusConfig.BOTTOM_RIGHT,
|
|
74
|
+
BorderRadiusConfig.BOTTOM_LEFT,
|
|
75
|
+
BorderRadiusConfig.BOTTOM_END,
|
|
76
|
+
BorderRadiusConfig.BOTTOM_START,
|
|
77
|
+
isRTL,
|
|
78
|
+
isRTLSwap
|
|
79
|
+
)
|
|
80
|
+
mCornerRadiiInvalidated = false
|
|
81
|
+
mConvexPathInvalidated = true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private fun updateCornerRadius(
|
|
85
|
+
outputPosition: CornerRadius,
|
|
86
|
+
inputPosition: BorderRadiusConfig,
|
|
87
|
+
oppositePosition: BorderRadiusConfig,
|
|
88
|
+
startPosition: BorderRadiusConfig,
|
|
89
|
+
endPosition: BorderRadiusConfig,
|
|
90
|
+
isRTL: Boolean,
|
|
91
|
+
isRTLSwap: Boolean
|
|
92
|
+
) {
|
|
93
|
+
var radius = borderRadiiConfig[inputPosition.ordinal]
|
|
94
|
+
if (isRTL) {
|
|
95
|
+
if (isRTLSwap) {
|
|
96
|
+
radius = borderRadiiConfig[oppositePosition.ordinal]
|
|
97
|
+
}
|
|
98
|
+
if (YogaConstants.isUndefined(radius)) {
|
|
99
|
+
radius = borderRadiiConfig[endPosition.ordinal]
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
if (YogaConstants.isUndefined(radius)) {
|
|
103
|
+
radius = borderRadiiConfig[startPosition.ordinal]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
radius = radius
|
|
107
|
+
.ifYogaUndefinedUse(borderRadiiConfig[BorderRadiusConfig.ALL.ordinal])
|
|
108
|
+
.ifYogaUndefinedUse(0f)
|
|
109
|
+
mCornerRadii[outputPosition.ordinal] = PixelUtil.toPixelFromDIP(radius)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private fun updateConvexPathIfNeeded() {
|
|
113
|
+
if (!mConvexPathInvalidated) {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
mConvexPath.reset()
|
|
117
|
+
mConvexPath.addRoundRect(
|
|
118
|
+
mBounds,
|
|
119
|
+
floatArrayOf(
|
|
120
|
+
mCornerRadii[CornerRadius.TOP_LEFT.ordinal],
|
|
121
|
+
mCornerRadii[CornerRadius.TOP_LEFT.ordinal],
|
|
122
|
+
mCornerRadii[CornerRadius.TOP_RIGHT.ordinal],
|
|
123
|
+
mCornerRadii[CornerRadius.TOP_RIGHT.ordinal],
|
|
124
|
+
mCornerRadii[CornerRadius.BOTTOM_RIGHT.ordinal],
|
|
125
|
+
mCornerRadii[CornerRadius.BOTTOM_RIGHT.ordinal],
|
|
126
|
+
mCornerRadii[CornerRadius.BOTTOM_LEFT.ordinal],
|
|
127
|
+
mCornerRadii[CornerRadius.BOTTOM_LEFT.ordinal]
|
|
128
|
+
),
|
|
129
|
+
Path.Direction.CW
|
|
130
|
+
)
|
|
131
|
+
mConvexPathInvalidated = false
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fun hasEqualCorners(): Boolean {
|
|
135
|
+
updateCornerRadiiIfNeeded()
|
|
136
|
+
val initialCornerRadius = mCornerRadii[0]
|
|
137
|
+
return mCornerRadii.all { initialCornerRadius == it }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
fun setBorderRadius(radius: Float, position: Int): Boolean {
|
|
141
|
+
if (!FloatUtil.floatsEqual(borderRadiiConfig[position], radius)) {
|
|
142
|
+
borderRadiiConfig[position] = radius
|
|
143
|
+
mCornerRadiiInvalidated = true
|
|
144
|
+
return true
|
|
145
|
+
}
|
|
146
|
+
return false
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private fun updateBoundsAndLayoutDirection(view: View) {
|
|
150
|
+
// Update layout direction
|
|
151
|
+
val layoutDirection = view.layoutDirection
|
|
152
|
+
if (mLayoutDirection != layoutDirection) {
|
|
153
|
+
mLayoutDirection = layoutDirection
|
|
154
|
+
mCornerRadiiInvalidated = true
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Update size
|
|
158
|
+
val left = 0
|
|
159
|
+
val top = 0
|
|
160
|
+
val right = view.width
|
|
161
|
+
val bottom = view.height
|
|
162
|
+
if (mBounds.left != left.toFloat() ||
|
|
163
|
+
mBounds.top != top.toFloat() ||
|
|
164
|
+
mBounds.right != right.toFloat() ||
|
|
165
|
+
mBounds.bottom != bottom.toFloat()
|
|
166
|
+
) {
|
|
167
|
+
mBounds[left.toFloat(), top.toFloat(), right.toFloat()] = bottom.toFloat()
|
|
168
|
+
mCornerRadiiInvalidated = true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
override fun getOutline(view: View, outline: Outline) {
|
|
173
|
+
updateBoundsAndLayoutDirection(view)
|
|
174
|
+
|
|
175
|
+
// Calculate outline
|
|
176
|
+
updateCornerRadiiIfNeeded()
|
|
177
|
+
if (hasEqualCorners()) {
|
|
178
|
+
val cornerRadius = mCornerRadii[0]
|
|
179
|
+
if (cornerRadius > 0) {
|
|
180
|
+
outline.setRoundRect(0, 0, mBounds.width().toInt(), mBounds.height().toInt(), cornerRadius)
|
|
181
|
+
} else {
|
|
182
|
+
outline.setRect(0, 0, mBounds.width().toInt(), mBounds.height().toInt())
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
// Clipping is not supported when using a convex path, but drawing the elevation
|
|
186
|
+
// shadow is. For the particular case, we fallback to canvas clipping in the view
|
|
187
|
+
// which is supposed to call `clipCanvasIfNeeded` in its `draw` method.
|
|
188
|
+
updateConvexPathIfNeeded()
|
|
189
|
+
outline.setConvexPath(mConvexPath)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
fun clipCanvasIfNeeded(canvas: Canvas, view: View) {
|
|
194
|
+
updateBoundsAndLayoutDirection(view)
|
|
195
|
+
updateCornerRadiiIfNeeded()
|
|
196
|
+
if (!hasEqualCorners()) {
|
|
197
|
+
updateConvexPathIfNeeded()
|
|
198
|
+
canvas.clipPath(mConvexPath)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
package expo.modules.image.enums
|
|
2
|
+
|
|
3
|
+
import android.graphics.Matrix
|
|
4
|
+
import android.graphics.RectF
|
|
5
|
+
import expo.modules.kotlin.types.Enumerable
|
|
6
|
+
import kotlin.math.max
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Describes how the image should be resized to fit its container.
|
|
10
|
+
* - Note: It mirrors the CSS [`object-fit`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) property.
|
|
11
|
+
*/
|
|
12
|
+
enum class ContentFit(val value: String) : Enumerable {
|
|
13
|
+
/**
|
|
14
|
+
* The image is scaled to maintain its aspect ratio while fitting within the container's box.
|
|
15
|
+
* The entire image is made to fill the box, while preserving its aspect ratio,
|
|
16
|
+
* so the image will be "letterboxed" if its aspect ratio does not match the aspect ratio of the box.
|
|
17
|
+
*/
|
|
18
|
+
Contain("contain"),
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The image is sized to maintain its aspect ratio while filling the element's entire content box.
|
|
22
|
+
* If the image's aspect ratio does not match the aspect ratio of its box, then the object will be clipped to fit.
|
|
23
|
+
*/
|
|
24
|
+
Cover("cover"),
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The image is sized to fill the element's content box. The entire object will completely fill the box.
|
|
28
|
+
* If the image's aspect ratio does not match the aspect ratio of its box, then the image will be stretched to fit.
|
|
29
|
+
*/
|
|
30
|
+
Fill("fill"),
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The image is not resized and is centered by default.
|
|
34
|
+
* When specified, the exact position can be controlled with `ContentPosition`.
|
|
35
|
+
*/
|
|
36
|
+
None("none"),
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The image is sized as if `none` or `contain` were specified,
|
|
40
|
+
* whichever would result in a smaller concrete image size.
|
|
41
|
+
*/
|
|
42
|
+
ScaleDown("scale-down");
|
|
43
|
+
|
|
44
|
+
internal fun toMatrix(imageRect: RectF, viewRect: RectF, sourceWidth: Int, sourceHeight: Int) = Matrix().apply {
|
|
45
|
+
when (this@ContentFit) {
|
|
46
|
+
Contain -> setRectToRect(imageRect, viewRect, Matrix.ScaleToFit.START)
|
|
47
|
+
Cover -> {
|
|
48
|
+
val imageWidth = imageRect.width()
|
|
49
|
+
val imageHeight = imageRect.height()
|
|
50
|
+
|
|
51
|
+
val reactWidth = viewRect.width()
|
|
52
|
+
val reactHeight = viewRect.height()
|
|
53
|
+
|
|
54
|
+
val scale = max(reactWidth / imageWidth, reactHeight / imageHeight)
|
|
55
|
+
|
|
56
|
+
setScale(scale, scale)
|
|
57
|
+
}
|
|
58
|
+
Fill -> setRectToRect(imageRect, viewRect, Matrix.ScaleToFit.FILL)
|
|
59
|
+
None -> {
|
|
60
|
+
// we don't need to do anything
|
|
61
|
+
}
|
|
62
|
+
ScaleDown -> {
|
|
63
|
+
// If we have information about the original size of the source, we can resize the image more drastically.
|
|
64
|
+
// In certain situations, we may even permit upscaling when we anticipate the image to be reloaded without any reduction in size,
|
|
65
|
+
// which will create a seamless transition between various states.
|
|
66
|
+
if (sourceWidth != -1 && sourceHeight != -1) {
|
|
67
|
+
val sourceRect = RectF(0f, 0f, sourceWidth.toFloat(), sourceHeight.toFloat())
|
|
68
|
+
// Rather than checking if the image rectangle is within the bounds of the view rectangle, we verify the original source rectangle.
|
|
69
|
+
// We know that the newly loaded image has larger dimensions than the current one, and therefore,
|
|
70
|
+
// it will not be downscaled.
|
|
71
|
+
if (sourceRect.width() >= viewRect.width() || sourceRect.height() >= viewRect.height()) {
|
|
72
|
+
setRectToRect(imageRect, viewRect, Matrix.ScaleToFit.START)
|
|
73
|
+
}
|
|
74
|
+
// If the source rectangle is larger than the view rectangle and the image rectangle has not been upscaled to match the source rectangle,
|
|
75
|
+
// temporary upscaling is necessary to ensure a seamless transition.
|
|
76
|
+
// It should be noted that this upscaling is applied to the downscaled version of the image,
|
|
77
|
+
// not the original source image, and will be replaced by the original asset shortly thereafter.
|
|
78
|
+
else {
|
|
79
|
+
setRectToRect(imageRect, sourceRect, Matrix.ScaleToFit.START)
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
if (imageRect.width() >= viewRect.width() || imageRect.height() >= viewRect.height()) {
|
|
83
|
+
setRectToRect(imageRect, viewRect, Matrix.ScaleToFit.START)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package expo.modules.image.enums
|
|
2
|
+
|
|
3
|
+
import com.bumptech.glide.load.DataSource
|
|
4
|
+
|
|
5
|
+
enum class ImageCacheType(private vararg val dataSources: DataSource) {
|
|
6
|
+
NONE(DataSource.LOCAL, DataSource.REMOTE),
|
|
7
|
+
DISK(DataSource.DATA_DISK_CACHE, DataSource.RESOURCE_DISK_CACHE),
|
|
8
|
+
MEMORY(DataSource.MEMORY_CACHE);
|
|
9
|
+
|
|
10
|
+
companion object {
|
|
11
|
+
fun fromNativeValue(value: DataSource): ImageCacheType =
|
|
12
|
+
values().firstOrNull { it.dataSources.contains(value) } ?: NONE
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package expo.modules.image.enums
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.types.Enumerable
|
|
4
|
+
|
|
5
|
+
enum class Priority(val value: String) : Enumerable {
|
|
6
|
+
LOW("low"),
|
|
7
|
+
NORMAL("normal"),
|
|
8
|
+
HIGH("high");
|
|
9
|
+
|
|
10
|
+
internal fun toGlidePriority(): com.bumptech.glide.Priority = when (this) {
|
|
11
|
+
LOW -> com.bumptech.glide.Priority.LOW
|
|
12
|
+
NORMAL -> com.bumptech.glide.Priority.NORMAL
|
|
13
|
+
HIGH -> com.bumptech.glide.Priority.IMMEDIATE
|
|
14
|
+
}
|
|
15
|
+
}
|