react-native-nitro-image-pipeline 0.3.5 → 1.0.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
@@ -48,7 +48,7 @@ import { useImage } from 'react-native-nitro-image-pipeline';
48
48
  function MyComponent() {
49
49
  const { image, error } = useImage({
50
50
  url: 'https://example.com/photo.jpg',
51
- blur: 4,
51
+ blur: 4, // Gaussian sigma in source pixels — same result on iOS and Android
52
52
  cornerRadius: 12,
53
53
  });
54
54
 
@@ -67,7 +67,7 @@ import { NitroImagePipeline } from 'react-native-nitro-image-pipeline';
67
67
 
68
68
  // Load an image with options
69
69
  const image = await NitroImagePipeline.loadImage('https://example.com/photo.jpg', {
70
- blur: 4,
70
+ blur: 4, // Gaussian sigma in source pixels — see "Blur units"
71
71
  cornerRadius: 12,
72
72
  cache: 'disk',
73
73
  });
@@ -96,7 +96,7 @@ Loads an image from a URL and returns a `Promise<Image>`.
96
96
 
97
97
  | Option | Type | Default | Description |
98
98
  |---|---|---|---|
99
- | `blur` | `number` | `0` | Gaussian blur radius applied at load time |
99
+ | `blur` | `number` | `0` | Gaussian blur strength applied at load time — see [Blur units](#blur-units) |
100
100
  | `cornerRadius` | `number` | `0` | Corner radius in points |
101
101
  | `cache` | `'memory' \| 'disk' \| 'none'` | platform default | Caching strategy |
102
102
 
@@ -110,12 +110,66 @@ Prefetches multiple images into the cache. Returns `Promise<void>`.
110
110
 
111
111
  ### `gaussianBlur(image, radius)`
112
112
 
113
- Applies a Gaussian blur to an existing `Image` object. Returns `Promise<Image>`.
113
+ Applies a Gaussian blur to an existing `Image` object. Returns `Promise<Image>`. `radius` uses the
114
+ same unit as the `blur` option — see [Blur units](#blur-units).
115
+
116
+ ### Blur units
117
+
118
+ `blur` (and `gaussianBlur`'s `radius`) is the **standard deviation (sigma) of the Gaussian, in
119
+ source-image pixels**. The same value on the same source file produces the same result on iOS and
120
+ Android — the platforms are calibrated against each other rather than each exposing its native
121
+ backend's own idea of "radius".
122
+
123
+ ```ts
124
+ // ~11px of blur on both platforms, whatever the device
125
+ await NitroImagePipeline.loadImage(url, { blur: 11 });
126
+ ```
127
+
128
+ Two things follow from the unit being *source* pixels:
129
+
130
+ - Blur is measured against the image's own resolution, not the size it is displayed at. A 4000px
131
+ photo at `blur: 11` looks subtler than a 400px thumbnail at `blur: 11`. To keep a feed visually
132
+ consistent, scale the value with the source width.
133
+ - Coming from React Native's `<Image blurRadius={n} />`? That halves its input internally, so
134
+ `blurRadius={n}` ≈ `blur: n / 2`. RN's value is also density-scaled on Android and not on iOS,
135
+ which is why the two never quite matched there.
136
+
137
+ Values below ~1 are smaller than the smallest kernel either backend can build and are effectively a
138
+ no-op. There is no upper bound.
139
+
140
+ Implementation: iOS runs three Accelerate box-convolution passes sized to hit the requested sigma
141
+ (the standard three-box Gaussian approximation, accurate to a few percent); Android uses
142
+ RenderScript's true Gaussian, downscaling first when sigma exceeds the single-pass ceiling of
143
+ ~10.6px and compensating the radius so the result is unchanged. Both clamp at the edges, so blurred
144
+ images keep their borders instead of fading out.
114
145
 
115
146
  ### `clearCache()`
116
147
 
117
148
  Removes all cached images from memory and disk. Returns `Promise<void>` that resolves once both caches are cleared.
118
149
 
150
+ ## Upgrading from 0.3.x
151
+
152
+ `blur` and `gaussianBlur(image, radius)` changed meaning in 1.0. They used to hand the number
153
+ straight to each platform's native blur, and the two platforms disagreed about what it meant; now
154
+ both read it as a Gaussian sigma in source-image pixels (see [Blur units](#blur-units)).
155
+
156
+ | | what `blur: n` did in 0.3.x | what it does in 1.0 |
157
+ |---|---|---|
158
+ | iOS | fed `n` to `CIGaussianBlur(inputRadius:)`, measured at `sigma ≈ 1.18 × n` | `sigma = n` |
159
+ | Android | RenderScript on a copy downscaled to 512px, so strength scaled with the source resolution: `sigma ≈ (0.4n + 0.6) × max(w, h) / 512` | `sigma = n`, resolution-independent |
160
+
161
+ To keep the look you had:
162
+
163
+ - **iOS:** multiply your old value by ~1.18 (`blur: 10` → `blur: 12`).
164
+ - **Android:** there is no single factor — the old result depended on the source image's
165
+ resolution. Re-tune against iOS, which the two platforms now agree with.
166
+
167
+ Also changed:
168
+
169
+ - `blur` above 25 used to reject the promise on Android. Sigma is now unbounded.
170
+ - Fractional values used to be truncated to whole numbers on iOS. They are honoured now.
171
+ - Blurred images used to fade out at the borders on iOS. Edges are clamped on both platforms now.
172
+
119
173
  ## Credits
120
174
 
121
175
  Bootstrapped with [create-nitro-module](https://github.com/patrickkabwe/create-nitro-module).
@@ -105,10 +105,15 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
105
105
  override fun gaussianBlur(image: HybridImageSpec, radius: Double): Promise<HybridImageSpec> =
106
106
  Promise.async {
107
107
  val hybridImage = image as? HybridImage ?: throw Error("Image is not a HybridImage")
108
- val clampedRadius = radius.toFloat().coerceIn(0.01f, 25f)
109
- val blurred =
110
- BlurTransformation(context, clampedRadius).transform(hybridImage.bitmap, Size.ORIGINAL)
111
- HybridImage(blurred)
108
+ // `radius` is a Gaussian sigma in source-image pixels — see BlurTransformation.
109
+ val sigma = radius.toFloat()
110
+ if (sigma <= 0f) {
111
+ hybridImage
112
+ } else {
113
+ val blurred =
114
+ BlurTransformation(context, sigma).transform(hybridImage.bitmap, Size.ORIGINAL)
115
+ HybridImage(blurred)
116
+ }
112
117
  }
113
118
 
114
119
  override fun clearCache(): Promise<Unit> = Promise.async {
@@ -13,33 +13,57 @@ import androidx.core.graphics.applyCanvas
13
13
  import androidx.core.graphics.scale
14
14
  import coil3.size.Size
15
15
  import coil3.transform.Transformation
16
+ import kotlin.math.ceil
17
+ import kotlin.math.max
16
18
 
19
+ /**
20
+ * Gaussian blur of standard deviation [sigma], measured in *source image pixels*.
21
+ *
22
+ * The unit is the point of this class: the same sigma on the same source file produces the same
23
+ * result here and on iOS. It is deliberately not a "radius" — RenderScript, CoreImage and React
24
+ * Native's `blurRadius` each define radius differently, which is why blurs never matched across
25
+ * platforms.
26
+ *
27
+ * For reference, React Native's `<Image blurRadius={n} />` halves its input before convolving, so
28
+ * `blurRadius={n}` is roughly `sigma = n / 2`.
29
+ */
17
30
  class BlurTransformation(
18
31
  private val context: Context,
19
- private val radius: Float = 10f,
20
- private val sampling: Float = 1f,
32
+ private val sigma: Float,
21
33
  ) : Transformation() {
22
34
 
23
35
  init {
24
- require(radius in 0.01..25.0) { "radius must be in (0, 25]." }
25
- require(sampling >= 1f) { "sampling must be >= 1." }
36
+ require(sigma > 0f && sigma.isFinite()) { "sigma must be > 0." }
26
37
  }
27
38
 
28
- override val cacheKey = "${BlurTransformation::class.java.name}-$radius-$sampling"
39
+ override val cacheKey = "${BlurTransformation::class.java.name}-$sigma"
29
40
 
30
41
  override suspend fun transform(input: Bitmap, size: Size): Bitmap {
42
+ // ScriptIntrinsicBlur's `radius` maps to a Gaussian sigma of 0.4 * radius + 0.6, and radius is
43
+ // capped at 25 — so a single pass tops out at sigma ~10.6px. Larger blurs are reached by
44
+ // blurring a downscaled copy: scaling down by `s`, blurring with sigma/s and scaling back up
45
+ // multiplies the effective sigma by `s`. A blur is a low-pass filter, so the detail the
46
+ // downscale drops is detail the blur was going to remove anyway. Downscale only as far as the
47
+ // sigma ceiling forces, which keeps small blurs pixel-exact.
48
+ val sampling = max(1f, sigma / MAX_SIGMA)
49
+ val scaledWidth = ceil(input.width / sampling).toInt().coerceAtLeast(1)
50
+ val scaledHeight = ceil(input.height / sampling).toInt().coerceAtLeast(1)
51
+ // Rounding to whole pixels shifts the scale slightly; derive the sigma from the scale actually
52
+ // applied rather than the requested one.
53
+ val scaleX = scaledWidth.toFloat() / input.width
54
+ val scaleY = scaledHeight.toFloat() / input.height
55
+ val scaledSigma = sigma * max(scaleX, scaleY)
56
+ val radius = ((scaledSigma - SIGMA_INTERCEPT) / SIGMA_SLOPE).coerceIn(MIN_RADIUS, MAX_RADIUS)
57
+
31
58
  val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
32
- // Auto-scale so the blurred image is at most 512px on the longest side.
33
- // Blur is a low-pass filter so blurring at lower resolution is identical in quality.
34
- val autoSampling = (maxOf(input.width, input.height) / 512f).coerceAtLeast(sampling)
35
- val scaledWidth = (input.width / autoSampling).toInt().coerceAtLeast(1)
36
- val scaledHeight = (input.height / autoSampling).toInt().coerceAtLeast(1)
37
- val softwareInput = if (input.config == Bitmap.Config.HARDWARE) input.copy(Bitmap.Config.ARGB_8888, false) else input
59
+ val softwareInput =
60
+ if (input.config == Bitmap.Config.HARDWARE) input.copy(Bitmap.Config.ARGB_8888, false)
61
+ else input
38
62
  val softwareConfig = softwareInput.config ?: Bitmap.Config.ARGB_8888
39
63
 
40
64
  val output = Bitmap.createBitmap(scaledWidth, scaledHeight, softwareConfig)
41
65
  output.applyCanvas {
42
- scale(1 / autoSampling, 1 / autoSampling)
66
+ scale(scaleX, scaleY)
43
67
  drawBitmap(softwareInput, 0f, 0f, paint)
44
68
  }
45
69
 
@@ -69,23 +93,25 @@ class BlurTransformation(
69
93
  blur?.destroy()
70
94
  }
71
95
 
72
- return output.scale(input.width, input.height)
96
+ return if (scaledWidth == input.width && scaledHeight == input.height) output
97
+ else output.scale(input.width, input.height)
73
98
  }
74
99
 
75
100
  override fun equals(other: Any?): Boolean {
76
101
  if (this === other) return true
77
- return other is BlurTransformation &&
78
- context == other.context &&
79
- radius == other.radius &&
80
- sampling == other.sampling
102
+ return other is BlurTransformation && context == other.context && sigma == other.sigma
81
103
  }
82
104
 
83
- override fun hashCode(): Int {
84
- var result = context.hashCode()
85
- result = 31 * result + radius.hashCode()
86
- result = 31 * result + sampling.hashCode()
87
- return result
88
- }
105
+ override fun hashCode(): Int = 31 * context.hashCode() + sigma.hashCode()
89
106
 
90
- override fun toString() = "BlurTransformation(radius=$radius, sampling=$sampling)"
107
+ override fun toString() = "BlurTransformation(sigma=$sigma)"
108
+
109
+ private companion object {
110
+ // sigma = 0.4 * radius + 0.6, per RenderScript's ScriptIntrinsicBlur.
111
+ const val SIGMA_SLOPE = 0.4f
112
+ const val SIGMA_INTERCEPT = 0.6f
113
+ const val MIN_RADIUS = 0.01f
114
+ const val MAX_RADIUS = 25f
115
+ const val MAX_SIGMA = SIGMA_SLOPE * MAX_RADIUS + SIGMA_INTERCEPT
116
+ }
91
117
  }
@@ -0,0 +1,135 @@
1
+ //
2
+ // GaussianBlur.swift
3
+ // NitroImagePipeline
4
+ //
5
+ // Cross-platform Gaussian blur — the kernel itself.
6
+ //
7
+ // This file deliberately depends on nothing but Accelerate and CoreGraphics
8
+ // so `scripts/verify-blur.swift` can compile and measure it on the host
9
+ // machine. The UIImage/Nuke plumbing lives in GaussianBlurProcessor.swift.
10
+ //
11
+ // `sigma` is the standard deviation of the Gaussian, measured in *source
12
+ // image pixels* — the same sigma applied to the same source file produces
13
+ // the same result on iOS and Android. It is deliberately not a "radius":
14
+ // CIGaussianBlur, RenderScript and React Native's `blurRadius` each define
15
+ // radius differently, which is why blurs never matched across platforms.
16
+ //
17
+ // For reference, React Native's `<Image blurRadius={n} />` halves its input
18
+ // before convolving, so `blurRadius={n}` is roughly `sigma = n / 2`.
19
+ //
20
+
21
+ import Accelerate
22
+ import CoreGraphics
23
+ import Foundation
24
+
25
+ enum GaussianBlur {
26
+ /// Runs the box-blur passes over `cgImage`. Returns `nil` if the pixel
27
+ /// buffers could not be allocated.
28
+ static func convolve(_ cgImage: CGImage, boxes: [UInt32]) -> CGImage? {
29
+ // Normalise to premultiplied ARGB8888 — vImageBoxConvolve_ARGB8888
30
+ // needs four 8-bit channels, and premultiplied alpha is what keeps
31
+ // transparent edges from bleeding dark halos into the blur.
32
+ guard var format = vImage_CGImageFormat(
33
+ bitsPerComponent: 8,
34
+ bitsPerPixel: 32,
35
+ colorSpace: CGColorSpaceCreateDeviceRGB(),
36
+ bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue),
37
+ renderingIntent: .defaultIntent
38
+ ) else { return nil }
39
+
40
+ var source = vImage_Buffer()
41
+ guard vImageBuffer_InitWithCGImage(
42
+ &source, &format, nil, cgImage, vImage_Flags(kvImageNoFlags)
43
+ ) == kvImageNoError else { return nil }
44
+ defer { free(source.data) }
45
+
46
+ var scratch = vImage_Buffer()
47
+ guard vImageBuffer_Init(
48
+ &scratch, source.height, source.width, 32, vImage_Flags(kvImageNoFlags)
49
+ ) == kvImageNoError else { return nil }
50
+ defer { free(scratch.data) }
51
+
52
+ // kvImageEdgeExtend clamps at the borders instead of sampling
53
+ // transparent black, so the image keeps its edges instead of fading
54
+ // out into the frame (CIGaussianBlur's default, and the reason blurred
55
+ // images used to come back with washed-out borders).
56
+ let flags = vImage_Flags(kvImageEdgeExtend)
57
+ // The passes use two different kernel widths; size the scratch buffer
58
+ // for the widest one so every pass fits in it.
59
+ let widest = boxes.max() ?? 1
60
+ let tempSize = vImageBoxConvolve_ARGB8888(
61
+ &source, &scratch, nil, 0, 0, widest, widest, nil,
62
+ vImage_Flags(kvImageEdgeExtend | kvImageGetTempBufferSize)
63
+ )
64
+ guard tempSize > 0, let temp = malloc(tempSize) else { return nil }
65
+ defer { free(temp) }
66
+
67
+ // Ping-pong between the two buffers; after an odd number of passes the
68
+ // result sits in `input`.
69
+ var input = source
70
+ var output = scratch
71
+ for box in boxes {
72
+ guard vImageBoxConvolve_ARGB8888(
73
+ &input, &output, temp, 0, 0, box, box, nil, flags
74
+ ) == kvImageNoError else { return nil }
75
+ swap(&input, &output)
76
+ }
77
+
78
+ var error = vImage_Error(kvImageNoError)
79
+ guard let blurred = vImageCreateCGImageFromBuffer(
80
+ &input, &format, nil, nil, vImage_Flags(kvImageNoFlags), &error
81
+ )?.takeRetainedValue(), error == kvImageNoError else { return nil }
82
+
83
+ return blurred
84
+ }
85
+
86
+ /// Widths for the three box-blur passes that approximate a Gaussian of
87
+ /// `sigma` (the classic approximation: three boxes land within a few
88
+ /// percent of a true Gaussian, and Accelerate runs them in O(1) per pixel
89
+ /// regardless of how wide they are).
90
+ ///
91
+ /// Three boxes of widths w1…w3 produce a standard deviation of
92
+ /// `sqrt((w1² + w2² + w3² - 3) / 12)`. Box widths have to be odd, so the
93
+ /// passes are split between the two odd integers straddling the ideal
94
+ /// width and the split landing closest to `sigma` wins — which keeps the
95
+ /// error under half a pixel at any sigma instead of always undershooting.
96
+ static func boxSizes(forSigma sigma: Double) -> [UInt32] {
97
+ let passes = 3
98
+ var lower = Int(((12 * sigma * sigma / Double(passes)) + 1).squareRoot())
99
+ if lower % 2 == 0 { lower -= 1 }
100
+ // Upper bound keeps `upper` inside UInt32 for absurd sigmas; a kernel
101
+ // that wide is already far larger than any real image.
102
+ lower = min(max(lower, 1), Int(UInt32.max) - 2)
103
+ let upper = lower + 2
104
+
105
+ // Written as a plain loop rather than map/min(by:): the inferred
106
+ // version tripped "type of expression is ambiguous" on Swift 6.2.
107
+ var best: [UInt32] = []
108
+ var bestError: Double = .infinity
109
+ for lowerCount in 0...passes {
110
+ var candidate: [UInt32] = []
111
+ for pass in 0..<passes {
112
+ candidate.append(UInt32(pass < lowerCount ? lower : upper))
113
+ }
114
+ // `.magnitude` rather than `abs()`: Nitro's C++ interop puts
115
+ // std::abs overloads in scope, and Swift 6.2 calls the resulting
116
+ // `abs` ambiguous when this file is compiled as part of the pod.
117
+ let error: Double = (standardDeviation(of: candidate) - sigma).magnitude
118
+ if error < bestError {
119
+ bestError = error
120
+ best = candidate
121
+ }
122
+ }
123
+ return best
124
+ }
125
+
126
+ /// The standard deviation three box blurs of the given widths add up to.
127
+ private static func standardDeviation(of boxes: [UInt32]) -> Double {
128
+ var sum: Double = 0
129
+ for box in boxes {
130
+ let width = Double(box)
131
+ sum += width * width - 1
132
+ }
133
+ return (sum / 12).squareRoot()
134
+ }
135
+ }
@@ -0,0 +1,41 @@
1
+ //
2
+ // GaussianBlurProcessor.swift
3
+ // NitroImagePipeline
4
+ //
5
+ // UIImage and Nuke plumbing around the kernel in GaussianBlur.swift.
6
+ //
7
+
8
+ import CoreGraphics
9
+ import Foundation
10
+ import Nuke
11
+ import UIKit
12
+
13
+ extension GaussianBlur {
14
+ /// Applies a Gaussian blur of standard deviation `sigma` (in image pixels).
15
+ ///
16
+ /// Returns `image` unchanged when `sigma` is too small to affect a pixel,
17
+ /// and `nil` only when the pixel buffers could not be allocated.
18
+ static func apply(to image: UIImage, sigma: Double) -> UIImage? {
19
+ guard sigma > 0, sigma.isFinite, let cgImage = image.cgImage else { return image }
20
+
21
+ let boxes = boxSizes(forSigma: sigma)
22
+ guard boxes.contains(where: { $0 > 1 }) else { return image }
23
+ guard let blurred = convolve(cgImage, boxes: boxes) else { return nil }
24
+
25
+ return UIImage(cgImage: blurred, scale: image.scale, orientation: image.imageOrientation)
26
+ }
27
+ }
28
+
29
+ /// Nuke processor so `loadImage(url:, { blur })` and `gaussianBlur(image:, sigma)`
30
+ /// go through the exact same kernel.
31
+ struct GaussianBlurProcessor: ImageProcessing {
32
+ let sigma: Double
33
+
34
+ var identifier: String { "com.nitroimagepipeline.gaussianBlur?sigma=\(sigma)" }
35
+
36
+ var hashableIdentifier: AnyHashable { identifier }
37
+
38
+ func process(_ image: PlatformImage) -> PlatformImage? {
39
+ GaussianBlur.apply(to: image, sigma: sigma)
40
+ }
41
+ }
@@ -11,7 +11,6 @@ import NitroImage
11
11
  import Nuke
12
12
 
13
13
  import UIKit
14
- import CoreImage
15
14
 
16
15
  private class HybridImage: HybridImageSpec, NativeImage {
17
16
  let uiImage: UIImage
@@ -101,9 +100,6 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
101
100
 
102
101
  private static let sharedPrefetcher = ImagePrefetcher(pipeline: sharedPipeline)
103
102
 
104
- // CIContext is expensive to create; Apple recommends reusing one.
105
- private static let ciContext = CIContext()
106
-
107
103
  private var pipeline: ImagePipeline { Self.sharedPipeline }
108
104
  private var prefetcher: ImagePrefetcher { Self.sharedPrefetcher }
109
105
 
@@ -122,7 +118,7 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
122
118
 
123
119
  var processors: [any ImageProcessing] = []
124
120
  if let blur = options?.blur, blur > 0 {
125
- processors.append(.gaussianBlur(radius: Int(blur)))
121
+ processors.append(GaussianBlurProcessor(sigma: blur))
126
122
  }
127
123
  if let cornerRadius = options?.cornerRadius, cornerRadius > 0 {
128
124
  processors.append(.roundedCorners(radius: cornerRadius))
@@ -168,22 +164,11 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
168
164
  throw RuntimeError.error(withMessage: "Unsupported image type")
169
165
  }
170
166
 
171
- let uiImage = nativeImage.uiImage
172
-
173
- guard let ciImage = CIImage(image: uiImage) else {
174
- throw RuntimeError.error(withMessage: "Failed to read image data")
175
- }
176
-
177
- let filter = CIFilter(name: "CIGaussianBlur")!
178
- filter.setValue(ciImage, forKey: kCIInputImageKey)
179
- filter.setValue(radius, forKey: kCIInputRadiusKey)
180
-
181
- guard let output = filter.outputImage,
182
- let cgImage = Self.ciContext.createCGImage(output, from: ciImage.extent) else {
167
+ guard let blurred = GaussianBlur.apply(to: nativeImage.uiImage, sigma: radius) else {
183
168
  throw RuntimeError.error(withMessage: "Failed to apply gaussian blur")
184
169
  }
185
170
 
186
- return HybridImage(uiImage: UIImage(cgImage: cgImage))
171
+ return HybridImage(uiImage: blurred)
187
172
  }
188
173
  }
189
174
 
@@ -1 +1 @@
1
- {"version":3,"names":["_react","require","_reactNativeNitroModules","NitroImagePipeline","exports","NitroModules","createHybridObject","useImage","url","blur","cornerRadius","cache","image","setImage","useState","undefined","error","loadedUrlRef","useRef","useEffect","cancelled","current","result","loadImage","e","Error"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAEA,IAAAC,wBAAA,GAAAD,OAAA;AAUO,MAAME,kBAAkB,GAAAC,OAAA,CAAAD,kBAAA,GAC7BE,qCAAY,CAACC,kBAAkB,CAAyB,oBAAoB,CAAC;AAmB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,QAAQA,CAAC;EACvBC,GAAG;EACHC,IAAI,GAAG,CAAC;EACRC,YAAY,GAAG,CAAC;EAChBC;AAMF,CAAC,EAAU;EACT,MAAM,CAACC,KAAK,EAAEC,QAAQ,CAAC,GAAG,IAAAC,eAAQ,EAAS;IACzCF,KAAK,EAAEG,SAAS;IAChBC,KAAK,EAAED;EACT,CAAC,CAAC;EACF,MAAME,YAAY,GAAG,IAAAC,aAAM,EAACV,GAAG,CAAC;EAEhC,IAAAW,gBAAS,EAAC,MAAM;IACd,IAAIC,SAAS,GAAG,KAAK;IACrB;IACA;IACA;IACA,IAAIH,YAAY,CAACI,OAAO,KAAKb,GAAG,EAAE;MAChCS,YAAY,CAACI,OAAO,GAAGb,GAAG;MAC1BK,QAAQ,CAAC;QAAED,KAAK,EAAEG,SAAS;QAAEC,KAAK,EAAED;MAAU,CAAC,CAAC;IAClD;IAEA,CAAC,YAAY;MACX,IAAI;QACF,MAAMO,MAAM,GAAG,MAAMnB,kBAAkB,CAACoB,SAAS,CAACf,GAAG,EAAE;UACrDC,IAAI;UACJC,YAAY;UACZC;QACF,CAAC,CAAC;QAEF,IAAI,CAACS,SAAS,EAAE;UACdP,QAAQ,CAAC;YAAED,KAAK,EAAEU,MAAM;YAAEN,KAAK,EAAED;UAAU,CAAC,CAAC;QAC/C;MACF,CAAC,CAAC,OAAOS,CAAC,EAAE;QACV,MAAMR,KAAK,GAAGQ,CAAC,YAAYC,KAAK,GAAGD,CAAC,GAAG,IAAIC,KAAK,CAAC,GAAGD,CAAC,EAAE,CAAC;QACxD,IAAI,CAACJ,SAAS,EAAE;UACdP,QAAQ,CAAC;YAAED,KAAK,EAAEG,SAAS;YAAEC,KAAK,EAAEA;UAAM,CAAC,CAAC;QAC9C;MACF;IACF,CAAC,EAAE,CAAC;IAEJ,OAAO,MAAM;MACXI,SAAS,GAAG,IAAI;IAClB,CAAC;EACH,CAAC,EAAE,CAACZ,GAAG,EAAEC,IAAI,EAAEC,YAAY,EAAEC,KAAK,CAAC,CAAC;EAEpC,OAAOC,KAAK;AACd","ignoreList":[]}
1
+ {"version":3,"names":["_react","require","_reactNativeNitroModules","NitroImagePipeline","exports","NitroModules","createHybridObject","useImage","url","blur","cornerRadius","cache","image","setImage","useState","undefined","error","loadedUrlRef","useRef","useEffect","cancelled","current","result","loadImage","e","Error"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAEA,IAAAC,wBAAA,GAAAD,OAAA;AAUO,MAAME,kBAAkB,GAAAC,OAAA,CAAAD,kBAAA,GAC7BE,qCAAY,CAACC,kBAAkB,CAAyB,oBAAoB,CAAC;AAmB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,QAAQA,CAAC;EACvBC,GAAG;EACHC,IAAI,GAAG,CAAC;EACRC,YAAY,GAAG,CAAC;EAChBC;AAWF,CAAC,EAAU;EACT,MAAM,CAACC,KAAK,EAAEC,QAAQ,CAAC,GAAG,IAAAC,eAAQ,EAAS;IACzCF,KAAK,EAAEG,SAAS;IAChBC,KAAK,EAAED;EACT,CAAC,CAAC;EACF,MAAME,YAAY,GAAG,IAAAC,aAAM,EAACV,GAAG,CAAC;EAEhC,IAAAW,gBAAS,EAAC,MAAM;IACd,IAAIC,SAAS,GAAG,KAAK;IACrB;IACA;IACA;IACA,IAAIH,YAAY,CAACI,OAAO,KAAKb,GAAG,EAAE;MAChCS,YAAY,CAACI,OAAO,GAAGb,GAAG;MAC1BK,QAAQ,CAAC;QAAED,KAAK,EAAEG,SAAS;QAAEC,KAAK,EAAED;MAAU,CAAC,CAAC;IAClD;IAEA,CAAC,YAAY;MACX,IAAI;QACF,MAAMO,MAAM,GAAG,MAAMnB,kBAAkB,CAACoB,SAAS,CAACf,GAAG,EAAE;UACrDC,IAAI;UACJC,YAAY;UACZC;QACF,CAAC,CAAC;QAEF,IAAI,CAACS,SAAS,EAAE;UACdP,QAAQ,CAAC;YAAED,KAAK,EAAEU,MAAM;YAAEN,KAAK,EAAED;UAAU,CAAC,CAAC;QAC/C;MACF,CAAC,CAAC,OAAOS,CAAC,EAAE;QACV,MAAMR,KAAK,GAAGQ,CAAC,YAAYC,KAAK,GAAGD,CAAC,GAAG,IAAIC,KAAK,CAAC,GAAGD,CAAC,EAAE,CAAC;QACxD,IAAI,CAACJ,SAAS,EAAE;UACdP,QAAQ,CAAC;YAAED,KAAK,EAAEG,SAAS;YAAEC,KAAK,EAAEA;UAAM,CAAC,CAAC;QAC9C;MACF;IACF,CAAC,EAAE,CAAC;IAEJ,OAAO,MAAM;MACXI,SAAS,GAAG,IAAI;IAClB,CAAC;EACH,CAAC,EAAE,CAACZ,GAAG,EAAEC,IAAI,EAAEC,YAAY,EAAEC,KAAK,CAAC,CAAC;EAEpC,OAAOC,KAAK;AACd","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["useEffect","useRef","useState","NitroModules","NitroImagePipeline","createHybridObject","useImage","url","blur","cornerRadius","cache","image","setImage","undefined","error","loadedUrlRef","cancelled","current","result","loadImage","e","Error"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAEnD,SAASC,YAAY,QAAQ,4BAA4B;AAUzD,OAAO,MAAMC,kBAAkB,GAC7BD,YAAY,CAACE,kBAAkB,CAAyB,oBAAoB,CAAC;AAmB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,QAAQA,CAAC;EACvBC,GAAG;EACHC,IAAI,GAAG,CAAC;EACRC,YAAY,GAAG,CAAC;EAChBC;AAMF,CAAC,EAAU;EACT,MAAM,CAACC,KAAK,EAAEC,QAAQ,CAAC,GAAGV,QAAQ,CAAS;IACzCS,KAAK,EAAEE,SAAS;IAChBC,KAAK,EAAED;EACT,CAAC,CAAC;EACF,MAAME,YAAY,GAAGd,MAAM,CAACM,GAAG,CAAC;EAEhCP,SAAS,CAAC,MAAM;IACd,IAAIgB,SAAS,GAAG,KAAK;IACrB;IACA;IACA;IACA,IAAID,YAAY,CAACE,OAAO,KAAKV,GAAG,EAAE;MAChCQ,YAAY,CAACE,OAAO,GAAGV,GAAG;MAC1BK,QAAQ,CAAC;QAAED,KAAK,EAAEE,SAAS;QAAEC,KAAK,EAAED;MAAU,CAAC,CAAC;IAClD;IAEA,CAAC,YAAY;MACX,IAAI;QACF,MAAMK,MAAM,GAAG,MAAMd,kBAAkB,CAACe,SAAS,CAACZ,GAAG,EAAE;UACrDC,IAAI;UACJC,YAAY;UACZC;QACF,CAAC,CAAC;QAEF,IAAI,CAACM,SAAS,EAAE;UACdJ,QAAQ,CAAC;YAAED,KAAK,EAAEO,MAAM;YAAEJ,KAAK,EAAED;UAAU,CAAC,CAAC;QAC/C;MACF,CAAC,CAAC,OAAOO,CAAC,EAAE;QACV,MAAMN,KAAK,GAAGM,CAAC,YAAYC,KAAK,GAAGD,CAAC,GAAG,IAAIC,KAAK,CAAC,GAAGD,CAAC,EAAE,CAAC;QACxD,IAAI,CAACJ,SAAS,EAAE;UACdJ,QAAQ,CAAC;YAAED,KAAK,EAAEE,SAAS;YAAEC,KAAK,EAAEA;UAAM,CAAC,CAAC;QAC9C;MACF;IACF,CAAC,EAAE,CAAC;IAEJ,OAAO,MAAM;MACXE,SAAS,GAAG,IAAI;IAClB,CAAC;EACH,CAAC,EAAE,CAACT,GAAG,EAAEC,IAAI,EAAEC,YAAY,EAAEC,KAAK,CAAC,CAAC;EAEpC,OAAOC,KAAK;AACd","ignoreList":[]}
1
+ {"version":3,"names":["useEffect","useRef","useState","NitroModules","NitroImagePipeline","createHybridObject","useImage","url","blur","cornerRadius","cache","image","setImage","undefined","error","loadedUrlRef","cancelled","current","result","loadImage","e","Error"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAEnD,SAASC,YAAY,QAAQ,4BAA4B;AAUzD,OAAO,MAAMC,kBAAkB,GAC7BD,YAAY,CAACE,kBAAkB,CAAyB,oBAAoB,CAAC;AAmB/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,QAAQA,CAAC;EACvBC,GAAG;EACHC,IAAI,GAAG,CAAC;EACRC,YAAY,GAAG,CAAC;EAChBC;AAWF,CAAC,EAAU;EACT,MAAM,CAACC,KAAK,EAAEC,QAAQ,CAAC,GAAGV,QAAQ,CAAS;IACzCS,KAAK,EAAEE,SAAS;IAChBC,KAAK,EAAED;EACT,CAAC,CAAC;EACF,MAAME,YAAY,GAAGd,MAAM,CAACM,GAAG,CAAC;EAEhCP,SAAS,CAAC,MAAM;IACd,IAAIgB,SAAS,GAAG,KAAK;IACrB;IACA;IACA;IACA,IAAID,YAAY,CAACE,OAAO,KAAKV,GAAG,EAAE;MAChCQ,YAAY,CAACE,OAAO,GAAGV,GAAG;MAC1BK,QAAQ,CAAC;QAAED,KAAK,EAAEE,SAAS;QAAEC,KAAK,EAAED;MAAU,CAAC,CAAC;IAClD;IAEA,CAAC,YAAY;MACX,IAAI;QACF,MAAMK,MAAM,GAAG,MAAMd,kBAAkB,CAACe,SAAS,CAACZ,GAAG,EAAE;UACrDC,IAAI;UACJC,YAAY;UACZC;QACF,CAAC,CAAC;QAEF,IAAI,CAACM,SAAS,EAAE;UACdJ,QAAQ,CAAC;YAAED,KAAK,EAAEO,MAAM;YAAEJ,KAAK,EAAED;UAAU,CAAC,CAAC;QAC/C;MACF,CAAC,CAAC,OAAOO,CAAC,EAAE;QACV,MAAMN,KAAK,GAAGM,CAAC,YAAYC,KAAK,GAAGD,CAAC,GAAG,IAAIC,KAAK,CAAC,GAAGD,CAAC,EAAE,CAAC;QACxD,IAAI,CAACJ,SAAS,EAAE;UACdJ,QAAQ,CAAC;YAAED,KAAK,EAAEE,SAAS;YAAEC,KAAK,EAAEA;UAAM,CAAC,CAAC;QAC9C;MACF;IACF,CAAC,EAAE,CAAC;IAEJ,OAAO,MAAM;MACXE,SAAS,GAAG,IAAI;IAClB,CAAC;EACH,CAAC,EAAE,CAACT,GAAG,EAAEC,IAAI,EAAEC,YAAY,EAAEC,KAAK,CAAC,CAAC;EAEpC,OAAOC,KAAK;AACd","ignoreList":[]}
@@ -22,6 +22,11 @@ type Result = {
22
22
  */
23
23
  export declare function useImage({ url, blur, cornerRadius, cache, }: {
24
24
  url: string;
25
+ /**
26
+ * Gaussian blur strength, as the standard deviation (sigma) of the blur in
27
+ * source-image pixels. Matches across iOS and Android; roughly half of
28
+ * React Native's `blurRadius`.
29
+ */
25
30
  blur?: number;
26
31
  cornerRadius?: number;
27
32
  cache?: CacheOption;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAGtD,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,IAAI,sBAAsB,EAC5C,OAAO,EACR,MAAM,mCAAmC,CAAC;AAE3C,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AAErC,eAAO,MAAM,kBAAkB,wBACgD,CAAC;AAEhF,KAAK,MAAM,GAEP;IACE,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;CAClB,GAED;IACE,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;CAClB,GAED;IACE,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEN;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,IAAQ,EACR,YAAgB,EAChB,KAAK,GACN,EAAE;IACD,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,MAAM,CA0CT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAGtD,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,IAAI,sBAAsB,EAC5C,OAAO,EACR,MAAM,mCAAmC,CAAC;AAE3C,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AAErC,eAAO,MAAM,kBAAkB,wBACgD,CAAC;AAEhF,KAAK,MAAM,GAEP;IACE,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;CAClB,GAED;IACE,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;CAClB,GAED;IACE,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEN;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,IAAQ,EACR,YAAgB,EAChB,KAAK,GACN,EAAE;IACD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,MAAM,CA0CT"}
@@ -2,6 +2,15 @@ import type { Image } from 'react-native-nitro-image';
2
2
  import type { HybridObject } from 'react-native-nitro-modules';
3
3
  export type CacheOption = 'memory' | 'disk' | 'none';
4
4
  export type Options = {
5
+ /**
6
+ * Gaussian blur strength, given as the standard deviation (sigma) of the
7
+ * blur in **source-image pixels**. The same value on the same source image
8
+ * produces the same result on iOS and Android.
9
+ *
10
+ * React Native's `<Image blurRadius={n} />` is roughly `blur: n / 2`.
11
+ *
12
+ * @default 0 (no blur)
13
+ */
5
14
  blur?: number;
6
15
  cache?: CacheOption;
7
16
  cornerRadius?: number;
@@ -13,6 +22,11 @@ export interface NitroImagePipeline extends HybridObject<{
13
22
  loadImage(url: string, options?: Options): Promise<Image>;
14
23
  preLoadImage(url: string): Promise<void>;
15
24
  preLoadImages(urls: string[]): Promise<void>;
25
+ /**
26
+ * Blurs an already-loaded image. `radius` is the standard deviation (sigma)
27
+ * of the Gaussian in **source-image pixels**, the same unit as
28
+ * {@linkcode Options.blur}.
29
+ */
16
30
  gaussianBlur(image: Image, radius: number): Promise<Image>;
17
31
  clearCache(): Promise<void>;
18
32
  }
@@ -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;AACrD,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,WAAW,kBACf,SAAQ,YAAY,CAAC;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC;IACzD,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,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;AACrD,MAAM,MAAM,OAAO,GAAG;IACpB;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-image-pipeline",
3
- "version": "0.3.5",
3
+ "version": "1.0.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",
@@ -9,12 +9,15 @@
9
9
  "source": "src/index",
10
10
  "scripts": {
11
11
  "typecheck": "tsc --noEmit",
12
- "lint": "biome check .",
13
- "format": "biome format --write .",
12
+ "lint": "oxlint && oxfmt --check",
13
+ "lint:fix": "oxlint --fix && oxfmt",
14
+ "format": "oxfmt",
14
15
  "clean": "git clean -dfX",
16
+ "prepare": "lefthook install || true",
15
17
  "release": "semantic-release",
16
18
  "build": "bun run typecheck && bob build",
17
- "codegen": "nitrogen --logLevel=\"debug\" && bun run build"
19
+ "codegen": "nitrogen --logLevel=\"debug\" && bun run build",
20
+ "verify:blur": "swiftc -O -o \"${TMPDIR:-/tmp}/nitro-verify-blur\" scripts/verify-blur.swift ios/GaussianBlur.swift && \"${TMPDIR:-/tmp}/nitro-verify-blur\""
18
21
  },
19
22
  "keywords": [
20
23
  "react-native",
@@ -59,26 +62,28 @@
59
62
  "registry": "https://registry.npmjs.org/"
60
63
  },
61
64
  "devDependencies": {
62
- "@biomejs/biome": "^2.5.6",
63
65
  "@semantic-release/changelog": "^7.0.0",
64
66
  "@semantic-release/git": "^11.0.1",
65
- "@types/bun": "^1.3.14",
67
+ "@types/bun": "^1.4.0",
66
68
  "@types/jest": "^30.0.0",
67
69
  "@types/react": "19.2.18",
68
- "conventional-changelog-conventionalcommits": "^10.2.1",
69
- "nitrogen": "0.36.5",
70
+ "conventional-changelog-conventionalcommits": "^9.1.0",
71
+ "lefthook": "^2.1.10",
72
+ "nitrogen": "0.37.0",
73
+ "oxfmt": "^0.65.0",
74
+ "oxlint": "^1.80.0",
70
75
  "react": "19.2.3",
71
76
  "react-native": "0.84.1",
72
77
  "react-native-builder-bob": "^0.43.0",
73
- "react-native-nitro-modules": "0.36.5",
74
- "semantic-release": "^25.0.8",
75
- "typescript": "^5.8.3"
78
+ "react-native-nitro-modules": "0.37.0",
79
+ "semantic-release": "^25.0.9",
80
+ "typescript": "^7.0.2"
76
81
  },
77
82
  "peerDependencies": {
78
83
  "react": "*",
79
84
  "react-native": "*",
80
85
  "react-native-nitro-modules": "*",
81
- "react-native-nitro-image": "*"
86
+ "react-native-nitro-image": "0.15.1"
82
87
  },
83
88
  "overrides": {
84
89
  "lodash-es": "4.17.21"
@@ -96,5 +101,8 @@
96
101
  }
97
102
  ]
98
103
  ]
99
- }
104
+ },
105
+ "trustedDependencies": [
106
+ "lefthook"
107
+ ]
100
108
  }
package/src/index.ts CHANGED
@@ -45,6 +45,11 @@ export function useImage({
45
45
  cache,
46
46
  }: {
47
47
  url: string;
48
+ /**
49
+ * Gaussian blur strength, as the standard deviation (sigma) of the blur in
50
+ * source-image pixels. Matches across iOS and Android; roughly half of
51
+ * React Native's `blurRadius`.
52
+ */
48
53
  blur?: number;
49
54
  cornerRadius?: number;
50
55
  cache?: CacheOption;
@@ -3,16 +3,32 @@ import type { HybridObject } from 'react-native-nitro-modules';
3
3
 
4
4
  export type CacheOption = 'memory' | 'disk' | 'none';
5
5
  export type Options = {
6
+ /**
7
+ * Gaussian blur strength, given as the standard deviation (sigma) of the
8
+ * blur in **source-image pixels**. The same value on the same source image
9
+ * produces the same result on iOS and Android.
10
+ *
11
+ * React Native's `<Image blurRadius={n} />` is roughly `blur: n / 2`.
12
+ *
13
+ * @default 0 (no blur)
14
+ */
6
15
  blur?: number;
7
16
  cache?: CacheOption;
8
17
  cornerRadius?: number;
9
18
  };
10
19
 
11
- export interface NitroImagePipeline
12
- extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
20
+ export interface NitroImagePipeline extends HybridObject<{
21
+ ios: 'swift';
22
+ android: 'kotlin';
23
+ }> {
13
24
  loadImage(url: string, options?: Options): Promise<Image>;
14
25
  preLoadImage(url: string): Promise<void>;
15
26
  preLoadImages(urls: string[]): Promise<void>;
27
+ /**
28
+ * Blurs an already-loaded image. `radius` is the standard deviation (sigma)
29
+ * of the Gaussian in **source-image pixels**, the same unit as
30
+ * {@linkcode Options.blur}.
31
+ */
16
32
  gaussianBlur(image: Image, radius: number): Promise<Image>;
17
33
  // Future: brightness, saturation, tint, etc.
18
34