react-native-nitro-image-pipeline 1.1.0 → 1.2.1
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 +84 -8
- package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/HybridNitroImagePipeline.kt +36 -15
- package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/transform/ResizeTransformation.kt +58 -0
- package/ios/HybridNitroImagePipeline.swift +54 -27
- package/ios/RoundedCornersProcessor.swift +6 -3
- package/lib/commonjs/NitroImagePipeline.js +9 -0
- package/lib/commonjs/NitroImagePipeline.js.map +1 -0
- package/lib/commonjs/PipelineImage.js +111 -0
- package/lib/commonjs/PipelineImage.js.map +1 -0
- package/lib/commonjs/index.js +40 -81
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/resizeForStyle.js +68 -0
- package/lib/commonjs/resizeForStyle.js.map +1 -0
- package/lib/commonjs/useImage.js +95 -0
- package/lib/commonjs/useImage.js.map +1 -0
- package/lib/module/NitroImagePipeline.js +5 -0
- package/lib/module/NitroImagePipeline.js.map +1 -0
- package/lib/module/PipelineImage.js +107 -0
- package/lib/module/PipelineImage.js.map +1 -0
- package/lib/module/index.js +4 -79
- package/lib/module/index.js.map +1 -1
- package/lib/module/resizeForStyle.js +62 -0
- package/lib/module/resizeForStyle.js.map +1 -0
- package/lib/module/useImage.js +91 -0
- package/lib/module/useImage.js.map +1 -0
- package/lib/typescript/src/NitroImagePipeline.d.ts +3 -0
- package/lib/typescript/src/NitroImagePipeline.d.ts.map +1 -0
- package/lib/typescript/src/PipelineImage.d.ts +57 -0
- package/lib/typescript/src/PipelineImage.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -38
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/resizeForStyle.d.ts +30 -0
- package/lib/typescript/src/resizeForStyle.d.ts.map +1 -0
- package/lib/typescript/src/specs/nitro-image-toolkit.nitro.d.ts +33 -5
- package/lib/typescript/src/specs/nitro-image-toolkit.nitro.d.ts.map +1 -1
- package/lib/typescript/src/useImage.d.ts +53 -0
- package/lib/typescript/src/useImage.d.ts.map +1 -0
- package/nitrogen/generated/android/c++/JHybridNitroImagePipelineSpec.cpp +4 -0
- package/nitrogen/generated/android/c++/JOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JResizeOptions.hpp +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroimagepipeline/Options.kt +9 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroimagepipeline/ResizeOptions.kt +56 -0
- package/nitrogen/generated/ios/NitroImagePipeline-Swift-Cxx-Bridge.hpp +18 -0
- package/nitrogen/generated/ios/NitroImagePipeline-Swift-Cxx-Umbrella.hpp +3 -0
- package/nitrogen/generated/ios/c++/HybridNitroImagePipelineSpecSwift.hpp +3 -0
- package/nitrogen/generated/ios/swift/Options.swift +12 -1
- package/nitrogen/generated/ios/swift/ResizeOptions.swift +34 -0
- package/nitrogen/generated/shared/c++/Options.hpp +9 -2
- package/nitrogen/generated/shared/c++/ResizeOptions.hpp +87 -0
- package/package.json +1 -1
- package/src/NitroImagePipeline.ts +6 -0
- package/src/PipelineImage.tsx +156 -0
- package/src/index.ts +10 -123
- package/src/resizeForStyle.ts +96 -0
- package/src/specs/nitro-image-toolkit.nitro.ts +34 -5
- package/src/useImage.ts +149 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ A high-performance image loading, caching, and processing library for React Nati
|
|
|
10
10
|
|
|
11
11
|
- Load remote images with built-in memory and disk caching
|
|
12
12
|
- Prefetch single or multiple images in the background
|
|
13
|
-
-
|
|
13
|
+
- Resize (aspect-fill, center-crop) and apply Gaussian blur and rounded corners (uniform or per-corner) at load time
|
|
14
14
|
- Apply Gaussian blur to already-loaded images
|
|
15
15
|
- Clear the image cache on demand
|
|
16
16
|
- `useImage` hook for declarative image loading in components
|
|
@@ -38,36 +38,80 @@ bun add react-native-nitro-image-pipeline react-native-nitro-modules react-nativ
|
|
|
38
38
|
|
|
39
39
|
## Usage
|
|
40
40
|
|
|
41
|
+
### `<PipelineImage>` component
|
|
42
|
+
|
|
43
|
+
The zero-math way to load an image in a component — no manual `PixelRatio` conversions:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { PipelineImage } from 'react-native-nitro-image-pipeline';
|
|
47
|
+
|
|
48
|
+
function MyComponent() {
|
|
49
|
+
return (
|
|
50
|
+
<PipelineImage
|
|
51
|
+
url="https://example.com/photo.jpg"
|
|
52
|
+
style={styles.photo} // bitmap is sized to this layout × PixelRatio.get()
|
|
53
|
+
blur={2} // points, like style
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const styles = StyleSheet.create({
|
|
59
|
+
photo: { width: 300, height: 200, borderRadius: 12 }, // baked into the bitmap
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Numeric `width`/`height` in `style` load immediately; percentage or flex-based sizes wait for the
|
|
64
|
+
first `onLayout` before fetching, so the full-size image is never requested just to be squeezed
|
|
65
|
+
into a small view. `blur` is in points **on this component only** — it's converted to bitmap
|
|
66
|
+
pixels internally, unlike the pixel-based values used everywhere else in this library.
|
|
67
|
+
`cornerRadius` works the same way, but if you don't pass it, it's derived instead from `style`'s
|
|
68
|
+
`borderRadius` (or the per-corner `borderTopLeftRadius`/etc. properties, e.g. a "ticket" shape) —
|
|
69
|
+
so a style that already rounds the view rounds the bitmap too, with no separate prop. Pass
|
|
70
|
+
`cornerRadius` explicitly to override that. `onLoad`/`onError` callbacks are supported, and every
|
|
71
|
+
other prop (`resizeMode`, `recyclingKey`, `testID`, …) is passed straight through to
|
|
72
|
+
`NativeNitroImage`.
|
|
73
|
+
|
|
41
74
|
### `useImage` hook
|
|
42
75
|
|
|
43
76
|
The simplest way to load an image in a component:
|
|
44
77
|
|
|
45
78
|
```tsx
|
|
46
|
-
import { useImage } from 'react-native-nitro-image-pipeline';
|
|
79
|
+
import { PixelRatio, useImage, resizeForStyle } from 'react-native-nitro-image-pipeline';
|
|
47
80
|
|
|
48
81
|
function MyComponent() {
|
|
49
82
|
const { image, error } = useImage({
|
|
50
83
|
url: 'https://example.com/photo.jpg',
|
|
51
|
-
blur: 4, // Gaussian sigma in
|
|
52
|
-
|
|
84
|
+
blur: 4, // Gaussian sigma in bitmap pixels — same result on iOS and Android
|
|
85
|
+
// Resize to the size you display (points × screen scale) so the corner
|
|
86
|
+
// radii apply 1:1 to what you see instead of the full-resolution source.
|
|
87
|
+
resize: resizeForStyle(styles.image), // display size × PixelRatio.get()
|
|
88
|
+
cornerRadius: 12 * PixelRatio.get(), // bitmap pixels
|
|
53
89
|
});
|
|
54
90
|
|
|
55
91
|
if (error) return <Text>Failed to load image</Text>;
|
|
56
92
|
if (!image) return <ActivityIndicator />;
|
|
57
93
|
|
|
58
94
|
// use `image` with react-native-nitro-image
|
|
59
|
-
return <NitroImage
|
|
95
|
+
return <NitroImage image={image} style={styles.image} />;
|
|
60
96
|
}
|
|
97
|
+
|
|
98
|
+
const styles = StyleSheet.create({ image: { width: 300, height: 200 } });
|
|
61
99
|
```
|
|
62
100
|
|
|
101
|
+
Pass `enabled: false` to defer the request — used internally by `<PipelineImage>` to wait for
|
|
102
|
+
layout before it has a size to resize to.
|
|
103
|
+
|
|
63
104
|
### Direct API
|
|
64
105
|
|
|
65
106
|
```ts
|
|
66
107
|
import { NitroImagePipeline } from 'react-native-nitro-image-pipeline';
|
|
67
108
|
|
|
68
|
-
// Load an image with options
|
|
109
|
+
// Load an image with options. resize and cornerRadius are in pixels of the
|
|
110
|
+
// produced bitmap: without resize, the radius applies to the full-resolution
|
|
111
|
+
// source and shrinks along with it when displayed small.
|
|
69
112
|
const image = await NitroImagePipeline.loadImage('https://example.com/photo.jpg', {
|
|
70
|
-
blur: 4, // Gaussian sigma in
|
|
113
|
+
blur: 4, // Gaussian sigma in bitmap pixels — see "Blur units"
|
|
114
|
+
resize: { width: 600, height: 400 }, // aspect-fill + center-crop, exact output size
|
|
71
115
|
cornerRadius: 12,
|
|
72
116
|
cache: 'disk',
|
|
73
117
|
});
|
|
@@ -75,6 +119,7 @@ const image = await NitroImagePipeline.loadImage('https://example.com/photo.jpg'
|
|
|
75
119
|
// Per-corner radii — e.g. a "ticket" shape with larger bottom corners.
|
|
76
120
|
// The rounding is baked into the bitmap, so no view-layer masking is needed.
|
|
77
121
|
const ticket = await NitroImagePipeline.loadImage('https://example.com/photo.jpg', {
|
|
122
|
+
resize: { width: 600, height: 400 },
|
|
78
123
|
cornerRadius: { topLeft: 24, topRight: 24, bottomLeft: 48, bottomRight: 48 },
|
|
79
124
|
});
|
|
80
125
|
|
|
@@ -103,8 +148,39 @@ Loads an image from a URL and returns a `Promise<Image>`.
|
|
|
103
148
|
| Option | Type | Default | Description |
|
|
104
149
|
|---|---|---|---|
|
|
105
150
|
| `blur` | `number` | `0` | Gaussian blur strength applied at load time — see [Blur units](#blur-units) |
|
|
106
|
-
| `
|
|
151
|
+
| `resize` | `{ width, height }` | source size | Target bitmap size in pixels. Scales to fill and center-crops (CSS `object-fit: cover`, upscaling if needed) before `blur`/`cornerRadius` run, so their pixel units refer to this final size. Typically your display size in points × `PixelRatio.get()` |
|
|
152
|
+
| `cornerRadius` | `number \| CornerRadii` | `0` | Corner radius in pixels of the produced bitmap — a single number for all four corners, or `{ topLeft?, topRight?, bottomLeft?, bottomRight? }` for independent per-corner radii (omitted corners stay square). Pair with `resize` for radii that match your layout |
|
|
153
|
+
| `cache` | `'memory' \| 'disk' \| 'none'` | platform default | Caching strategy |
|
|
154
|
+
|
|
155
|
+
### `<PipelineImage>`
|
|
156
|
+
|
|
157
|
+
| Prop | Type | Default | Description |
|
|
158
|
+
|---|---|---|---|
|
|
159
|
+
| `url` | `string` | — | Image URL to load |
|
|
160
|
+
| `style` | `StyleProp<ViewStyle>` | — | Layout style; also determines the resize target (see [`resizeForStyle`](#resizeforstyle-style--resizeforlayoutwidth-height)) and, if `cornerRadius` is omitted, the corner radius (see [`cornerRadiusForStyle`](#cornerradiusforstylestyle)) |
|
|
161
|
+
| `blur` | `number` | `0` | Gaussian blur strength, in **points** (converted to bitmap pixels internally) |
|
|
162
|
+
| `cornerRadius` | `number \| CornerRadii` | derived from `style` | Corner radius, in **points** (converted to bitmap pixels internally). When omitted, derived from `style`'s `borderRadius`/`borderTopLeftRadius`/etc.; square if neither is set |
|
|
107
163
|
| `cache` | `'memory' \| 'disk' \| 'none'` | platform default | Caching strategy |
|
|
164
|
+
| `onLoad` | `(image: Image) => void` | — | Called when the image finishes loading |
|
|
165
|
+
| `onError` | `(error: Error) => void` | — | Called if loading fails |
|
|
166
|
+
| `onLayout` | `(event: LayoutChangeEvent) => void` | — | Standard `View` layout callback; also drives the deferred resize for non-numeric sizes |
|
|
167
|
+
| `…NativeNitroImage props` | — | — | Everything else (`resizeMode`, `recyclingKey`, `testID`, …) is passed through to `NativeNitroImage` |
|
|
168
|
+
|
|
169
|
+
### `resizeForStyle(style)` / `resizeForLayout(width, height)`
|
|
170
|
+
|
|
171
|
+
Converts a layout size in points to a bitmap `resize` option in pixels. Returns
|
|
172
|
+
`{ width, height }` in whole pixels via `PixelRatio.getPixelSizeForLayoutSize`, or `undefined` for
|
|
173
|
+
non-numeric sizes (e.g. `'100%'`, `undefined`) — `resizeForStyle` reads `style.width`/`style.height`,
|
|
174
|
+
`resizeForLayout` takes explicit numbers.
|
|
175
|
+
|
|
176
|
+
### `cornerRadiusForStyle(style)`
|
|
177
|
+
|
|
178
|
+
Converts a view style's `borderRadius`/`borderTopLeftRadius`/`borderTopRightRadius`/
|
|
179
|
+
`borderBottomLeftRadius`/`borderBottomRightRadius` (in points) into a `cornerRadius` option — a
|
|
180
|
+
plain number for uniform `borderRadius` alone, or a `CornerRadii` object once any per-corner
|
|
181
|
+
property is set (falling back to `borderRadius` for the corners left unset). Returns `undefined`
|
|
182
|
+
when none are set. This is what `<PipelineImage>` uses internally when its `cornerRadius` prop is
|
|
183
|
+
omitted.
|
|
108
184
|
|
|
109
185
|
### `preLoadImage(url)`
|
|
110
186
|
|
package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/HybridNitroImagePipeline.kt
CHANGED
|
@@ -14,6 +14,7 @@ import coil3.request.ImageRequest
|
|
|
14
14
|
import coil3.request.SuccessResult
|
|
15
15
|
import coil3.request.allowHardware
|
|
16
16
|
import coil3.request.transformations
|
|
17
|
+
import coil3.size.Scale
|
|
17
18
|
import coil3.size.Size
|
|
18
19
|
import coil3.transform.RoundedCornersTransformation
|
|
19
20
|
import com.google.net.cronet.okhttptransport.CronetInterceptor
|
|
@@ -22,6 +23,8 @@ import com.margelo.nitro.core.Promise
|
|
|
22
23
|
import com.margelo.nitro.image.HybridImage
|
|
23
24
|
import com.margelo.nitro.image.HybridImageSpec
|
|
24
25
|
import com.margelo.nitro.nitroimagepipeline.transform.BlurTransformation
|
|
26
|
+
import com.margelo.nitro.nitroimagepipeline.transform.ResizeTransformation
|
|
27
|
+
import kotlin.math.roundToInt
|
|
25
28
|
import kotlinx.coroutines.Dispatchers
|
|
26
29
|
import kotlinx.coroutines.async
|
|
27
30
|
import kotlinx.coroutines.awaitAll
|
|
@@ -40,23 +43,34 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
|
|
|
40
43
|
|
|
41
44
|
override fun loadImage(url: String, options: Options?): Promise<HybridImageSpec> = Promise.async {
|
|
42
45
|
val blur = options?.blur?.toFloat() ?: 0f
|
|
46
|
+
val resize =
|
|
47
|
+
options?.resize?.let { r ->
|
|
48
|
+
val width = r.width.roundToInt()
|
|
49
|
+
val height = r.height.roundToInt()
|
|
50
|
+
if (width > 0 && height > 0) width to height else null
|
|
51
|
+
}
|
|
43
52
|
val transformations = buildList {
|
|
53
|
+
// Resize first: blur sigma and corner radii are in pixels of the bitmap
|
|
54
|
+
// they run on, so they must see the final size.
|
|
55
|
+
resize?.let { (width, height) -> add(ResizeTransformation(width, height)) }
|
|
44
56
|
if (blur > 0f) add(BlurTransformation(context, blur))
|
|
45
|
-
options
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
options
|
|
58
|
+
?.cornerRadius
|
|
59
|
+
?.match(
|
|
60
|
+
first = { radius ->
|
|
61
|
+
if (radius > 0.0) add(RoundedCornersTransformation(radius.toFloat()))
|
|
62
|
+
},
|
|
63
|
+
second = { radii ->
|
|
64
|
+
// RoundedCornersTransformation rejects negative radii; treat them as square.
|
|
65
|
+
val topLeft = (radii.topLeft?.toFloat() ?: 0f).coerceAtLeast(0f)
|
|
66
|
+
val topRight = (radii.topRight?.toFloat() ?: 0f).coerceAtLeast(0f)
|
|
67
|
+
val bottomLeft = (radii.bottomLeft?.toFloat() ?: 0f).coerceAtLeast(0f)
|
|
68
|
+
val bottomRight = (radii.bottomRight?.toFloat() ?: 0f).coerceAtLeast(0f)
|
|
69
|
+
if (topLeft > 0f || topRight > 0f || bottomLeft > 0f || bottomRight > 0f) {
|
|
70
|
+
add(RoundedCornersTransformation(topLeft, topRight, bottomLeft, bottomRight))
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
)
|
|
60
74
|
}
|
|
61
75
|
val request =
|
|
62
76
|
ImageRequest.Builder(context)
|
|
@@ -77,6 +91,13 @@ class HybridNitroImagePipeline : HybridNitroImagePipelineSpec() {
|
|
|
77
91
|
}
|
|
78
92
|
null -> Unit // Coil defaults: both enabled
|
|
79
93
|
}
|
|
94
|
+
// Ask the decoder for the target size so a large source is
|
|
95
|
+
// subsampled near it instead of decoded at full resolution;
|
|
96
|
+
// ResizeTransformation then makes the size exact.
|
|
97
|
+
resize?.let { (width, height) ->
|
|
98
|
+
size(width, height)
|
|
99
|
+
scale(Scale.FILL)
|
|
100
|
+
}
|
|
80
101
|
}
|
|
81
102
|
.allowHardware(true)
|
|
82
103
|
.transformations(transformations)
|
package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/transform/ResizeTransformation.kt
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package com.margelo.nitro.nitroimagepipeline.transform
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.Canvas
|
|
5
|
+
import android.graphics.Matrix
|
|
6
|
+
import android.graphics.Paint
|
|
7
|
+
import coil3.size.Size
|
|
8
|
+
import coil3.transform.Transformation
|
|
9
|
+
import kotlin.math.max
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Scales the input to fill exactly [width] × [height] pixels and center-crops the overflow (CSS
|
|
13
|
+
* `object-fit: cover`), upscaling smaller sources. It runs before [BlurTransformation] and rounded
|
|
14
|
+
* corners so their pixel units refer to the final bitmap — matching Nuke's
|
|
15
|
+
* `ImageProcessors.Resize(contentMode: .aspectFill, crop: true)` on iOS.
|
|
16
|
+
*/
|
|
17
|
+
class ResizeTransformation(
|
|
18
|
+
private val width: Int,
|
|
19
|
+
private val height: Int,
|
|
20
|
+
) : Transformation() {
|
|
21
|
+
|
|
22
|
+
init {
|
|
23
|
+
require(width > 0 && height > 0) { "width and height must be > 0." }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override val cacheKey = "${ResizeTransformation::class.java.name}-$width-$height"
|
|
27
|
+
|
|
28
|
+
override suspend fun transform(input: Bitmap, size: Size): Bitmap {
|
|
29
|
+
val softwareInput =
|
|
30
|
+
if (input.config == Bitmap.Config.HARDWARE) input.copy(Bitmap.Config.ARGB_8888, false)
|
|
31
|
+
else input
|
|
32
|
+
if (softwareInput.width == width && softwareInput.height == height) return softwareInput
|
|
33
|
+
|
|
34
|
+
val scale = max(width.toFloat() / softwareInput.width, height.toFloat() / softwareInput.height)
|
|
35
|
+
val matrix =
|
|
36
|
+
Matrix().apply {
|
|
37
|
+
setScale(scale, scale)
|
|
38
|
+
postTranslate(
|
|
39
|
+
(width - softwareInput.width * scale) / 2f,
|
|
40
|
+
(height - softwareInput.height * scale) / 2f,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
val config = softwareInput.config ?: Bitmap.Config.ARGB_8888
|
|
44
|
+
val output = Bitmap.createBitmap(width, height, config)
|
|
45
|
+
Canvas(output)
|
|
46
|
+
.drawBitmap(softwareInput, matrix, Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG))
|
|
47
|
+
return output
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
override fun equals(other: Any?): Boolean {
|
|
51
|
+
if (this === other) return true
|
|
52
|
+
return other is ResizeTransformation && width == other.width && height == other.height
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override fun hashCode(): Int = 31 * width + height
|
|
56
|
+
|
|
57
|
+
override fun toString() = "ResizeTransformation(width=$width, height=$height)"
|
|
58
|
+
}
|
|
@@ -103,41 +103,68 @@ class HybridNitroImagePipeline: HybridNitroImagePipelineSpec {
|
|
|
103
103
|
private var pipeline: ImagePipeline { Self.sharedPipeline }
|
|
104
104
|
private var prefetcher: ImagePrefetcher { Self.sharedPrefetcher }
|
|
105
105
|
|
|
106
|
+
private static func cacheOptions(for cache: CacheOption?) -> ImageRequest.Options {
|
|
107
|
+
switch cache {
|
|
108
|
+
case .memory: [.disableDiskCache]
|
|
109
|
+
case .disk: [.disableMemoryCache]
|
|
110
|
+
case .none?: [.disableDiskCache, .disableMemoryCache]
|
|
111
|
+
default: []
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// The processor that bakes `cornerRadius` into the bitmap, or `nil` when
|
|
116
|
+
/// no corner is actually rounded.
|
|
117
|
+
private static func cornerRadiusProcessor(
|
|
118
|
+
for cornerRadius: Variant_Double_CornerRadii?
|
|
119
|
+
) -> (any ImageProcessing)? {
|
|
120
|
+
switch cornerRadius {
|
|
121
|
+
case .first(let radius):
|
|
122
|
+
guard radius > 0 else { return nil }
|
|
123
|
+
// `unit: .pixels` is required: Nuke defaults to `.points`, which
|
|
124
|
+
// multiplies the radius by the screen scale. The radius is
|
|
125
|
+
// documented — and implemented on Android and in
|
|
126
|
+
// RoundedCornersProcessor — as bitmap pixels.
|
|
127
|
+
return ImageProcessors.RoundedCorners(radius: radius, unit: .pixels)
|
|
128
|
+
case .second(let radii):
|
|
129
|
+
let roundedCorners = RoundedCornersProcessor(radii: radii)
|
|
130
|
+
return roundedCorners.hasRounding ? roundedCorners : nil
|
|
131
|
+
case nil:
|
|
132
|
+
return nil
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private static func processors(for options: Options?) -> [any ImageProcessing] {
|
|
137
|
+
var processors: [any ImageProcessing] = []
|
|
138
|
+
// Resize first: blur sigma and corner radii are defined in pixels
|
|
139
|
+
// 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 {
|
|
141
|
+
processors.append(ImageProcessors.Resize(
|
|
142
|
+
size: CGSize(width: resize.width, height: resize.height),
|
|
143
|
+
unit: .pixels,
|
|
144
|
+
contentMode: .aspectFill,
|
|
145
|
+
crop: true,
|
|
146
|
+
upscale: true
|
|
147
|
+
))
|
|
148
|
+
}
|
|
149
|
+
if let blur = options?.blur, blur > 0 {
|
|
150
|
+
processors.append(GaussianBlurProcessor(sigma: blur))
|
|
151
|
+
}
|
|
152
|
+
if let roundedCorners = cornerRadiusProcessor(for: options?.cornerRadius) {
|
|
153
|
+
processors.append(roundedCorners)
|
|
154
|
+
}
|
|
155
|
+
return processors
|
|
156
|
+
}
|
|
157
|
+
|
|
106
158
|
func loadImage(url: String, options: Options?) throws -> Promise<any HybridImageSpec> {
|
|
107
159
|
return Promise.async {
|
|
108
160
|
guard let imageUrl = URL(string: url) else {
|
|
109
161
|
throw RuntimeError.error(withMessage: "Invalid URL: \(url)")
|
|
110
162
|
}
|
|
111
163
|
|
|
112
|
-
let cacheOptions: ImageRequest.Options = switch options?.cache {
|
|
113
|
-
case .memory: [.disableDiskCache]
|
|
114
|
-
case .disk: [.disableMemoryCache]
|
|
115
|
-
case .none?: [.disableDiskCache, .disableMemoryCache]
|
|
116
|
-
default: []
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
var processors: [any ImageProcessing] = []
|
|
120
|
-
if let blur = options?.blur, blur > 0 {
|
|
121
|
-
processors.append(GaussianBlurProcessor(sigma: blur))
|
|
122
|
-
}
|
|
123
|
-
switch options?.cornerRadius {
|
|
124
|
-
case .first(let radius):
|
|
125
|
-
if radius > 0 {
|
|
126
|
-
processors.append(.roundedCorners(radius: radius))
|
|
127
|
-
}
|
|
128
|
-
case .second(let radii):
|
|
129
|
-
let roundedCorners = RoundedCornersProcessor(radii: radii)
|
|
130
|
-
if roundedCorners.hasRounding {
|
|
131
|
-
processors.append(roundedCorners)
|
|
132
|
-
}
|
|
133
|
-
case nil:
|
|
134
|
-
break
|
|
135
|
-
}
|
|
136
|
-
|
|
137
164
|
let imgRequest = ImageRequest(
|
|
138
165
|
url: imageUrl,
|
|
139
|
-
processors: processors,
|
|
140
|
-
options: cacheOptions
|
|
166
|
+
processors: Self.processors(for: options),
|
|
167
|
+
options: Self.cacheOptions(for: options?.cache)
|
|
141
168
|
)
|
|
142
169
|
|
|
143
170
|
let image = try await self.pipeline.image(for: imgRequest)
|
|
@@ -12,8 +12,11 @@ import Foundation
|
|
|
12
12
|
import Nuke
|
|
13
13
|
import UIKit
|
|
14
14
|
|
|
15
|
-
/// Nuke processor that rounds each corner with its own radius
|
|
16
|
-
/// like `ImageProcessors.RoundedCorners`
|
|
15
|
+
/// Nuke processor that rounds each corner with its own radius, measured in
|
|
16
|
+
/// pixels of the bitmap it receives (like `ImageProcessors.RoundedCorners`
|
|
17
|
+
/// with `unit: .points` on the scale-1 images the pipeline decodes). Runs
|
|
18
|
+
/// after `ImageProcessors.Resize` when `Options.resize` is set, so the radii
|
|
19
|
+
/// refer to the final output size.
|
|
17
20
|
struct RoundedCornersProcessor: ImageProcessing {
|
|
18
21
|
let topLeft: CGFloat
|
|
19
22
|
let topRight: CGFloat
|
|
@@ -75,7 +78,7 @@ struct RoundedCornersProcessor: ImageProcessing {
|
|
|
75
78
|
(rect.width, topLeft + topRight),
|
|
76
79
|
(rect.width, bottomLeft + bottomRight),
|
|
77
80
|
(rect.height, topLeft + bottomLeft),
|
|
78
|
-
(rect.height, topRight + bottomRight)
|
|
81
|
+
(rect.height, topRight + bottomRight)
|
|
79
82
|
] where pair > edge {
|
|
80
83
|
scale = min(scale, edge / pair)
|
|
81
84
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NitroImagePipeline = void 0;
|
|
7
|
+
var _reactNativeNitroModules = require("react-native-nitro-modules");
|
|
8
|
+
const NitroImagePipeline = exports.NitroImagePipeline = _reactNativeNitroModules.NitroModules.createHybridObject('NitroImagePipeline');
|
|
9
|
+
//# sourceMappingURL=NitroImagePipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNativeNitroModules","require","NitroImagePipeline","exports","NitroModules","createHybridObject"],"sourceRoot":"../../src","sources":["NitroImagePipeline.ts"],"mappings":";;;;;;AAAA,IAAAA,wBAAA,GAAAC,OAAA;AAIO,MAAMC,kBAAkB,GAAAC,OAAA,CAAAD,kBAAA,GAC7BE,qCAAY,CAACC,kBAAkB,CAAyB,oBAAoB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.PipelineImage = PipelineImage;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _reactNativeNitroImage = require("react-native-nitro-image");
|
|
10
|
+
var _resizeForStyle = require("./resizeForStyle");
|
|
11
|
+
var _useImage = require("./useImage");
|
|
12
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
|
+
function sameSize(a, b) {
|
|
14
|
+
return a?.width === b?.width && a?.height === b?.height;
|
|
15
|
+
}
|
|
16
|
+
function scaleRadius(cornerRadius, scale) {
|
|
17
|
+
if (typeof cornerRadius === 'number') {
|
|
18
|
+
return cornerRadius * scale;
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
topLeft: (cornerRadius.topLeft ?? 0) * scale,
|
|
22
|
+
topRight: (cornerRadius.topRight ?? 0) * scale,
|
|
23
|
+
bottomLeft: (cornerRadius.bottomLeft ?? 0) * scale,
|
|
24
|
+
bottomRight: (cornerRadius.bottomRight ?? 0) * scale
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A `NativeNitroImage` that loads `url` through the pipeline at exactly the
|
|
30
|
+
* size it is displayed: the bitmap is resized to the view's size in points ×
|
|
31
|
+
* `PixelRatio.get()`, so `blur` and `cornerRadius` (both in points here) apply
|
|
32
|
+
* 1:1 to what is on screen and large sources are never decoded at full size.
|
|
33
|
+
*
|
|
34
|
+
* A numeric `width`/`height` in `style` starts loading immediately; otherwise
|
|
35
|
+
* (`'50%'`, `flex`, `aspectRatio`, …) loading waits for the first `onLayout`.
|
|
36
|
+
* If the layout size later changes, a new variant is loaded and swapped in
|
|
37
|
+
* without flashing. Without an explicit `cornerRadius` prop, `style`'s
|
|
38
|
+
* `borderRadius`-family properties are baked into the bitmap instead — no
|
|
39
|
+
* separate view-layer rounding needed.
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* <PipelineImage
|
|
43
|
+
* url="https://example.com/photo.jpg"
|
|
44
|
+
* style={{ width: 300, height: 200 }}
|
|
45
|
+
* cornerRadius={24}
|
|
46
|
+
* />
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
function PipelineImage({
|
|
50
|
+
url,
|
|
51
|
+
blur = 0,
|
|
52
|
+
cornerRadius,
|
|
53
|
+
cache,
|
|
54
|
+
onLoad,
|
|
55
|
+
onError,
|
|
56
|
+
style,
|
|
57
|
+
onLayout,
|
|
58
|
+
...viewProps
|
|
59
|
+
}) {
|
|
60
|
+
const scale = _reactNative.PixelRatio.get();
|
|
61
|
+
const styleSize = (0, _resizeForStyle.resizeForStyle)(style);
|
|
62
|
+
const [layoutSize, setLayoutSize] = (0, _react.useState)(undefined);
|
|
63
|
+
// A numeric style is what the caller declared, so it wins and starts the
|
|
64
|
+
// request a frame earlier; the measured layout is the fallback.
|
|
65
|
+
const resize = styleSize ?? layoutSize;
|
|
66
|
+
// Same precedence: an explicit prop wins over what style implies.
|
|
67
|
+
const effectiveCornerRadius = cornerRadius ?? (0, _resizeForStyle.cornerRadiusForStyle)(style) ?? 0;
|
|
68
|
+
const {
|
|
69
|
+
image,
|
|
70
|
+
error
|
|
71
|
+
} = (0, _useImage.useImage)({
|
|
72
|
+
url,
|
|
73
|
+
blur: blur * scale,
|
|
74
|
+
cornerRadius: scaleRadius(effectiveCornerRadius, scale),
|
|
75
|
+
cache,
|
|
76
|
+
resize,
|
|
77
|
+
enabled: resize !== undefined
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Latest callbacks in refs so inline arrow props don't re-fire the effects.
|
|
81
|
+
const onLoadRef = (0, _react.useRef)(onLoad);
|
|
82
|
+
const onErrorRef = (0, _react.useRef)(onError);
|
|
83
|
+
(0, _react.useEffect)(() => {
|
|
84
|
+
onLoadRef.current = onLoad;
|
|
85
|
+
onErrorRef.current = onError;
|
|
86
|
+
});
|
|
87
|
+
(0, _react.useEffect)(() => {
|
|
88
|
+
if (image) onLoadRef.current?.(image);
|
|
89
|
+
}, [image]);
|
|
90
|
+
(0, _react.useEffect)(() => {
|
|
91
|
+
if (error) onErrorRef.current?.(error);
|
|
92
|
+
}, [error]);
|
|
93
|
+
const handleLayout = event => {
|
|
94
|
+
onLayout?.(event);
|
|
95
|
+
const {
|
|
96
|
+
width,
|
|
97
|
+
height
|
|
98
|
+
} = event.nativeEvent.layout;
|
|
99
|
+
const next = (0, _resizeForStyle.resizeForLayout)(width, height);
|
|
100
|
+
// Always record it (even when a numeric style is in charge) so a later
|
|
101
|
+
// switch to a non-numeric style has a size to fall back on.
|
|
102
|
+
setLayoutSize(prev => sameSize(prev, next) ? prev : next);
|
|
103
|
+
};
|
|
104
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeNitroImage.NativeNitroImage, {
|
|
105
|
+
...viewProps,
|
|
106
|
+
style: style,
|
|
107
|
+
onLayout: handleLayout,
|
|
108
|
+
image: image
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=PipelineImage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_react","require","_reactNative","_reactNativeNitroImage","_resizeForStyle","_useImage","_jsxRuntime","sameSize","a","b","width","height","scaleRadius","cornerRadius","scale","topLeft","topRight","bottomLeft","bottomRight","PipelineImage","url","blur","cache","onLoad","onError","style","onLayout","viewProps","PixelRatio","get","styleSize","resizeForStyle","layoutSize","setLayoutSize","useState","undefined","resize","effectiveCornerRadius","cornerRadiusForStyle","image","error","useImage","enabled","onLoadRef","useRef","onErrorRef","useEffect","current","handleLayout","event","nativeEvent","layout","next","resizeForLayout","prev","jsx","NativeNitroImage"],"sourceRoot":"../../src","sources":["PipelineImage.tsx"],"mappings":"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","ignoreList":[]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -3,85 +3,44 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (loadedUrlRef.current !== url) {
|
|
47
|
-
loadedUrlRef.current = url;
|
|
48
|
-
setImage({
|
|
49
|
-
image: undefined,
|
|
50
|
-
error: undefined
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
(async () => {
|
|
54
|
-
try {
|
|
55
|
-
const result = await NitroImagePipeline.loadImage(url, {
|
|
56
|
-
blur,
|
|
57
|
-
cornerRadius: isUniformRadius ? uniformRadius : {
|
|
58
|
-
topLeft,
|
|
59
|
-
topRight,
|
|
60
|
-
bottomLeft,
|
|
61
|
-
bottomRight
|
|
62
|
-
},
|
|
63
|
-
cache
|
|
64
|
-
});
|
|
65
|
-
if (!cancelled) {
|
|
66
|
-
setImage({
|
|
67
|
-
image: result,
|
|
68
|
-
error: undefined
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
} catch (e) {
|
|
72
|
-
const error = e instanceof Error ? e : new Error(`${e}`);
|
|
73
|
-
if (!cancelled) {
|
|
74
|
-
setImage({
|
|
75
|
-
image: undefined,
|
|
76
|
-
error: error
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
})();
|
|
81
|
-
return () => {
|
|
82
|
-
cancelled = true;
|
|
83
|
-
};
|
|
84
|
-
}, [url, blur, isUniformRadius, uniformRadius, topLeft, topRight, bottomLeft, bottomRight, cache]);
|
|
85
|
-
return image;
|
|
86
|
-
}
|
|
6
|
+
Object.defineProperty(exports, "NitroImagePipeline", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _NitroImagePipeline.NitroImagePipeline;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "PipelineImage", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _PipelineImage.PipelineImage;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "cornerRadiusForStyle", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _resizeForStyle.cornerRadiusForStyle;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "resizeForLayout", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _resizeForStyle.resizeForLayout;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "resizeForStyle", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () {
|
|
33
|
+
return _resizeForStyle.resizeForStyle;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(exports, "useImage", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _useImage.useImage;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
var _NitroImagePipeline = require("./NitroImagePipeline");
|
|
43
|
+
var _PipelineImage = require("./PipelineImage");
|
|
44
|
+
var _resizeForStyle = require("./resizeForStyle");
|
|
45
|
+
var _useImage = require("./useImage");
|
|
87
46
|
//# sourceMappingURL=index.js.map
|