Skip to content

Commit 3a1fa60

Browse files
committed
feat: extract thumbhash to separate package
1 parent 3770778 commit 3a1fa60

92 files changed

Lines changed: 3036 additions & 558 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 449 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/__tests__/image-loaders.harness.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Platform } from 'react-native'
22
import { describe, expect, it } from 'react-native-harness'
33
import { Images, loadImage } from 'react-native-nitro-image'
4+
import { ThumbHash } from 'react-native-nitro-image-thumbhash'
45
import { WebImages } from 'react-native-nitro-web-image'
56

67
const RED = { r: 1, g: 0, b: 0, a: 1 }
@@ -96,19 +97,19 @@ describe('Images.loadFromFile', () => {
9697
})
9798
})
9899

99-
describe('Images.loadFromThumbHash', () => {
100+
describe('ThumbHash.decode', () => {
100101
it('decodes a ThumbHash buffer back into an Image', () => {
101102
const source = Images.createBlankImage(64, 64, true, BLUE).resize(32, 32)
102-
const hash = source.toThumbHash()
103-
const decoded = Images.loadFromThumbHash(hash)
103+
const hash = ThumbHash.encode(source)
104+
const decoded = ThumbHash.decode(hash)
104105
expect(decoded.width).toBeGreaterThan(0)
105106
expect(decoded.height).toBeGreaterThan(0)
106107
})
107108

108109
it('async variant decodes the ThumbHash buffer', async () => {
109110
const source = Images.createBlankImage(64, 64, true, GREEN).resize(32, 32)
110-
const hash = await source.toThumbHashAsync()
111-
const decoded = await Images.loadFromThumbHashAsync(hash)
111+
const hash = await ThumbHash.encodeAsync(source)
112+
const decoded = await ThumbHash.decodeAsync(hash)
112113
expect(decoded.width).toBeGreaterThan(0)
113114
expect(decoded.height).toBeGreaterThan(0)
114115
})

example/__tests__/image-utils.harness.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ import {
33
Images,
44
supportsHeicLoading,
55
supportsHeicWriting,
6-
thumbHashFromBase64String,
7-
thumbHashToBase64String,
86
} from 'react-native-nitro-image'
7+
import { ThumbHash } from 'react-native-nitro-image-thumbhash'
98

109
const expectTemporaryHeicPath = (path: string) => {
1110
expect(path.length).toBeGreaterThan(0)
@@ -58,7 +57,7 @@ describe('ImageUtils - HEIC round-trip', () => {
5857
})
5958
})
6059

61-
describe('ImageUtils - thumbHash round-trip', () => {
60+
describe('ThumbHash - round-trip', () => {
6261
it('encodes a small image to a thumbHash and converts to base64', () => {
6362
const image = Images.createBlankImage(64, 64, true, {
6463
r: 0.5,
@@ -67,14 +66,14 @@ describe('ImageUtils - thumbHash round-trip', () => {
6766
a: 1,
6867
})
6968
const small = image.resize(32, 32)
70-
const hash = small.toThumbHash()
69+
const hash = ThumbHash.encode(small)
7170
expect(hash.byteLength).toBeGreaterThan(0)
7271

73-
const base64 = thumbHashToBase64String(hash)
72+
const base64 = ThumbHash.toBase64String(hash)
7473
expect(base64.length).toBeGreaterThan(0)
7574
expect(base64).toMatch(/^[A-Za-z0-9+/=]+$/)
7675

77-
const restored = thumbHashFromBase64String(base64)
76+
const restored = ThumbHash.fromBase64String(base64)
7877
expect(restored.byteLength).toBe(hash.byteLength)
7978
})
8079

@@ -85,8 +84,8 @@ describe('ImageUtils - thumbHash round-trip', () => {
8584
b: 0,
8685
a: 1,
8786
})
88-
const hash = image.toThumbHash()
89-
const decoded = Images.loadFromThumbHash(hash)
87+
const hash = ThumbHash.encode(image)
88+
const decoded = ThumbHash.decode(hash)
9089
expect(decoded.width).toBeGreaterThan(0)
9190
expect(decoded.height).toBeGreaterThan(0)
9291
})

example/ios/Podfile.lock

Lines changed: 102 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ PODS:
33
- DoubleConversion (1.1.6)
44
- fast_float (8.0.0)
55
- FBLazyVector (0.81.0)
6-
- fmt (11.0.2)
6+
- fmt (12.1.0)
77
- glog (0.3.5)
88
- HarnessUI (1.1.0):
99
- boost
@@ -78,6 +78,37 @@ PODS:
7878
- ReactCommon/turbomodule/core
7979
- SocketRocket
8080
- Yoga
81+
- NitroImageThumbHash (0.0.1):
82+
- boost
83+
- DoubleConversion
84+
- fast_float
85+
- fmt
86+
- glog
87+
- hermes-engine
88+
- NitroImage
89+
- NitroModules
90+
- RCT-Folly
91+
- RCT-Folly/Fabric
92+
- RCTRequired
93+
- RCTTypeSafety
94+
- React-callinvoker
95+
- React-Core
96+
- React-debug
97+
- React-Fabric
98+
- React-featureflags
99+
- React-graphics
100+
- React-ImageManager
101+
- React-jsi
102+
- React-NativeModulesApple
103+
- React-RCTFabric
104+
- React-renderercss
105+
- React-rendererdebug
106+
- React-utils
107+
- ReactCodegen
108+
- ReactCommon/turbomodule/bridging
109+
- ReactCommon/turbomodule/core
110+
- SocketRocket
111+
- Yoga
81112
- NitroModules (0.35.5):
82113
- boost
83114
- DoubleConversion
@@ -143,20 +174,20 @@ PODS:
143174
- boost
144175
- DoubleConversion
145176
- fast_float (= 8.0.0)
146-
- fmt (= 11.0.2)
177+
- fmt (= 12.1.0)
147178
- glog
148179
- RCT-Folly/Default (= 2024.11.18.00)
149180
- RCT-Folly/Default (2024.11.18.00):
150181
- boost
151182
- DoubleConversion
152183
- fast_float (= 8.0.0)
153-
- fmt (= 11.0.2)
184+
- fmt (= 12.1.0)
154185
- glog
155186
- RCT-Folly/Fabric (2024.11.18.00):
156187
- boost
157188
- DoubleConversion
158189
- fast_float (= 8.0.0)
159-
- fmt (= 11.0.2)
190+
- fmt (= 12.1.0)
160191
- glog
161192
- RCTDeprecation (0.81.0)
162193
- RCTRequired (0.81.0)
@@ -2523,6 +2554,7 @@ DEPENDENCIES:
25232554
- "HarnessUI (from `../../node_modules/@react-native-harness/ui`)"
25242555
- hermes-engine (from `../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
25252556
- NitroImage (from `../../node_modules/react-native-nitro-image`)
2557+
- NitroImageThumbHash (from `../../node_modules/react-native-nitro-image-thumbhash`)
25262558
- NitroModules (from `../../node_modules/react-native-nitro-modules`)
25272559
- NitroWebImage (from `../../node_modules/react-native-nitro-web-image`)
25282560
- RCT-Folly (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
@@ -2623,6 +2655,8 @@ EXTERNAL SOURCES:
26232655
:tag: hermes-2025-07-07-RNv0.81.0-e0fc67142ec0763c6b6153ca2bf96df815539782
26242656
NitroImage:
26252657
:path: "../../node_modules/react-native-nitro-image"
2658+
NitroImageThumbHash:
2659+
:path: "../../node_modules/react-native-nitro-image-thumbhash"
26262660
NitroModules:
26272661
:path: "../../node_modules/react-native-nitro-modules"
26282662
NitroWebImage:
@@ -2767,85 +2801,86 @@ SPEC CHECKSUMS:
27672801
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
27682802
fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6
27692803
FBLazyVector: a867936a67af0d09c37935a1b900a1a3c795b6d1
2770-
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
2804+
fmt: 530618a01105dae0fa3a2f27c81ae11fa8f67eac
27712805
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
2772-
HarnessUI: 89b0197f0c33f741f001a9113d4979f17e8004bf
2806+
HarnessUI: 01740b858c62c55d42995d4ca459ead036b96c9a
27732807
hermes-engine: e7491a2038f2618c8cd444ed411a6deb350a3742
27742808
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
2775-
NitroImage: 358f7e81941d911eeb27ac37526731eb9672bc19
2776-
NitroModules: 6e73058747e2da022cb4fca5654f39eaf61e3c4f
2777-
NitroWebImage: 2e181d7eae11fad17d689eb054ab803f9d60a5e3
2778-
RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f
2809+
NitroImage: cef8d9c91d6d9247ec0569bb68148f7fba717026
2810+
NitroImageThumbHash: 992d4d9529f1770664b62e79f53e111381653bca
2811+
NitroModules: ea5dc6c43666f4a75e71e372eaca6d3605856e51
2812+
NitroWebImage: 3b45bf7868d02a3ea2ba20c9c9f955288384e31a
2813+
RCT-Folly: b29feb752b08042c62badaef7d453f3bb5e6ae23
27792814
RCTDeprecation: 0735ab4f6b3ec93a7f98187b5da74d7916e2cf4c
27802815
RCTRequired: 8fcc7801bfc433072287b0f24a662e2816e89d0c
27812816
RCTTypeSafety: 2b2be515d6b968bcba7a68c4179d8199bd8c9b58
27822817
React: 1000c0e96d8fb9fbdaf13f7d31d0b09db3cbb4ac
27832818
React-callinvoker: 7e52661bfaf5d8881a9cee049792627a00001fbe
2784-
React-Core: a9128dd77ec52432727bfbec8c55d17189f6c039
2785-
React-CoreModules: 4597116bd78ae2b183547e3700be0dc9537918e9
2786-
React-cxxreact: e3a02f535cc1f1b547ac1baafe6ac25552352362
2819+
React-Core: 949b436ddfe76cf47ac96375152de2f3506a8421
2820+
React-CoreModules: 0f27580d0d82d430fa4f2cf4d970b6ad1120d63a
2821+
React-cxxreact: 48754f11f47a29ea4800cbdd694c10f874a26b9b
27872822
React-debug: 7a23d96f709f437c5e08973d6e06d0a54dd180a1
2788-
React-defaultsnativemodule: f01b6e58a23efe4fc8d74db7dadeea112908f5d5
2789-
React-domnativemodule: 2d9796d40ab675e0f91ae8aae26c796b6e9a7499
2790-
React-Fabric: f4344b3a882292783de9a5404852023b6c4fdd2d
2791-
React-FabricComponents: 7c51eb1619473ae3ed92d8bbf5d5dd3be0c5ef9d
2792-
React-FabricImage: 9e743575e67a9c14242bec3ae0e26663eed641bb
2793-
React-featureflags: 5188951cc2fc81f4d249dc37e8f96dca7ef50e96
2794-
React-featureflagsnativemodule: 0fa7473065377ca4e5651c75614796326ef57aa8
2795-
React-graphics: f65ecd0a8c70f9c7dcdae322851c19b21c83ec27
2796-
React-hermes: 8418dae38a0513aa66aaa0a1b0904e55c4448644
2797-
React-idlecallbacksnativemodule: 540d6f743fcb595b26da8b182b28c878a1176a96
2798-
React-ImageManager: 5f9f1e33611a852d21a63e1de76d211fb04ac935
2799-
React-jserrorhandler: 9c0a7d69cd07c9ae08fab3a61150d526c0174c83
2800-
React-jsi: b711b7a11d77357beb95fa2eabd30c1ae34dcf40
2801-
React-jsiexecutor: 0d1c78e666c5be71ff7c0ff5ea7fb043e5b1f14c
2802-
React-jsinspector: 5fabd9f0be9390d5b5eb5fc88a8965d97e0c14ac
2803-
React-jsinspectorcdp: e78c65e25253999c0efd5e23c99e649e02fd0244
2804-
React-jsinspectornetwork: b02c6f7fe00e12b575a7faea0ed9ec9ddbc1c20f
2805-
React-jsinspectortracing: c6d8da3c8bcd939b8dcfd5113e247d56af932e1b
2806-
React-jsitooling: 4ca9b158d65909590daf6bf30a345b663eb71964
2807-
React-jsitracing: d9e9378d5a3e05febea2164a5d0c5fab06492872
2808-
React-logger: 839abfd18a3fbdf88132824de584b226d0c5cbce
2809-
React-Mapbuffer: bd5b1120c9bbaac6203eb288735e239f04e03009
2810-
React-microtasksnativemodule: 10892b00e612d79436022a11e5bc8bdf468a284f
2811-
react-native-safe-area-context: 0f4986a88ec555aff660503b483d6e4bd6980a9a
2812-
React-NativeModulesApple: 3f9e97a4a90eeec1ceade511f973b277632650bb
2823+
React-defaultsnativemodule: 569d9222a701ed3dc60a60b2ce066b5bd88da059
2824+
React-domnativemodule: 34474bda3973bfd0ca2ea9f1b3db20db5d504cc7
2825+
React-Fabric: 45c3e9b112075451e592f0e008cabd4b82575355
2826+
React-FabricComponents: a428f23938c27a073baacc069d484b3478df85f3
2827+
React-FabricImage: 4375129ba8a26e8a7074af1c2468870fb8aab723
2828+
React-featureflags: ed973a134993f3be204d0b2d385d386603c9a0af
2829+
React-featureflagsnativemodule: aa3e1dc86bc185344d4875e7cb40cce0bd28de76
2830+
React-graphics: b5b8709a8216075bb6a5f9e7bb68881212d924ee
2831+
React-hermes: c543ffa2866304c582bdcb135c184e0f776f0d0b
2832+
React-idlecallbacksnativemodule: f19c4060b12fffc3ad33ce5de190338751b462ef
2833+
React-ImageManager: ecaf317aa5dff5eebba178b0813ef998c62547ea
2834+
React-jserrorhandler: 92eea1ee4f8c56b466b34e0065def59805e5d3a9
2835+
React-jsi: 7336786a4a14c473d104e6b37df935620d218fcd
2836+
React-jsiexecutor: 7c750f5b63fbc071d0f0e56e86f1a1589914f7b1
2837+
React-jsinspector: da5f336c1aa174a05885d061559a92e1d07b8a80
2838+
React-jsinspectorcdp: 0e807e4c2dc8ae8a07f0a6bfe50377f442079ba3
2839+
React-jsinspectornetwork: 3399384f2b6b70b287d8b9675452af4cec21dc65
2840+
React-jsinspectortracing: 030af0e9dca9a4eaa1d0ba258c7bd859fb90f61d
2841+
React-jsitooling: f8ed67814b17ebb124c48fccdf587ee1e02f16f4
2842+
React-jsitracing: 5cf6b84d46a4653895e30956a0ce3a315244c10a
2843+
React-logger: 04ce9229cb57db2c2a8164eaec1105f89da7fb22
2844+
React-Mapbuffer: e402e7a0535b2213c50727553621480fe8cd8ade
2845+
React-microtasksnativemodule: a63ce5595016996a9bac1f10c70a7a7fe6506649
2846+
react-native-safe-area-context: befb5404eb8a16fdc07fa2bebab3568ecabcbb8a
2847+
React-NativeModulesApple: b3766e1f87b08064ebc459b9e1538da2447ca874
28132848
React-oscompat: 34f3d3c06cadcbc470bc4509c717fb9b919eaa8b
2814-
React-perflogger: 95dff8cc9901777360716cbdcb2998849f133a4f
2815-
React-performancetimeline: 2937a27399b52ca8baf46f22c39087f617e626b5
2849+
React-perflogger: a1edb025fd5d44f61bf09307e248f7608d7b2dcf
2850+
React-performancetimeline: 1f86dc9782e3fe78727c5fbb3e2178b9fd1aa6fd
28162851
React-RCTActionSheet: 550c9c6c2e7dcd85a51954dc08e2f3837a148e7c
2817-
React-RCTAnimation: 0008bfe273566acd3128da13598073383325ac7a
2818-
React-RCTAppDelegate: 8b9452baef5548856a22f4710d4135cf68746cf5
2819-
React-RCTBlob: 60006ab743e5fd807aaf536092f5ce86e87df526
2820-
React-RCTFabric: 8d5d1006b3812c35fd0f37c117ff7bcf6449e20d
2821-
React-RCTFBReactNativeSpec: 3cb4265fa9a4e4f8250ae89feb345edc542731da
2822-
React-RCTImage: f40a2ee0f79c1666e8b81da4ea2d9d1182c94962
2823-
React-RCTLinking: cfe6995bdd8d08d0bb0df12771f4d28fd5fd54ff
2824-
React-RCTNetwork: 565c0cd46313f2cad0e4db70a44958b2842c372b
2825-
React-RCTRuntime: 971a71a42d8979475a380e5179083302e5506cdd
2826-
React-RCTSettings: afcec6060d916e9c0410004ad8419d45f9dbcd36
2827-
React-RCTText: 952f2a1b618d3f3872e7e5a82aefc5e5082c59aa
2828-
React-RCTVibration: 2a7e7497ffefa135c7f0fee8ee10e3505ab5cc61
2852+
React-RCTAnimation: 19d4bb6d2190983d1354b096b7b65dbd591924da
2853+
React-RCTAppDelegate: 6c71d16eef920831a312ff363355fc3b99c02a98
2854+
React-RCTBlob: b81a0cffe1a083bcf9d8aa9f27f4d37864579e90
2855+
React-RCTFabric: 01005d2fa799bba6e21aae18820498f56fe0be5f
2856+
React-RCTFBReactNativeSpec: 5adb84a81c4ed7a1f2661835d166e4b2c4320cd4
2857+
React-RCTImage: 607e5e373fb56d72417464bd82e8046af81ab502
2858+
React-RCTLinking: 301434c7bf1100458be5a3866326ba33491e3687
2859+
React-RCTNetwork: a118a47bd123ac96c9877e04f5731a1d6545aba5
2860+
React-RCTRuntime: 85fdbf469fe8a12c4db6c836731b190efc33d11d
2861+
React-RCTSettings: 5a5aa2cf9ac40f7a8897cc0f9d945ac803886604
2862+
React-RCTText: e6e00bee9847a8af1218079b73c8bfed16c75b8d
2863+
React-RCTVibration: 5a05fa0ef05ee73d074a3314e57586afc969f1ba
28292864
React-rendererconsistency: c2cb23365f4a7b511893748fe8cad1830bbae637
2830-
React-renderercss: 621b2b85af14694e93c2bcd63986fb57bcceab2e
2831-
React-rendererdebug: 4ba0769131e20347b900757fcac3c7919b27080c
2832-
React-RuntimeApple: c1a211351c14d35805d45a94094cfb3e5649552c
2833-
React-RuntimeCore: b7c7d8dffa3728a9e9616e0e8b5b6b41037ebcca
2834-
React-runtimeexecutor: e931e48afc888fe459f6ffb481971e23bb34f7ee
2835-
React-RuntimeHermes: 5763230801ee57d9f414818f48e44b874f3ce1be
2836-
React-runtimescheduler: b2e99f9702705fc8c11cf3c51f9911f478ee2210
2865+
React-renderercss: 0c1472d6572c05e493aee476598c3ed6234b6c33
2866+
React-rendererdebug: d6335da9730fa5a151537aa976a16d48de6135e2
2867+
React-RuntimeApple: 5684c2a5d8768e5728a5817c21e5dba798d54c58
2868+
React-RuntimeCore: 52428a1b48fb3c50ddf4dd5eee494486e4ecffc6
2869+
React-runtimeexecutor: 1b4e99e5c27d2cb8bdeca9773ff5f1a8eac7709c
2870+
React-RuntimeHermes: a688639233a3ea44b4f8e4d448f51943d7e00815
2871+
React-runtimescheduler: b833f0fc8c788329a497e93f55ce30508f56307a
28372872
React-timing: 25e8229ad1cf6874e9f0711515213cb2bc322215
2838-
React-utils: 7ea6e4d300c43a763e4e08091413aec962588f93
2839-
ReactAppDependencyProvider: 562d731311d0524a577cf8a01faa97874bacbdfe
2840-
ReactCodegen: 0fc801cfa34581b2acfb9568ef6180042043826a
2841-
ReactCommon: c235ebd26d63fde9a2dfa72cee9f8294b910fee1
2842-
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
2843-
RNScreens: 7179cc1ba31b4e18ed29f10abf20c24a7961cf4c
2873+
React-utils: 068cec677032ba78ca0700f2dcbe6d08a0939647
2874+
ReactAppDependencyProvider: c91900fa724baee992f01c05eeb4c9e01a807f78
2875+
ReactCodegen: c3a2e945d68bcf8839624acaf1b276acbb41e9ba
2876+
ReactCommon: 116d6ee71679243698620d8cd9a9042541e44aa6
2877+
RNFastImage: 462a183c4b0b6b26fdfd639e1ed6ba37536c3b87
2878+
RNScreens: 7f643ee0fd1407dc5085c7795460bd93da113b8f
28442879
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
28452880
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
28462881
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
2847-
Yoga: b01392348aeea02064c21a2762a42893d82b60a7
2882+
Yoga: 00013dd9cde63a2d98e8002fcc4f5ddb66c10782
28482883

28492884
PODFILE CHECKSUM: 8c90c25c7a6bc16ec7b3ed7968df16467ab0fc35
28502885

2851-
COCOAPODS: 1.15.2
2886+
COCOAPODS: 1.16.2

example/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"react-native-fast-image": "^8.6.3",
2020
"react-native-harness": "^1.0.0-alpha.25",
2121
"react-native-nitro-image": "workspace:*",
22+
"react-native-nitro-image-thumbhash": "workspace:*",
2223
"react-native-nitro-web-image": "workspace:*",
2324
"react-native-nitro-modules": "0.35.5",
2425
"react-native-safe-area-context": "^5.6.0",

example/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import { createStaticNavigation } from '@react-navigation/native'
1010
import { EmptyTab } from './EmptyTab'
1111
import { FastImageTab } from './FastImageTab'
1212
import { NitroImageTab } from './NitroImageTab'
13+
import { ThumbHashTab } from './ThumbHashTab'
1314

1415
const Tabs = createBottomTabNavigator({
1516
detachInactiveScreens: false,
1617
screens: {
1718
Empty: EmptyTab,
1819
FastImage: FastImageTab,
1920
NitroImage: NitroImageTab,
21+
ThumbHash: ThumbHashTab,
2022
},
2123
})
2224
const Navigation = createStaticNavigation(Tabs)

0 commit comments

Comments
 (0)