react-native-nitro-image-pipeline 1.3.1 → 1.4.0

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/README.md CHANGED
@@ -259,11 +259,17 @@ omitted.
259
259
 
260
260
  ### `preLoadImage(url)`
261
261
 
262
- Prefetches a single image into the cache. Returns `Promise<void>`.
262
+ Prefetches a single image into the **disk cache**, without decoding it. Returns `Promise<void>`.
263
+
264
+ Prefetching only pays the network and disk I/O cost up front — no bitmap is decoded or held in
265
+ memory, so prefetching a long list of URLs doesn't balloon RAM. The image is decoded (at the
266
+ `resize` target size, when one is given) the first time `loadImage`/`useImage`/`<PipelineImage>`
267
+ actually displays it.
263
268
 
264
269
  ### `preLoadImages(urls)`
265
270
 
266
- Prefetches multiple images into the cache. Returns `Promise<void>`.
271
+ Prefetches multiple images into the disk cache same behavior as `preLoadImage`, for a batch.
272
+ Returns `Promise<void>`.
267
273
 
268
274
  ### `gaussianBlur(image, radius)`
269
275
 
@@ -300,10 +306,52 @@ RenderScript's true Gaussian, downscaling first when sigma exceeds the single-pa
300
306
  ~10.6px and compensating the radius so the result is unchanged. Both clamp at the edges, so blurred
301
307
  images keep their borders instead of fading out.
302
308
 
309
+ ### `setMemoryCacheLimit(bytes)`
310
+
311
+ Caps the in-memory cache of decoded bitmaps at `bytes`, evicting least-recently-used entries
312
+ immediately if the cache is currently larger. Pass `0` to disable in-memory caching entirely — the
313
+ disk cache keeps working. Synchronous; throws on negative or non-finite values.
314
+
315
+ ```ts
316
+ // Keep at most 32 MB of decoded bitmaps in RAM
317
+ NitroImagePipeline.setMemoryCacheLimit(32 * 1024 * 1024);
318
+
319
+ // Or opt out of decoded-bitmap caching altogether
320
+ NitroImagePipeline.setMemoryCacheLimit(0);
321
+ ```
322
+
303
323
  ### `clearCache()`
304
324
 
305
325
  Removes all cached images from memory and disk. Returns `Promise<void>` that resolves once both caches are cleared.
306
326
 
327
+ ## Memory usage
328
+
329
+ The pipeline is set up so RAM scales with what you display, not with what you download:
330
+
331
+ - **Pass `resize` (or just use `<PipelineImage>`, which derives it from layout).** With a target
332
+ size known, both platforms decode the source *near that size* instead of at full resolution —
333
+ iOS via a downsampled thumbnail decode, Android via Coil's subsampling. Without `resize`, a
334
+ 48 MP photo decompresses to ~190 MB of bitmap no matter how small you display it.
335
+ - **Prefetching stores bytes, not bitmaps.** `preLoadImage(s)` writes the download to the disk
336
+ cache and skips decoding entirely.
337
+ - **The in-memory cache is capped and tunable** (defaults: 128 MB on iOS, 25% of the app's memory
338
+ class on Android). It holds decoded bitmaps for instant re-display and evicts
339
+ least-recently-used entries — also in response to memory warnings and backgrounding. Memory
340
+ profilers attribute this cache to the app; a plateau at the cap is expected and evictable, not a
341
+ leak. Lower the cap with [`setMemoryCacheLimit`](#setmemorycachelimitbytes), use `cache: 'disk'`
342
+ or `cache: 'none'` on images you won't show again soon, and `clearCache()` to drop everything.
343
+ - **Coming from a setup with no decoded-image cache** (e.g. loading files you downloaded
344
+ yourself)? Steady-state RAM will read higher here *by design*: after screens unmount, the cache
345
+ keeps their bitmaps around for instant re-display. For the old memory profile with the
346
+ pipeline's features intact, pass `cache: 'disk'` on your requests or call
347
+ `setMemoryCacheLimit(0)` once — RAM then holds only the images currently referenced, and
348
+ re-displays decode from the disk cache (cheap, since decodes are subsampled to the target size).
349
+ - **Android + `Image.dispose()`:** only call `dispose()` on images loaded with `cache: 'disk'` /
350
+ `cache: 'none'` (or with the memory cache disabled). With the memory cache on, the returned
351
+ image shares its bitmap with the cache, and disposing recycles a bitmap the cache may serve
352
+ again. Without `dispose()`, images are freed by the JS garbage collector — their bitmap size is
353
+ reported to it, so unreferenced images do get collected under pressure.
354
+
307
355
  ## Upgrading from 0.3.x
308
356
 
309
357
  `blur` and `gaussianBlur(image, radius)` changed meaning in 1.0. They used to hand the number
@@ -3,8 +3,11 @@ package com.margelo.nitro.nitroimagepipeline
3
3
  import android.content.Context
4
4
  import androidx.core.graphics.drawable.toBitmap
5
5
  import coil3.BitmapImage
6
+ import coil3.ColorImage
6
7
  import coil3.DrawableImage
7
8
  import coil3.ImageLoader
9
+ import coil3.decode.DecodeResult
10
+ import coil3.decode.Decoder
8
11
  import coil3.disk.DiskCache
9
12
  import coil3.memory.MemoryCache
10
13
  import coil3.network.okhttp.OkHttpNetworkFetcherFactory
@@ -117,21 +120,25 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
117
120
  }
118
121
  }
119
122
 
123
+ // Preloading warms the disk cache only: the memory cache is skipped and the
124
+ // decode step is replaced with a no-op (Coil's documented preload pattern),
125
+ // so prefetching N URLs costs network + disk I/O instead of N full-resolution
126
+ // bitmaps on the heap. The image is decoded — subsampled to the requested
127
+ // size — only when a loadImage actually displays it.
128
+ private fun preloadRequest(url: String): ImageRequest =
129
+ ImageRequest.Builder(context)
130
+ .data(url)
131
+ .memoryCachePolicy(CachePolicy.DISABLED)
132
+ .decoderFactory { _, _, _ -> Decoder { DecodeResult(ColorImage(), false) } }
133
+ .build()
134
+
120
135
  override fun preLoadImage(url: String): Promise<Unit> = Promise.async {
121
- val request = ImageRequest.Builder(context).data(url).build()
122
- imageLoader.execute(request)
136
+ imageLoader.execute(preloadRequest(url))
123
137
  }
124
138
 
125
139
  override fun preLoadImages(urls: Array<String>): Promise<Unit> = Promise.async {
126
140
  coroutineScope {
127
- urls
128
- .map { url ->
129
- async {
130
- val request = ImageRequest.Builder(context).data(url).build()
131
- imageLoader.execute(request)
132
- }
133
- }
134
- .awaitAll()
141
+ urls.map { url -> async { imageLoader.execute(preloadRequest(url)) } }.awaitAll()
135
142
  }
136
143
  Unit
137
144
  }
@@ -150,6 +157,18 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
150
157
  }
151
158
  }
152
159
 
160
+ override fun setMemoryCacheLimit(bytes: Double) {
161
+ require(bytes >= 0 && bytes.isFinite()) {
162
+ "Memory cache limit must be a non-negative, finite number of bytes (got $bytes)"
163
+ }
164
+ imageLoader.memoryCache?.apply {
165
+ maxSize = bytes.toLong()
166
+ // Setting maxSize only affects future inserts; evict down to it now so
167
+ // the call frees memory immediately.
168
+ trimToSize(bytes.toLong())
169
+ }
170
+ }
171
+
153
172
  override fun clearCache(): Promise<Unit> = Promise.async {
154
173
  imageLoader.memoryCache?.clear()
155
174
  // DiskCache.clear() does file I/O; keep it off the JS thread but await
@@ -95,10 +95,28 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
95
95
  // the original download, so they survive memory eviction and
96
96
  // restarts without dropping the original for other variants.
97
97
  configuration.dataCachePolicy = .storeAll
98
+ // A private, capped memory cache. The default (ImageCache.shared)
99
+ // sizes itself at 15% of physical RAM — up to 768 MB of decoded
100
+ // bitmaps on modern devices before anything is evicted, which shows
101
+ // up as "high RAM usage" even though it is all evictable cache.
102
+ // 128 MB still holds ~10 full-screen bitmaps or hundreds of list
103
+ // thumbnails, and the LRU cache keeps trimming itself on memory
104
+ // warnings and when the app enters the background.
105
+ configuration.imageCache = ImageCache(
106
+ costLimit: min(ImageCache.defaultCostLimit, 128 * 1024 * 1024)
107
+ )
98
108
  return ImagePipeline(configuration: configuration)
99
109
  }()
100
110
 
101
- private static let sharedPrefetcher = ImagePrefetcher(pipeline: sharedPipeline)
111
+ // `.diskCache` stores the downloaded data without decoding it, so
112
+ // prefetching N URLs costs network + disk I/O instead of N decoded
113
+ // full-resolution bitmaps parked in the memory cache (the default
114
+ // `.memoryCache` destination). The image is decoded — at the requested
115
+ // target size — only when a loadImage actually displays it.
116
+ private static let sharedPrefetcher = ImagePrefetcher(
117
+ pipeline: sharedPipeline,
118
+ destination: .diskCache
119
+ )
102
120
 
103
121
  private var pipeline: ImagePipeline { Self.sharedPipeline }
104
122
  private var prefetcher: ImagePrefetcher { Self.sharedPrefetcher }
@@ -133,13 +151,21 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
133
151
  }
134
152
  }
135
153
 
154
+ /// The target size in pixels, or `nil` when no (valid) resize was requested.
155
+ private static func resizeSize(for options: Options?) -> CGSize? {
156
+ guard let resize = options?.resize, resize.width > 0, resize.height > 0 else {
157
+ return nil
158
+ }
159
+ return CGSize(width: resize.width, height: resize.height)
160
+ }
161
+
136
162
  private static func processors(for options: Options?) -> [any ImageProcessing] {
137
163
  var processors: [any ImageProcessing] = []
138
164
  // Resize first: blur sigma and corner radii are defined in pixels
139
165
  // of the bitmap they run on, so they must see the final size.
140
- if let resize = options?.resize, resize.width > 0, resize.height > 0 {
166
+ if let size = resizeSize(for: options) {
141
167
  processors.append(ImageProcessors.Resize(
142
- size: CGSize(width: resize.width, height: resize.height),
168
+ size: size,
143
169
  unit: .pixels,
144
170
  contentMode: .aspectFill,
145
171
  crop: true,
@@ -161,11 +187,24 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
161
187
  throw RuntimeError.error(withMessage: "Invalid URL: \(url)")
162
188
  }
163
189
 
164
- let imgRequest = ImageRequest(
190
+ var imgRequest = ImageRequest(
165
191
  url: imageUrl,
166
192
  processors: Self.processors(for: options),
167
193
  options: Self.cacheOptions(for: options?.cache)
168
194
  )
195
+ // With a target size known, decode near it (aspect-fill, so the
196
+ // decoded image always covers the target) instead of at full
197
+ // resolution — a 48 MP photo displayed as a 300 pt card would
198
+ // otherwise decompress to ~190 MB before Resize shrinks it.
199
+ // Matches Android, where the request's size() drives subsampling;
200
+ // the exact size and crop still come from the Resize processor.
201
+ if let size = Self.resizeSize(for: options) {
202
+ imgRequest.thumbnail = ImageRequest.ThumbnailOptions(
203
+ size: size,
204
+ unit: .pixels,
205
+ contentMode: .aspectFill
206
+ )
207
+ }
169
208
 
170
209
  let image = try await self.pipeline.image(for: imgRequest)
171
210
  return HybridImage(uiImage: image)
@@ -189,6 +228,22 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
189
228
  }
190
229
  }
191
230
 
231
+ func setMemoryCacheLimit(bytes: Double) throws {
232
+ guard bytes >= 0, bytes.isFinite else {
233
+ throw RuntimeError.error(
234
+ withMessage: "Memory cache limit must be a non-negative, finite number of bytes (got \(bytes))"
235
+ )
236
+ }
237
+ guard let cache = pipeline.configuration.imageCache as? ImageCache else { return }
238
+ // Double(Int.max) rounds up to 2^63, so Int(_:) of the clamped value
239
+ // would trap; branch instead of min-ing.
240
+ let limit = bytes >= Double(Int.max) ? Int.max : Int(bytes)
241
+ cache.costLimit = limit
242
+ // Setting the limit only affects future inserts; evict down to it now
243
+ // so the call frees memory immediately.
244
+ cache.trim(toCost: limit)
245
+ }
246
+
192
247
  func clearCache() throws -> Promise<Void> {
193
248
  return Promise.async {
194
249
  self.pipeline.cache.removeAll()
@@ -73,6 +73,16 @@ export interface NitroImagePipeline extends HybridObject<{
73
73
  * {@linkcode Options.blur}.
74
74
  */
75
75
  gaussianBlur(image: Image, radius: number): Promise<Image>;
76
+ /**
77
+ * Caps the in-memory cache of decoded bitmaps at `bytes`, evicting
78
+ * least-recently-used entries immediately if it is currently larger. Pass
79
+ * `0` to disable in-memory caching entirely (the disk cache still works).
80
+ *
81
+ * Defaults: 128 MB on iOS, 25% of the app's memory class on Android. The
82
+ * cache trades RAM for instant re-display; lower it (or use
83
+ * `cache: 'disk'` per request) in memory-constrained apps.
84
+ */
85
+ setMemoryCacheLimit(bytes: number): void;
76
86
  clearCache(): Promise<void>;
77
87
  }
78
88
  //# sourceMappingURL=nitro-image-toolkit.nitro.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"nitro-image-toolkit.nitro.d.ts","sourceRoot":"","sources":["../../../../src/specs/nitro-image-toolkit.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,OAAO,GAAG;IACpB;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACpC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,MAAM,WAAW,kBAAmB,SAAQ,YAAY,CAAC;IACvD,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,QAAQ,CAAC;CACnB,CAAC;IACA,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1D,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAG3D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B"}
1
+ {"version":3,"file":"nitro-image-toolkit.nitro.d.ts","sourceRoot":"","sources":["../../../../src/specs/nitro-image-toolkit.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,OAAO,GAAG;IACpB;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACpC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,MAAM,WAAW,kBAAmB,SAAQ,YAAY,CAAC;IACvD,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,QAAQ,CAAC;CACnB,CAAC;IACA,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1D,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAG3D;;;;;;;;OAQG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B"}
@@ -142,6 +142,10 @@ namespace margelo::nitro::nitroimagepipeline {
142
142
  return __promise;
143
143
  }();
144
144
  }
145
+ void JHybridNitroImagePipelineSpec::setMemoryCacheLimit(double bytes) {
146
+ static const auto method = _javaPart->javaClassStatic()->getMethod<void(double /* bytes */)>("setMemoryCacheLimit");
147
+ method(_javaPart, bytes);
148
+ }
145
149
  std::shared_ptr<Promise<void>> JHybridNitroImagePipelineSpec::clearCache() {
146
150
  static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>()>("clearCache");
147
151
  auto __result = method(_javaPart);
@@ -58,6 +58,7 @@ namespace margelo::nitro::nitroimagepipeline {
58
58
  std::shared_ptr<Promise<void>> preLoadImage(const std::string& url) override;
59
59
  std::shared_ptr<Promise<void>> preLoadImages(const std::vector<std::string>& urls) override;
60
60
  std::shared_ptr<Promise<std::shared_ptr<margelo::nitro::image::HybridImageSpec>>> gaussianBlur(const std::shared_ptr<margelo::nitro::image::HybridImageSpec>& image, double radius) override;
61
+ void setMemoryCacheLimit(double bytes) override;
61
62
  std::shared_ptr<Promise<void>> clearCache() override;
62
63
 
63
64
  private:
@@ -47,6 +47,10 @@ abstract class HybridNitroImagePipelineSpec: HybridObject() {
47
47
  @Keep
48
48
  abstract fun gaussianBlur(image: com.margelo.nitro.image.HybridImageSpec, radius: Double): Promise<com.margelo.nitro.image.HybridImageSpec>
49
49
 
50
+ @DoNotStrip
51
+ @Keep
52
+ abstract fun setMemoryCacheLimit(bytes: Double): Unit
53
+
50
54
  @DoNotStrip
51
55
  @Keep
52
56
  abstract fun clearCache(): Promise<Unit>
@@ -297,5 +297,14 @@ namespace margelo::nitro::nitroimagepipeline::bridge::swift {
297
297
  inline Result_std__shared_ptr_Promise_void___ create_Result_std__shared_ptr_Promise_void___(const std::exception_ptr& error) noexcept {
298
298
  return Result<std::shared_ptr<Promise<void>>>::withError(error);
299
299
  }
300
+
301
+ // pragma MARK: Result<void>
302
+ using Result_void_ = Result<void>;
303
+ inline Result_void_ create_Result_void_() noexcept {
304
+ return Result<void>::withValue();
305
+ }
306
+ inline Result_void_ create_Result_void_(const std::exception_ptr& error) noexcept {
307
+ return Result<void>::withError(error);
308
+ }
300
309
 
301
310
  } // namespace margelo::nitro::nitroimagepipeline::bridge::swift
@@ -117,6 +117,12 @@ namespace margelo::nitro::nitroimagepipeline {
117
117
  auto __value = std::move(__result.value());
118
118
  return __value;
119
119
  }
120
+ inline void setMemoryCacheLimit(double bytes) override {
121
+ auto __result = _swiftPart.setMemoryCacheLimit(std::forward<decltype(bytes)>(bytes));
122
+ if (__result.hasError()) [[unlikely]] {
123
+ std::rethrow_exception(__result.error());
124
+ }
125
+ }
120
126
  inline std::shared_ptr<Promise<void>> clearCache() override {
121
127
  auto __result = _swiftPart.clearCache();
122
128
  if (__result.hasError()) [[unlikely]] {
@@ -18,6 +18,7 @@ public protocol HybridNitroImagePipelineSpec_protocol: HybridObject {
18
18
  func preLoadImage(url: String) throws -> Promise<Void>
19
19
  func preLoadImages(urls: [String]) throws -> Promise<Void>
20
20
  func gaussianBlur(image: (any HybridImageSpec), radius: Double) throws -> Promise<(any HybridImageSpec)>
21
+ func setMemoryCacheLimit(bytes: Double) throws -> Void
21
22
  func clearCache() throws -> Promise<Void>
22
23
  }
23
24
 
@@ -211,6 +211,17 @@ open class HybridNitroImagePipelineSpec_cxx {
211
211
  }
212
212
  }
213
213
 
214
+ @inline(__always)
215
+ public final func setMemoryCacheLimit(bytes: Double) -> bridge.Result_void_ {
216
+ do {
217
+ try self.__implementation.setMemoryCacheLimit(bytes: bytes)
218
+ return bridge.create_Result_void_()
219
+ } catch (let __error) {
220
+ let __exceptionPtr = __error.toCpp()
221
+ return bridge.create_Result_void_(__exceptionPtr)
222
+ }
223
+ }
224
+
214
225
  @inline(__always)
215
226
  public final func clearCache() -> bridge.Result_std__shared_ptr_Promise_void___ {
216
227
  do {
@@ -18,6 +18,7 @@ namespace margelo::nitro::nitroimagepipeline {
18
18
  prototype.registerHybridMethod("preLoadImage", &HybridNitroImagePipelineSpec::preLoadImage);
19
19
  prototype.registerHybridMethod("preLoadImages", &HybridNitroImagePipelineSpec::preLoadImages);
20
20
  prototype.registerHybridMethod("gaussianBlur", &HybridNitroImagePipelineSpec::gaussianBlur);
21
+ prototype.registerHybridMethod("setMemoryCacheLimit", &HybridNitroImagePipelineSpec::setMemoryCacheLimit);
21
22
  prototype.registerHybridMethod("clearCache", &HybridNitroImagePipelineSpec::clearCache);
22
23
  });
23
24
  }
@@ -61,6 +61,7 @@ namespace margelo::nitro::nitroimagepipeline {
61
61
  virtual std::shared_ptr<Promise<void>> preLoadImage(const std::string& url) = 0;
62
62
  virtual std::shared_ptr<Promise<void>> preLoadImages(const std::vector<std::string>& urls) = 0;
63
63
  virtual std::shared_ptr<Promise<std::shared_ptr<margelo::nitro::image::HybridImageSpec>>> gaussianBlur(const std::shared_ptr<margelo::nitro::image::HybridImageSpec>& image, double radius) = 0;
64
+ virtual void setMemoryCacheLimit(double bytes) = 0;
64
65
  virtual std::shared_ptr<Promise<void>> clearCache() = 0;
65
66
 
66
67
  protected:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-image-pipeline",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "High-performance image loading, caching, and processing for React Native, built with Nitro Modules",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/module/index.js",
@@ -80,5 +80,16 @@ export interface NitroImagePipeline extends HybridObject<{
80
80
  gaussianBlur(image: Image, radius: number): Promise<Image>;
81
81
  // Future: brightness, saturation, tint, etc.
82
82
 
83
+ /**
84
+ * Caps the in-memory cache of decoded bitmaps at `bytes`, evicting
85
+ * least-recently-used entries immediately if it is currently larger. Pass
86
+ * `0` to disable in-memory caching entirely (the disk cache still works).
87
+ *
88
+ * Defaults: 128 MB on iOS, 25% of the app's memory class on Android. The
89
+ * cache trades RAM for instant re-display; lower it (or use
90
+ * `cache: 'disk'` per request) in memory-constrained apps.
91
+ */
92
+ setMemoryCacheLimit(bytes: number): void;
93
+
83
94
  clearCache(): Promise<void>;
84
95
  }