@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.
Files changed (289) hide show
  1. package/.eslintrc.js +2 -0
  2. package/CHANGELOG.md +285 -0
  3. package/CMakeLists.txt +91 -0
  4. package/LICENSE +7 -0
  5. package/README.kepler.md +123 -0
  6. package/README.md +54 -0
  7. package/THIRD_PARTY_NOTICES.txt +24 -0
  8. package/android/build.gradle +124 -0
  9. package/android/proguard-rules.pro +28 -0
  10. package/android/src/main/AndroidManifest.xml +15 -0
  11. package/android/src/main/java/expo/modules/image/Exceptions.kt +9 -0
  12. package/android/src/main/java/expo/modules/image/ExpoImageAppGlideModule.kt +19 -0
  13. package/android/src/main/java/expo/modules/image/ExpoImageModule.kt +192 -0
  14. package/android/src/main/java/expo/modules/image/ExpoImageView.kt +220 -0
  15. package/android/src/main/java/expo/modules/image/ExpoImageViewWrapper.kt +652 -0
  16. package/android/src/main/java/expo/modules/image/GlideModel.kt +73 -0
  17. package/android/src/main/java/expo/modules/image/ImageUtils.kt +50 -0
  18. package/android/src/main/java/expo/modules/image/ImageViewWrapperTarget.kt +313 -0
  19. package/android/src/main/java/expo/modules/image/ResourceIdHelper.kt +47 -0
  20. package/android/src/main/java/expo/modules/image/ThumbnailRequestCoordinatorExtension.kt +21 -0
  21. package/android/src/main/java/expo/modules/image/YogaUtils.kt +20 -0
  22. package/android/src/main/java/expo/modules/image/blurhash/BlurHashFetcher.kt +32 -0
  23. package/android/src/main/java/expo/modules/image/blurhash/BlurhashDecoder.kt +189 -0
  24. package/android/src/main/java/expo/modules/image/blurhash/BlurhashModelLoader.kt +30 -0
  25. package/android/src/main/java/expo/modules/image/blurhash/BlurhashModelLoaderFactory.kt +14 -0
  26. package/android/src/main/java/expo/modules/image/blurhash/BlurhashModule.kt +17 -0
  27. package/android/src/main/java/expo/modules/image/dataurls/Base64DataFetcher.kt +27 -0
  28. package/android/src/main/java/expo/modules/image/dataurls/Base64ModelLoader.kt +24 -0
  29. package/android/src/main/java/expo/modules/image/dataurls/Base64ModelLoaderFactory.kt +12 -0
  30. package/android/src/main/java/expo/modules/image/dataurls/Base64Module.kt +16 -0
  31. package/android/src/main/java/expo/modules/image/drawing/OutlineProvider.kt +201 -0
  32. package/android/src/main/java/expo/modules/image/enums/ContentFit.kt +89 -0
  33. package/android/src/main/java/expo/modules/image/enums/ImageCacheType.kt +14 -0
  34. package/android/src/main/java/expo/modules/image/enums/Priority.kt +15 -0
  35. package/android/src/main/java/expo/modules/image/events/GlideRequestListener.kt +64 -0
  36. package/android/src/main/java/expo/modules/image/events/OkHttpProgressListener.kt +26 -0
  37. package/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt +80 -0
  38. package/android/src/main/java/expo/modules/image/okhttp/GlideUrlWrapperLoader.kt +55 -0
  39. package/android/src/main/java/expo/modules/image/records/CachePolicy.kt +10 -0
  40. package/android/src/main/java/expo/modules/image/records/ContentPosition.kt +89 -0
  41. package/android/src/main/java/expo/modules/image/records/ImageTransition.kt +8 -0
  42. package/android/src/main/java/expo/modules/image/records/SourceMap.kt +160 -0
  43. package/android/src/main/java/expo/modules/image/records/events.kt +25 -0
  44. package/android/src/main/java/expo/modules/image/svg/SVGDecoder.kt +33 -0
  45. package/android/src/main/java/expo/modules/image/svg/SVGDrawableTranscoder.kt +37 -0
  46. package/android/src/main/java/expo/modules/image/svg/SVGModule.kt +26 -0
  47. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashDecoder.kt +365 -0
  48. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashFetcher.kt +31 -0
  49. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModelLoader.kt +26 -0
  50. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModelLoaderFactory.kt +14 -0
  51. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModule.kt +17 -0
  52. package/build/ExpoImage.d.ts +15 -0
  53. package/build/ExpoImage.d.ts.map +1 -0
  54. package/build/ExpoImage.js +75 -0
  55. package/build/ExpoImage.js.map +1 -0
  56. package/build/ExpoImage.kepler.d.ts +23 -0
  57. package/build/ExpoImage.kepler.d.ts.map +1 -0
  58. package/build/ExpoImage.kepler.js +71 -0
  59. package/build/ExpoImage.kepler.js.map +1 -0
  60. package/build/ExpoImage.web.d.ts +9 -0
  61. package/build/ExpoImage.web.d.ts.map +1 -0
  62. package/build/ExpoImage.web.js +90 -0
  63. package/build/ExpoImage.web.js.map +1 -0
  64. package/build/ExpoImageModules.d.ts +3 -0
  65. package/build/ExpoImageModules.d.ts.map +1 -0
  66. package/build/ExpoImageModules.js +4 -0
  67. package/build/ExpoImageModules.js.map +1 -0
  68. package/build/ExpoImageModules.kepler.d.ts +6 -0
  69. package/build/ExpoImageModules.kepler.d.ts.map +1 -0
  70. package/build/ExpoImageModules.kepler.js +5 -0
  71. package/build/ExpoImageModules.kepler.js.map +1 -0
  72. package/build/Image.d.ts +30 -0
  73. package/build/Image.d.ts.map +1 -0
  74. package/build/Image.js +49 -0
  75. package/build/Image.js.map +1 -0
  76. package/build/Image.types.d.ts +374 -0
  77. package/build/Image.types.d.ts.map +1 -0
  78. package/build/Image.types.js +2 -0
  79. package/build/Image.types.js.map +1 -0
  80. package/build/ImageBackground.d.ts +6 -0
  81. package/build/ImageBackground.d.ts.map +1 -0
  82. package/build/ImageBackground.js +13 -0
  83. package/build/ImageBackground.js.map +1 -0
  84. package/build/ImageViewNativeComponent.d.ts +20 -0
  85. package/build/ImageViewNativeComponent.d.ts.map +1 -0
  86. package/build/ImageViewNativeComponent.js +42 -0
  87. package/build/ImageViewNativeComponent.js.map +1 -0
  88. package/build/NativeImageLoaderKepler.d.ts +18 -0
  89. package/build/NativeImageLoaderKepler.d.ts.map +1 -0
  90. package/build/NativeImageLoaderKepler.js +3 -0
  91. package/build/NativeImageLoaderKepler.js.map +1 -0
  92. package/build/fabric/registerNativeComponent.d.ts +15 -0
  93. package/build/fabric/registerNativeComponent.d.ts.map +1 -0
  94. package/build/fabric/registerNativeComponent.js +47 -0
  95. package/build/fabric/registerNativeComponent.js.map +1 -0
  96. package/build/index.d.ts +4 -0
  97. package/build/index.d.ts.map +1 -0
  98. package/build/index.js +4 -0
  99. package/build/index.js.map +1 -0
  100. package/build/utils/AssetSourceResolver.web.d.ts +24 -0
  101. package/build/utils/AssetSourceResolver.web.d.ts.map +1 -0
  102. package/build/utils/AssetSourceResolver.web.js +67 -0
  103. package/build/utils/AssetSourceResolver.web.js.map +1 -0
  104. package/build/utils/blurhash/base83.d.ts +3 -0
  105. package/build/utils/blurhash/base83.d.ts.map +1 -0
  106. package/build/utils/blurhash/base83.js +103 -0
  107. package/build/utils/blurhash/base83.js.map +1 -0
  108. package/build/utils/blurhash/decode.d.ts +7 -0
  109. package/build/utils/blurhash/decode.d.ts.map +1 -0
  110. package/build/utils/blurhash/decode.js +92 -0
  111. package/build/utils/blurhash/decode.js.map +1 -0
  112. package/build/utils/blurhash/error.d.ts +4 -0
  113. package/build/utils/blurhash/error.d.ts.map +1 -0
  114. package/build/utils/blurhash/error.js +8 -0
  115. package/build/utils/blurhash/error.js.map +1 -0
  116. package/build/utils/blurhash/useBlurhash.d.ts +8 -0
  117. package/build/utils/blurhash/useBlurhash.d.ts.map +1 -0
  118. package/build/utils/blurhash/useBlurhash.js +64 -0
  119. package/build/utils/blurhash/useBlurhash.js.map +1 -0
  120. package/build/utils/blurhash/utils.d.ts +5 -0
  121. package/build/utils/blurhash/utils.d.ts.map +1 -0
  122. package/build/utils/blurhash/utils.js +21 -0
  123. package/build/utils/blurhash/utils.js.map +1 -0
  124. package/build/utils/resolveAssetSource.d.ts +3 -0
  125. package/build/utils/resolveAssetSource.d.ts.map +1 -0
  126. package/build/utils/resolveAssetSource.js +3 -0
  127. package/build/utils/resolveAssetSource.js.map +1 -0
  128. package/build/utils/resolveAssetSource.web.d.ts +9 -0
  129. package/build/utils/resolveAssetSource.web.d.ts.map +1 -0
  130. package/build/utils/resolveAssetSource.web.js +31 -0
  131. package/build/utils/resolveAssetSource.web.js.map +1 -0
  132. package/build/utils/resolveHashString.d.ts +14 -0
  133. package/build/utils/resolveHashString.d.ts.map +1 -0
  134. package/build/utils/resolveHashString.js +31 -0
  135. package/build/utils/resolveHashString.js.map +1 -0
  136. package/build/utils/resolveHashString.web.d.ts +20 -0
  137. package/build/utils/resolveHashString.web.d.ts.map +1 -0
  138. package/build/utils/resolveHashString.web.js +31 -0
  139. package/build/utils/resolveHashString.web.js.map +1 -0
  140. package/build/utils/resolveSources.d.ts +8 -0
  141. package/build/utils/resolveSources.d.ts.map +1 -0
  142. package/build/utils/resolveSources.js +45 -0
  143. package/build/utils/resolveSources.js.map +1 -0
  144. package/build/utils/thumbhash/thumbhash.d.ts +66 -0
  145. package/build/utils/thumbhash/thumbhash.d.ts.map +1 -0
  146. package/build/utils/thumbhash/thumbhash.js +338 -0
  147. package/build/utils/thumbhash/thumbhash.js.map +1 -0
  148. package/build/utils.d.ts +17 -0
  149. package/build/utils.d.ts.map +1 -0
  150. package/build/utils.js +85 -0
  151. package/build/utils.js.map +1 -0
  152. package/build/web/AnimationManager.d.ts +37 -0
  153. package/build/web/AnimationManager.d.ts.map +1 -0
  154. package/build/web/AnimationManager.js +155 -0
  155. package/build/web/AnimationManager.js.map +1 -0
  156. package/build/web/ColorTintFilter.d.ts +12 -0
  157. package/build/web/ColorTintFilter.d.ts.map +1 -0
  158. package/build/web/ColorTintFilter.js +30 -0
  159. package/build/web/ColorTintFilter.js.map +1 -0
  160. package/build/web/ImageWrapper.d.ts +5 -0
  161. package/build/web/ImageWrapper.d.ts.map +1 -0
  162. package/build/web/ImageWrapper.js +44 -0
  163. package/build/web/ImageWrapper.js.map +1 -0
  164. package/build/web/ImageWrapper.types.d.ts +29 -0
  165. package/build/web/ImageWrapper.types.d.ts.map +1 -0
  166. package/build/web/ImageWrapper.types.js +2 -0
  167. package/build/web/ImageWrapper.types.js.map +1 -0
  168. package/build/web/getImageWrapperEventHandler.d.ts +8 -0
  169. package/build/web/getImageWrapperEventHandler.d.ts.map +1 -0
  170. package/build/web/getImageWrapperEventHandler.js +25 -0
  171. package/build/web/getImageWrapperEventHandler.js.map +1 -0
  172. package/build/web/hooks.d.ts +13 -0
  173. package/build/web/hooks.d.ts.map +1 -0
  174. package/build/web/hooks.js +58 -0
  175. package/build/web/hooks.js.map +1 -0
  176. package/build/web/imageStyles.d.ts +2 -0
  177. package/build/web/imageStyles.d.ts.map +1 -0
  178. package/build/web/imageStyles.js +137 -0
  179. package/build/web/imageStyles.js.map +1 -0
  180. package/build/web/positioning.d.ts +11 -0
  181. package/build/web/positioning.d.ts.map +1 -0
  182. package/build/web/positioning.js +35 -0
  183. package/build/web/positioning.js.map +1 -0
  184. package/build/web/useSourceSelection.d.ts +14 -0
  185. package/build/web/useSourceSelection.d.ts.map +1 -0
  186. package/build/web/useSourceSelection.js +98 -0
  187. package/build/web/useSourceSelection.js.map +1 -0
  188. package/expo-module.config.json +9 -0
  189. package/ios/Blurhash.swift +326 -0
  190. package/ios/BlurhashLoader.swift +52 -0
  191. package/ios/ContentFit.swift +55 -0
  192. package/ios/ContentPosition.swift +108 -0
  193. package/ios/ExpoImage.podspec +30 -0
  194. package/ios/ImageCachePolicy.swift +24 -0
  195. package/ios/ImageCacheType.swift +21 -0
  196. package/ios/ImageModule.swift +127 -0
  197. package/ios/ImagePriority.swift +22 -0
  198. package/ios/ImageSource.swift +44 -0
  199. package/ios/ImageTransition.swift +67 -0
  200. package/ios/ImageUtils.swift +227 -0
  201. package/ios/ImageView.swift +469 -0
  202. package/ios/Loaders/PhotoLibraryAssetLoader.swift +146 -0
  203. package/ios/Thumbhash.swift +550 -0
  204. package/ios/ThumbhashLoader.swift +53 -0
  205. package/jest.config.js +3 -0
  206. package/kepler/ExpoImage.cpp +63 -0
  207. package/kepler/ExpoImage.h +29 -0
  208. package/kepler/ExpoImageCacheHandler.cpp +15 -0
  209. package/kepler/ExpoImageCacheHandler.h +28 -0
  210. package/kepler/ExpoImageComponent.cpp +579 -0
  211. package/kepler/ExpoImageComponent.h +199 -0
  212. package/kepler/ImageWrapper/BlurhashImageWrapper.cpp +37 -0
  213. package/kepler/ImageWrapper/BlurhashImageWrapper.h +23 -0
  214. package/kepler/ImageWrapper/CacheImageWrapper.cpp +80 -0
  215. package/kepler/ImageWrapper/CacheImageWrapper.h +41 -0
  216. package/kepler/ImageWrapper/HashImageWrapper.cpp +21 -0
  217. package/kepler/ImageWrapper/HashImageWrapper.h +22 -0
  218. package/kepler/ImageWrapper/ImageWrapper.cpp +24 -0
  219. package/kepler/ImageWrapper/ImageWrapper.h +33 -0
  220. package/kepler/ImageWrapper/LocalImageWrapper.cpp +61 -0
  221. package/kepler/ImageWrapper/LocalImageWrapper.h +30 -0
  222. package/kepler/ImageWrapper/ThumbhashImageWrapper.cpp +33 -0
  223. package/kepler/ImageWrapper/ThumbhashImageWrapper.h +20 -0
  224. package/kepler/KeplerImageViewComponent.h +64 -0
  225. package/kepler/KeplerInit.cpp +35 -0
  226. package/kepler/blurhash/common.h +32 -0
  227. package/kepler/blurhash/decode.cpp +187 -0
  228. package/kepler/blurhash/decode.h +58 -0
  229. package/kepler/contentposition/ImageContentPosition.cpp +86 -0
  230. package/kepler/contentposition/ImageContentPosition.h +48 -0
  231. package/kepler/enums/ImageContentFit.h +17 -0
  232. package/kepler/enums/ImagePriority.h +17 -0
  233. package/kepler/managers/ExpoImageManager.cpp +38 -0
  234. package/kepler/managers/ExpoImageManager.h +23 -0
  235. package/kepler/test/ExpoImage.cpp +73 -0
  236. package/kepler/test/ExpoImageComponent.cpp +290 -0
  237. package/kepler/test/blurhash/decode.cpp +39 -0
  238. package/kepler/test/contentposition/ImageContentPosition.cpp +100 -0
  239. package/kepler/test/managers/ExpoImageManager.cpp +39 -0
  240. package/kepler/test/thumbhash/decode.cpp +18 -0
  241. package/kepler/test/utils/LoaderDelegate.cpp +130 -0
  242. package/kepler/thumbhash/decode.cpp +166 -0
  243. package/kepler/thumbhash/decode.h +22 -0
  244. package/kepler/utils/PlaceholderImageLoaderDelegate.cpp +26 -0
  245. package/kepler/utils/PlaceholderImageLoaderDelegate.h +26 -0
  246. package/kepler/utils/PrefetchImageLoaderDelegate.cpp +55 -0
  247. package/kepler/utils/PrefetchImageLoaderDelegate.h +35 -0
  248. package/kepler/utils/SourceImageLoaderDelegate.cpp +47 -0
  249. package/kepler/utils/SourceImageLoaderDelegate.h +42 -0
  250. package/kepler/utils/SourceImageLoaderDelegateEmitter.cpp +15 -0
  251. package/kepler/utils/SourceImageLoaderDelegateEmitter.h +28 -0
  252. package/kepler-compatibility.json +8 -0
  253. package/package.json +1 -0
  254. package/react-native.config.js +22 -0
  255. package/src/ExpoImage.kepler.tsx +119 -0
  256. package/src/ExpoImage.tsx +119 -0
  257. package/src/ExpoImage.web.tsx +155 -0
  258. package/src/ExpoImageModules.kepler.ts +7 -0
  259. package/src/ExpoImageModules.ts +4 -0
  260. package/src/Image.tsx +82 -0
  261. package/src/Image.types.ts +450 -0
  262. package/src/ImageBackground.tsx +18 -0
  263. package/src/ImageViewNativeComponent.ts +59 -0
  264. package/src/NativeImageLoaderKepler.ts +22 -0
  265. package/src/fabric/registerNativeComponent.ts +70 -0
  266. package/src/index.ts +3 -0
  267. package/src/utils/AssetSourceResolver.web.ts +88 -0
  268. package/src/utils/blurhash/base83.ts +104 -0
  269. package/src/utils/blurhash/decode.ts +114 -0
  270. package/src/utils/blurhash/error.ts +7 -0
  271. package/src/utils/blurhash/useBlurhash.tsx +81 -0
  272. package/src/utils/blurhash/utils.ts +21 -0
  273. package/src/utils/resolveAssetSource.tsx +3 -0
  274. package/src/utils/resolveAssetSource.web.ts +40 -0
  275. package/src/utils/resolveHashString.tsx +36 -0
  276. package/src/utils/resolveHashString.web.tsx +33 -0
  277. package/src/utils/resolveSources.tsx +48 -0
  278. package/src/utils/thumbhash/thumbhash.ts +387 -0
  279. package/src/utils.ts +109 -0
  280. package/src/web/AnimationManager.tsx +224 -0
  281. package/src/web/ColorTintFilter.tsx +36 -0
  282. package/src/web/ImageWrapper.tsx +85 -0
  283. package/src/web/ImageWrapper.types.ts +36 -0
  284. package/src/web/getImageWrapperEventHandler.ts +29 -0
  285. package/src/web/hooks.ts +72 -0
  286. package/src/web/imageStyles.tsx +136 -0
  287. package/src/web/positioning.ts +48 -0
  288. package/src/web/useSourceSelection.ts +151 -0
  289. package/tsconfig.json +9 -0
@@ -0,0 +1,550 @@
1
+ import Foundation
2
+
3
+ // Blurhash implementation thanks to @evanw work
4
+ // https://github.com/evanw/thumbhash
5
+
6
+ // NOTE: Swift has an exponential-time type checker and compiling very simple
7
+ // expressions can easily take many seconds, especially when expressions involve
8
+ // numeric type constructors.
9
+ //
10
+ // This file deliberately breaks compound expressions up into separate variables
11
+ // to improve compile time even though this comes at the expense of readability.
12
+ // This is a known workaround for this deficiency in the Swift compiler.
13
+ //
14
+ // The following command is helpful when debugging Swift compile time issues:
15
+ //
16
+ // swiftc ThumbHash.swift -Xfrontend -debug-time-function-bodies
17
+ //
18
+ // These optimizations brought the compile time for this file from around 2.5
19
+ // seconds to around 250ms (10x faster).
20
+ // NOTE: Swift's debug-build performance of for-in loops over numeric ranges is
21
+ // really awful. Debug builds compile a very generic indexing iterator thing
22
+ // that makes many nested calls for every iteration, which makes debug-build
23
+ // performance crawl.
24
+ //
25
+ // This file deliberately avoids for-in loops that loop for more than a few
26
+ // times to improve debug-build run time even though this comes at the expense
27
+ // of readability. Similarly unsafe pointers are used instead of array getters
28
+ // to avoid unnecessary bounds checks, which have extra overhead in debug builds.
29
+ //
30
+ // These optimizations brought the run time to encode and decode 10 ThumbHashes
31
+ // in debug mode from 700ms to 70ms (10x faster).
32
+
33
+ // swiftlint:disable all
34
+ func rgbaToThumbHash(w: Int, h: Int, rgba: Data) -> Data {
35
+ // Encoding an image larger than 100x100 is slow with no benefit
36
+ assert(w <= 100 && h <= 100)
37
+ assert(rgba.count == w * h * 4)
38
+
39
+ // Determine the average color
40
+ var avg_r: Float32 = 0
41
+ var avg_g: Float32 = 0
42
+ var avg_b: Float32 = 0
43
+ var avg_a: Float32 = 0
44
+ rgba.withUnsafeBytes { rgba in
45
+ var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count)
46
+ let n = w * h
47
+ var i = 0
48
+ while i < n {
49
+ let alpha = Float32(rgba[3]) / 255
50
+ avg_r += alpha / 255 * Float32(rgba[0])
51
+ avg_g += alpha / 255 * Float32(rgba[1])
52
+ avg_b += alpha / 255 * Float32(rgba[2])
53
+ avg_a += alpha
54
+ rgba = rgba.advanced(by: 4)
55
+ i += 1
56
+ }
57
+ }
58
+ if avg_a > 0 {
59
+ avg_r /= avg_a
60
+ avg_g /= avg_a
61
+ avg_b /= avg_a
62
+ }
63
+
64
+ let hasAlpha = avg_a < Float32(w * h)
65
+ let l_limit = hasAlpha ? 5 : 7 // Use fewer luminance bits if there's alpha
66
+ let imax_wh = max(w, h)
67
+ let iwl_limit = l_limit * w
68
+ let ihl_limit = l_limit * h
69
+ let fmax_wh = Float32(imax_wh)
70
+ let fwl_limit = Float32(iwl_limit)
71
+ let fhl_limit = Float32(ihl_limit)
72
+ let flx = round(fwl_limit / fmax_wh)
73
+ let fly = round(fhl_limit / fmax_wh)
74
+ var lx = Int(flx)
75
+ var ly = Int(fly)
76
+ lx = max(1, lx)
77
+ ly = max(1, ly)
78
+ var lpqa = [Float32](repeating: 0, count: w * h * 4)
79
+
80
+ // Convert the image from RGBA to LPQA (composite atop the average color)
81
+ rgba.withUnsafeBytes { rgba in
82
+ lpqa.withUnsafeMutableBytes { lpqa in
83
+ var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count)
84
+ var lpqa = lpqa.baseAddress!.bindMemory(to: Float32.self, capacity: lpqa.count)
85
+ let n = w * h
86
+ var i = 0
87
+ while i < n {
88
+ let alpha = Float32(rgba[3]) / 255
89
+ let r = avg_r * (1 - alpha) + alpha / 255 * Float32(rgba[0])
90
+ let g = avg_g * (1 - alpha) + alpha / 255 * Float32(rgba[1])
91
+ let b = avg_b * (1 - alpha) + alpha / 255 * Float32(rgba[2])
92
+ lpqa[0] = (r + g + b) / 3
93
+ lpqa[1] = (r + g) / 2 - b
94
+ lpqa[2] = r - g
95
+ lpqa[3] = alpha
96
+ rgba = rgba.advanced(by: 4)
97
+ lpqa = lpqa.advanced(by: 4)
98
+ i += 1
99
+ }
100
+ }
101
+ }
102
+
103
+ // Encode using the DCT into DC (constant) and normalized AC (varying) terms
104
+ let encodeChannel = { (channel: UnsafePointer<Float32>, nx: Int, ny: Int) -> (Float32, [Float32], Float32) in
105
+ var dc: Float32 = 0
106
+ var ac: [Float32] = []
107
+ var scale: Float32 = 0
108
+ var fx = [Float32](repeating: 0, count: w)
109
+ fx.withUnsafeMutableBytes { fx in
110
+ let fx = fx.baseAddress!.bindMemory(to: Float32.self, capacity: fx.count)
111
+ var cy = 0
112
+ while cy < ny {
113
+ var cx = 0
114
+ while cx * ny < nx * (ny - cy) {
115
+ var ptr = channel
116
+ var f: Float32 = 0
117
+ var x = 0
118
+ while x < w {
119
+ let fw = Float32(w)
120
+ let fxx = Float32(x)
121
+ let fcx = Float32(cx)
122
+ fx[x] = cos(Float32.pi / fw * fcx * (fxx + 0.5))
123
+ x += 1
124
+ }
125
+ var y = 0
126
+ while y < h {
127
+ let fh = Float32(h)
128
+ let fyy = Float32(y)
129
+ let fcy = Float32(cy)
130
+ let fy = cos(Float32.pi / fh * fcy * (fyy + 0.5))
131
+ var x = 0
132
+ while x < w {
133
+ f += ptr.pointee * fx[x] * fy
134
+ x += 1
135
+ ptr = ptr.advanced(by: 4)
136
+ }
137
+ y += 1
138
+ }
139
+ f /= Float32(w * h)
140
+ if cx > 0 || cy > 0 {
141
+ ac.append(f)
142
+ scale = max(scale, abs(f))
143
+ } else {
144
+ dc = f
145
+ }
146
+ cx += 1
147
+ }
148
+ cy += 1
149
+ }
150
+ }
151
+ if scale > 0 {
152
+ let n = ac.count
153
+ var i = 0
154
+ while i < n {
155
+ ac[i] = 0.5 + 0.5 / scale * ac[i]
156
+ i += 1
157
+ }
158
+ }
159
+ return (dc, ac, scale)
160
+ }
161
+ let (
162
+ (l_dc, l_ac, l_scale),
163
+ (p_dc, p_ac, p_scale),
164
+ (q_dc, q_ac, q_scale),
165
+ (a_dc, a_ac, a_scale)
166
+ ) = lpqa.withUnsafeBytes { lpqa in
167
+ let lpqa = lpqa.baseAddress!.bindMemory(to: Float32.self, capacity: lpqa.count)
168
+ return (
169
+ encodeChannel(lpqa, max(3, lx), max(3, ly)),
170
+ encodeChannel(lpqa.advanced(by: 1), 3, 3),
171
+ encodeChannel(lpqa.advanced(by: 2), 3, 3),
172
+ hasAlpha ? encodeChannel(lpqa.advanced(by: 3), 5, 5) : (1, [], 1)
173
+ )
174
+ }
175
+
176
+ // Write the constants
177
+ let isLandscape = w > h
178
+ let fl_dc = round(63.0 * l_dc)
179
+ let fp_dc = round(31.5 + 31.5 * p_dc)
180
+ let fq_dc = round(31.5 + 31.5 * q_dc)
181
+ let fl_scale = round(31.0 * l_scale)
182
+ let il_dc = UInt32(fl_dc)
183
+ let ip_dc = UInt32(fp_dc)
184
+ let iq_dc = UInt32(fq_dc)
185
+ let il_scale = UInt32(fl_scale)
186
+ let ihasAlpha = UInt32(hasAlpha ? 1 : 0)
187
+ let header24 = il_dc | (ip_dc << 6) | (iq_dc << 12) | (il_scale << 18) | (ihasAlpha << 23)
188
+ let fp_scale = round(63.0 * p_scale)
189
+ let fq_scale = round(63.0 * q_scale)
190
+ let ilxy = UInt16(isLandscape ? ly : lx)
191
+ let ip_scale = UInt16(fp_scale)
192
+ let iq_scale = UInt16(fq_scale)
193
+ let iisLandscape = UInt16(isLandscape ? 1 : 0)
194
+ let header16 = ilxy | (ip_scale << 3) | (iq_scale << 9) | (iisLandscape << 15)
195
+ var hash = Data(capacity: 25)
196
+ hash.append(UInt8(header24 & 255))
197
+ hash.append(UInt8((header24 >> 8) & 255))
198
+ hash.append(UInt8(header24 >> 16))
199
+ hash.append(UInt8(header16 & 255))
200
+ hash.append(UInt8(header16 >> 8))
201
+ var isOdd = false
202
+ if hasAlpha {
203
+ let fa_dc = round(15.0 * a_dc)
204
+ let fa_scale = round(15.0 * a_scale)
205
+ let ia_dc = UInt8(fa_dc)
206
+ let ia_scale = UInt8(fa_scale)
207
+ hash.append(ia_dc | (ia_scale << 4))
208
+ }
209
+
210
+ // Write the varying factors
211
+ for ac in [l_ac, p_ac, q_ac] {
212
+ for f in ac {
213
+ let f15 = round(15.0 * f)
214
+ let i15 = UInt8(f15)
215
+ if isOdd {
216
+ hash[hash.count - 1] |= i15 << 4
217
+ } else {
218
+ hash.append(i15)
219
+ }
220
+ isOdd = !isOdd
221
+ }
222
+ }
223
+ if hasAlpha {
224
+ for f in a_ac {
225
+ let f15 = round(15.0 * f)
226
+ let i15 = UInt8(f15)
227
+ if isOdd {
228
+ hash[hash.count - 1] |= i15 << 4
229
+ } else {
230
+ hash.append(i15)
231
+ }
232
+ isOdd = !isOdd
233
+ }
234
+ }
235
+ return hash
236
+ }
237
+
238
+ func thumbHashToRGBA(hash: Data) -> (Int, Int, Data) {
239
+ // Read the constants
240
+ let h0 = UInt32(hash[0])
241
+ let h1 = UInt32(hash[1])
242
+ let h2 = UInt32(hash[2])
243
+ let h3 = UInt16(hash[3])
244
+ let h4 = UInt16(hash[4])
245
+ let header24 = h0 | (h1 << 8) | (h2 << 16)
246
+ let header16 = h3 | (h4 << 8)
247
+ let il_dc = header24 & 63
248
+ let ip_dc = (header24 >> 6) & 63
249
+ let iq_dc = (header24 >> 12) & 63
250
+ var l_dc = Float32(il_dc)
251
+ var p_dc = Float32(ip_dc)
252
+ var q_dc = Float32(iq_dc)
253
+ l_dc = l_dc / 63
254
+ p_dc = p_dc / 31.5 - 1
255
+ q_dc = q_dc / 31.5 - 1
256
+ let il_scale = (header24 >> 18) & 31
257
+ var l_scale = Float32(il_scale)
258
+ l_scale = l_scale / 31
259
+ let hasAlpha = (header24 >> 23) != 0
260
+ let ip_scale = (header16 >> 3) & 63
261
+ let iq_scale = (header16 >> 9) & 63
262
+ var p_scale = Float32(ip_scale)
263
+ var q_scale = Float32(iq_scale)
264
+ p_scale = p_scale / 63
265
+ q_scale = q_scale / 63
266
+ let isLandscape = (header16 >> 15) != 0
267
+ let lx16 = max(3, isLandscape ? hasAlpha ? 5 : 7 : header16 & 7)
268
+ let ly16 = max(3, isLandscape ? header16 & 7 : hasAlpha ? 5 : 7)
269
+ let lx = Int(lx16)
270
+ let ly = Int(ly16)
271
+ var a_dc = Float32(1)
272
+ var a_scale = Float32(1)
273
+ if hasAlpha {
274
+ let ia_dc = hash[5] & 15
275
+ let ia_scale = hash[5] >> 4
276
+ a_dc = Float32(ia_dc)
277
+ a_scale = Float32(ia_scale)
278
+ a_dc /= 15
279
+ a_scale /= 15
280
+ }
281
+
282
+ // Read the varying factors (boost saturation by 1.25x to compensate for quantization)
283
+ let ac_start = hasAlpha ? 6 : 5
284
+ var ac_index = 0
285
+ let decodeChannel = { (nx: Int, ny: Int, scale: Float32) -> [Float32] in
286
+ var ac: [Float32] = []
287
+ for cy in 0 ..< ny {
288
+ var cx = cy > 0 ? 0 : 1
289
+ while cx * ny < nx * (ny - cy) {
290
+ let iac = (hash[ac_start + (ac_index >> 1)] >> ((ac_index & 1) << 2)) & 15
291
+ var fac = Float32(iac)
292
+ fac = (fac / 7.5 - 1) * scale
293
+ ac.append(fac)
294
+ ac_index += 1
295
+ cx += 1
296
+ }
297
+ }
298
+ return ac
299
+ }
300
+ let l_ac = decodeChannel(lx, ly, l_scale)
301
+ let p_ac = decodeChannel(3, 3, p_scale * 1.25)
302
+ let q_ac = decodeChannel(3, 3, q_scale * 1.25)
303
+ let a_ac = hasAlpha ? decodeChannel(5, 5, a_scale) : []
304
+
305
+ // Decode using the DCT into RGB
306
+ let ratio = thumbHashToApproximateAspectRatio(hash: hash)
307
+ let fw = round(ratio > 1 ? 32 : 32 * ratio)
308
+ let fh = round(ratio > 1 ? 32 / ratio : 32)
309
+ let w = Int(fw)
310
+ let h = Int(fh)
311
+ var rgba = Data(count: w * h * 4)
312
+ let cx_stop = max(lx, hasAlpha ? 5 : 3)
313
+ let cy_stop = max(ly, hasAlpha ? 5 : 3)
314
+ var fx = [Float32](repeating: 0, count: cx_stop)
315
+ var fy = [Float32](repeating: 0, count: cy_stop)
316
+ fx.withUnsafeMutableBytes { fx in
317
+ let fx = fx.baseAddress!.bindMemory(to: Float32.self, capacity: fx.count)
318
+ fy.withUnsafeMutableBytes { fy in
319
+ let fy = fy.baseAddress!.bindMemory(to: Float32.self, capacity: fy.count)
320
+ rgba.withUnsafeMutableBytes { rgba in
321
+ var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count)
322
+ var y = 0
323
+ while y < h {
324
+ var x = 0
325
+ while x < w {
326
+ var l = l_dc
327
+ var p = p_dc
328
+ var q = q_dc
329
+ var a = a_dc
330
+
331
+ // Precompute the coefficients
332
+ var cx = 0
333
+ while cx < cx_stop {
334
+ let fw = Float32(w)
335
+ let fxx = Float32(x)
336
+ let fcx = Float32(cx)
337
+ fx[cx] = cos(Float32.pi / fw * (fxx + 0.5) * fcx)
338
+ cx += 1
339
+ }
340
+ var cy = 0
341
+ while cy < cy_stop {
342
+ let fh = Float32(h)
343
+ let fyy = Float32(y)
344
+ let fcy = Float32(cy)
345
+ fy[cy] = cos(Float32.pi / fh * (fyy + 0.5) * fcy)
346
+ cy += 1
347
+ }
348
+
349
+ // Decode L
350
+ var j = 0
351
+ cy = 0
352
+ while cy < ly {
353
+ var cx = cy > 0 ? 0 : 1
354
+ let fy2 = fy[cy] * 2
355
+ while cx * ly < lx * (ly - cy) {
356
+ l += l_ac[j] * fx[cx] * fy2
357
+ j += 1
358
+ cx += 1
359
+ }
360
+ cy += 1
361
+ }
362
+
363
+ // Decode P and Q
364
+ j = 0
365
+ cy = 0
366
+ while cy < 3 {
367
+ var cx = cy > 0 ? 0 : 1
368
+ let fy2 = fy[cy] * 2
369
+ while cx < 3 - cy {
370
+ let f = fx[cx] * fy2
371
+ p += p_ac[j] * f
372
+ q += q_ac[j] * f
373
+ j += 1
374
+ cx += 1
375
+ }
376
+ cy += 1
377
+ }
378
+
379
+ // Decode A
380
+ if hasAlpha {
381
+ j = 0
382
+ cy = 0
383
+ while cy < 5 {
384
+ var cx = cy > 0 ? 0 : 1
385
+ let fy2 = fy[cy] * 2
386
+ while cx < 5 - cy {
387
+ a += a_ac[j] * fx[cx] * fy2
388
+ j += 1
389
+ cx += 1
390
+ }
391
+ cy += 1
392
+ }
393
+ }
394
+
395
+ // Convert to RGB
396
+ var b = l - 2 / 3 * p
397
+ var r = (3 * l - b + q) / 2
398
+ var g = r - q
399
+ r = max(0, 255 * min(1, r))
400
+ g = max(0, 255 * min(1, g))
401
+ b = max(0, 255 * min(1, b))
402
+ a = max(0, 255 * min(1, a))
403
+ rgba[0] = UInt8(r)
404
+ rgba[1] = UInt8(g)
405
+ rgba[2] = UInt8(b)
406
+ rgba[3] = UInt8(a)
407
+ rgba = rgba.advanced(by: 4)
408
+ x += 1
409
+ }
410
+ y += 1
411
+ }
412
+ }
413
+ }
414
+ }
415
+ return (w, h, rgba)
416
+ }
417
+
418
+ func thumbHashToAverageRGBA(hash: Data) -> (Float32, Float32, Float32, Float32) {
419
+ let h0 = UInt32(hash[0])
420
+ let h1 = UInt32(hash[1])
421
+ let h2 = UInt32(hash[2])
422
+ let header = h0 | (h1 << 8) | (h2 << 16)
423
+ let il = header & 63
424
+ let ip = (header >> 6) & 63
425
+ let iq = (header >> 12) & 63
426
+ var l = Float32(il)
427
+ var p = Float32(ip)
428
+ var q = Float32(iq)
429
+ l = l / 63
430
+ p = p / 31.5 - 1
431
+ q = q / 31.5 - 1
432
+ let hasAlpha = (header >> 23) != 0
433
+ var a = Float32(1)
434
+ if hasAlpha {
435
+ let ia = hash[5] & 15
436
+ a = Float32(ia)
437
+ a = a / 15
438
+ }
439
+ let b = l - 2 / 3 * p
440
+ let r = (3 * l - b + q) / 2
441
+ let g = r - q
442
+ return (
443
+ max(0, min(1, r)),
444
+ max(0, min(1, g)),
445
+ max(0, min(1, b)),
446
+ a
447
+ )
448
+ }
449
+
450
+ func thumbHashToApproximateAspectRatio(hash: Data) -> Float32 {
451
+ let header = hash[3]
452
+ let hasAlpha = (hash[2] & 0x80) != 0
453
+ let isLandscape = (hash[4] & 0x80) != 0
454
+ let lx = isLandscape ? hasAlpha ? 5 : 7 : header & 7
455
+ let ly = isLandscape ? header & 7 : hasAlpha ? 5 : 7
456
+ return Float32(lx) / Float32(ly)
457
+ }
458
+
459
+ #if os(iOS)
460
+ import UIKit
461
+
462
+ func thumbHash(fromImage: UIImage) -> Data {
463
+ let size = fromImage.size
464
+ let w = Int(round(100 * size.width / max(size.width, size.height)))
465
+ let h = Int(round(100 * size.height / max(size.width, size.height)))
466
+ var rgba = Data(count: w * h * 4)
467
+ rgba.withUnsafeMutableBytes { rgba in
468
+ if
469
+ let space = fromImage.cgImage?.colorSpace,
470
+ let context = CGContext(
471
+ data: rgba.baseAddress,
472
+ width: w,
473
+ height: h,
474
+ bitsPerComponent: 8,
475
+ bytesPerRow: w * 4,
476
+ space: space,
477
+ bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
478
+ )
479
+ {
480
+ // EXIF orientation only works if you draw the UIImage, not the CGImage
481
+ context.concatenate(CGAffineTransform(1, 0, 0, -1, 0, CGFloat(h)))
482
+ UIGraphicsPushContext(context)
483
+ fromImage.draw(in: CGRect(x: 0, y: 0, width: w, height: h))
484
+ UIGraphicsPopContext()
485
+
486
+ // Convert from premultiplied alpha to unpremultiplied alpha
487
+ var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count)
488
+ let n = w * h
489
+ var i = 0
490
+ while i < n {
491
+ let a = UInt16(rgba[3])
492
+ if a > 0 && a < 255 {
493
+ var r = UInt16(rgba[0])
494
+ var g = UInt16(rgba[1])
495
+ var b = UInt16(rgba[2])
496
+ r = min(255, r * 255 / a)
497
+ g = min(255, g * 255 / a)
498
+ b = min(255, b * 255 / a)
499
+ rgba[0] = UInt8(r)
500
+ rgba[1] = UInt8(g)
501
+ rgba[2] = UInt8(b)
502
+ }
503
+ rgba = rgba.advanced(by: 4)
504
+ i += 1
505
+ }
506
+ }
507
+ }
508
+ return rgbaToThumbHash(w: w, h: h, rgba: rgba)
509
+ }
510
+
511
+ func image(fromThumbhash: Data) -> UIImage {
512
+ var (w, h, rgba) = thumbHashToRGBA(hash: fromThumbhash)
513
+ rgba.withUnsafeMutableBytes { rgba in
514
+ // Convert from unpremultiplied alpha to premultiplied alpha
515
+ var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count)
516
+ let n = w * h
517
+ var i = 0
518
+ while i < n {
519
+ let a = UInt16(rgba[3])
520
+ if a < 255 {
521
+ var r = UInt16(rgba[0])
522
+ var g = UInt16(rgba[1])
523
+ var b = UInt16(rgba[2])
524
+ r = min(255, r * a / 255)
525
+ g = min(255, g * a / 255)
526
+ b = min(255, b * a / 255)
527
+ rgba[0] = UInt8(r)
528
+ rgba[1] = UInt8(g)
529
+ rgba[2] = UInt8(b)
530
+ }
531
+ rgba = rgba.advanced(by: 4)
532
+ i += 1
533
+ }
534
+ }
535
+ let image = CGImage(
536
+ width: w,
537
+ height: h,
538
+ bitsPerComponent: 8,
539
+ bitsPerPixel: 32,
540
+ bytesPerRow: w * 4,
541
+ space: CGColorSpaceCreateDeviceRGB(),
542
+ bitmapInfo: CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue),
543
+ provider: CGDataProvider(data: rgba as CFData)!,
544
+ decode: nil,
545
+ shouldInterpolate: true,
546
+ intent: .perceptual
547
+ )
548
+ return UIImage(cgImage: image!)
549
+ }
550
+ #endif
@@ -0,0 +1,53 @@
1
+ import SDWebImage
2
+ import ExpoModulesCore
3
+
4
+ class ThumbhashLoader: NSObject, SDImageLoader {
5
+ // MARK: - SDImageLoader
6
+
7
+ func canRequestImage(for url: URL?) -> Bool {
8
+ return url?.scheme == "thumbhash"
9
+ }
10
+
11
+ func requestImage(
12
+ with url: URL?,
13
+ options: SDWebImageOptions = [],
14
+ context: [SDWebImageContextOption: Any]?,
15
+ progress progressBlock: SDImageLoaderProgressBlock?,
16
+ completed completedBlock: SDImageLoaderCompletedBlock? = nil
17
+ ) -> SDWebImageOperation? {
18
+ guard let url = url else {
19
+ let error = makeNSError(description: "URL provided to ThumbhashLoader is missing")
20
+ completedBlock?(nil, nil, error, false)
21
+ return nil
22
+ }
23
+
24
+ // The URI looks like this: thumbhash:/3OcRJYB4d3h\iIeHeEh3eIhw+j2w
25
+ // ThumbHash may include slashes which could break the structure of the URL, so we replace them
26
+ // with backslashes on the JS side and revert them back to slashes here, before generating the image.
27
+ var thumbhash = (url.pathComponents[1] ?? "").replacingOccurrences(of: "\\", with: "/")
28
+
29
+ // Thumbhashes with transparency cause the conversion to data to fail, padding the thumbhash string to correct length fixes that
30
+ let remainder = thumbhash.count % 4
31
+ if remainder > 0 {
32
+ thumbhash = thumbhash.padding(toLength: thumbhash.count + 4 - remainder, withPad: "=", startingAt: 0)
33
+ }
34
+
35
+ guard !thumbhash.isEmpty, let thumbhashData = Data(base64Encoded: thumbhash, options: .ignoreUnknownCharacters) else {
36
+ let error = makeNSError(description: "URL provided to ThumbhashLoader is invalid")
37
+ completedBlock?(nil, nil, error, false)
38
+ return nil
39
+ }
40
+
41
+ DispatchQueue.global(qos: .userInitiated).async {
42
+ let image = image(fromThumbhash: thumbhashData)
43
+ DispatchQueue.main.async {
44
+ completedBlock?(image, nil, nil, true)
45
+ }
46
+ }
47
+ return nil
48
+ }
49
+
50
+ func shouldBlockFailedURL(with url: URL, error: Error) -> Bool {
51
+ return true
52
+ }
53
+ }
package/jest.config.js ADDED
@@ -0,0 +1,3 @@
1
+ const zeroTestPackageConfig = require('expo-module-scripts/zero-test-package.jest.config');
2
+
3
+ module.exports = zeroTestPackageConfig;
@@ -0,0 +1,63 @@
1
+ /*
2
+ * Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * PROPRIETARY/CONFIDENTIAL
5
+ *
6
+ * Use is subject to license terms.
7
+ */
8
+
9
+ #include "ExpoImage.h"
10
+
11
+ #include <KeplerScript/appinit/KeplerScriptApplication.h>
12
+ #include <KeplerScript/components/ImageSourceBuilder.h>
13
+ #include <KeplerScript/logger/KPLog.h>
14
+ #include <KeplerScript/utils/image/KeplerScriptImageLoaderDelegate.h>
15
+
16
+ #include "utils/PrefetchImageLoaderDelegate.h"
17
+
18
+
19
+ ExpoImage::ExpoImage(std::shared_ptr<IExpoImageCacheHandler> handler) : volta::react::KeplerTurboModule("ExpoImage") {
20
+ volta::react::KPLog.debug("ExpoImage constructor {}", name());
21
+
22
+ if (handler) {
23
+ cacheHandler = std::move(handler);
24
+ } else {
25
+ cacheHandler = std::make_shared<ExpoImageCacheHandler>();
26
+ }
27
+ }
28
+
29
+ void ExpoImage::aggregateMethods(
30
+ volta::react::MethodAggregator<volta::react::KeplerTurboModule>& methodAggregator) const noexcept {
31
+ methodAggregator.addMethod("prefetch", 1, &ExpoImage::prefetch);
32
+ methodAggregator.addMethod("clearMemoryCache", 0, &ExpoImage::clearMemoryCache);
33
+ methodAggregator.addMethod("clearDiskCache", 0, &ExpoImage::clearDiskCache);
34
+ }
35
+
36
+ volta::react::Promise ExpoImage::prefetch(const std::vector<std::string> urls) {
37
+ volta::react::KPLog.debug("ExpoImage :: prefetch ");
38
+ return volta::react::Promise([handler = cacheHandler, urls = urls](std::shared_ptr<volta::react::Promise> promise) {
39
+ auto delegate = std::make_shared<PrefetchImageLoaderDelegate>(urls.size(), [promise](bool success) {
40
+ promise->resolve(success);
41
+ });
42
+
43
+ for (const auto& url : urls) {
44
+ // KeplerScriptImageCacheHandler::prefetch does not emit any events to `delegate`
45
+ // As a workaround `load` is used, but sometimes does not keep image in cache
46
+ const volta::ImageSource imageSourceUrl = volta::react::buildImageSource(url);
47
+ volta::ImageDetails details(imageSourceUrl, volta::ImageOptions{}, static_cast<uint8_t>(0));
48
+ handler->load(details, delegate);
49
+ // cacheHandler.prefetch(url, 1, delegate);
50
+ }
51
+ });
52
+ }
53
+
54
+
55
+ void ExpoImage::clearMemoryCache() {
56
+ volta::react::KPLog.debug("ExpoImage :: clearMemoryCache");
57
+ cacheHandler->clearMemoryCache();
58
+ }
59
+
60
+ void ExpoImage::clearDiskCache() {
61
+ volta::react::KPLog.debug("ExpoImage :: clearDiskCache");
62
+ cacheHandler->clearDiskCache();
63
+ }
@@ -0,0 +1,29 @@
1
+ /*
2
+ * Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * PROPRIETARY/CONFIDENTIAL
5
+ *
6
+ * Use is subject to license terms.
7
+ */
8
+ #pragma once
9
+
10
+ #include <KeplerScript/modules/KeplerTurboModule.h>
11
+ #include "ExpoImageCacheHandler.h"
12
+ #include <string>
13
+
14
+
15
+ class ExpoImage : public volta::react::KeplerTurboModule {
16
+ public:
17
+ explicit ExpoImage(std::shared_ptr<IExpoImageCacheHandler> cacheHandler = nullptr);
18
+
19
+ volta::react::Promise prefetch(const std::vector<std::string> urls);
20
+ void clearMemoryCache();
21
+ void clearDiskCache();
22
+
23
+ protected:
24
+ void aggregateMethods(
25
+ volta::react::MethodAggregator<volta::react::KeplerTurboModule>& methodAggregator) const noexcept override;
26
+
27
+ private:
28
+ std::shared_ptr<IExpoImageCacheHandler> cacheHandler = nullptr;
29
+ };