react-native-nitro-image-pipeline 1.5.0 → 1.7.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.
Files changed (87) hide show
  1. package/README.md +142 -18
  2. package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/HybridNitroImagePipeline.kt +72 -16
  3. package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/PipelineImageLoader.kt +47 -8
  4. package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/transform/FitGeometry.kt +104 -0
  5. package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/transform/PipelineRoundedCornersTransformation.kt +103 -0
  6. package/android/src/main/java/com/margelo/nitro/nitroimagepipeline/transform/ResizeTransformation.kt +69 -16
  7. package/ios/FitResizeProcessor.swift +127 -0
  8. package/ios/HybridNitroImagePipeline.swift +88 -24
  9. package/ios/PipelineImageLoader.swift +108 -18
  10. package/lib/commonjs/NativePipelineImage.js +20 -4
  11. package/lib/commonjs/NativePipelineImage.js.map +1 -1
  12. package/lib/commonjs/PipelineImage.js +13 -1
  13. package/lib/commonjs/PipelineImage.js.map +1 -1
  14. package/lib/commonjs/index.js +13 -0
  15. package/lib/commonjs/index.js.map +1 -1
  16. package/lib/commonjs/resizeForStyle.js +3 -1
  17. package/lib/commonjs/resizeForStyle.js.map +1 -1
  18. package/lib/commonjs/resolveImageSource.js +89 -0
  19. package/lib/commonjs/resolveImageSource.js.map +1 -0
  20. package/lib/commonjs/useImage.js +14 -6
  21. package/lib/commonjs/useImage.js.map +1 -1
  22. package/lib/commonjs/usePipelineImageLoader.js +114 -11
  23. package/lib/commonjs/usePipelineImageLoader.js.map +1 -1
  24. package/lib/module/NativePipelineImage.js +20 -4
  25. package/lib/module/NativePipelineImage.js.map +1 -1
  26. package/lib/module/PipelineImage.js +13 -1
  27. package/lib/module/PipelineImage.js.map +1 -1
  28. package/lib/module/index.js +1 -0
  29. package/lib/module/index.js.map +1 -1
  30. package/lib/module/resizeForStyle.js +3 -1
  31. package/lib/module/resizeForStyle.js.map +1 -1
  32. package/lib/module/resolveImageSource.js +83 -0
  33. package/lib/module/resolveImageSource.js.map +1 -0
  34. package/lib/module/useImage.js +14 -6
  35. package/lib/module/useImage.js.map +1 -1
  36. package/lib/module/usePipelineImageLoader.js +115 -12
  37. package/lib/module/usePipelineImageLoader.js.map +1 -1
  38. package/lib/typescript/src/NativePipelineImage.d.ts +51 -5
  39. package/lib/typescript/src/NativePipelineImage.d.ts.map +1 -1
  40. package/lib/typescript/src/PipelineImage.d.ts +35 -3
  41. package/lib/typescript/src/PipelineImage.d.ts.map +1 -1
  42. package/lib/typescript/src/index.d.ts +2 -1
  43. package/lib/typescript/src/index.d.ts.map +1 -1
  44. package/lib/typescript/src/resizeForStyle.d.ts +3 -1
  45. package/lib/typescript/src/resizeForStyle.d.ts.map +1 -1
  46. package/lib/typescript/src/resolveImageSource.d.ts +56 -0
  47. package/lib/typescript/src/resolveImageSource.d.ts.map +1 -0
  48. package/lib/typescript/src/specs/nitro-image-toolkit.nitro.d.ts +95 -8
  49. package/lib/typescript/src/specs/nitro-image-toolkit.nitro.d.ts.map +1 -1
  50. package/lib/typescript/src/useImage.d.ts +13 -6
  51. package/lib/typescript/src/useImage.d.ts.map +1 -1
  52. package/lib/typescript/src/usePipelineImageLoader.d.ts +7 -3
  53. package/lib/typescript/src/usePipelineImageLoader.d.ts.map +1 -1
  54. package/nitrogen/generated/android/NitroImagePipelineOnLoad.cpp +4 -0
  55. package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +75 -0
  56. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  57. package/nitrogen/generated/android/c++/JHybridNitroImagePipelineSpec.cpp +8 -0
  58. package/nitrogen/generated/android/c++/JOptions.hpp +2 -0
  59. package/nitrogen/generated/android/c++/JResizeFit.hpp +64 -0
  60. package/nitrogen/generated/android/c++/JResizeOptions.hpp +14 -4
  61. package/nitrogen/generated/android/c++/JViewOptions.hpp +42 -3
  62. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroimagepipeline/Func_void_double_double.kt +78 -0
  63. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroimagepipeline/Func_void_std__string.kt +78 -0
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroimagepipeline/ResizeFit.kt +25 -0
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroimagepipeline/ResizeOptions.kt +14 -4
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroimagepipeline/ViewOptions.kt +29 -5
  67. package/nitrogen/generated/ios/NitroImagePipeline-Swift-Cxx-Bridge.cpp +16 -0
  68. package/nitrogen/generated/ios/NitroImagePipeline-Swift-Cxx-Bridge.hpp +107 -0
  69. package/nitrogen/generated/ios/NitroImagePipeline-Swift-Cxx-Umbrella.hpp +4 -0
  70. package/nitrogen/generated/ios/c++/HybridNitroImagePipelineSpecSwift.hpp +4 -0
  71. package/nitrogen/generated/ios/swift/Func_void_double_double.swift +46 -0
  72. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  73. package/nitrogen/generated/ios/swift/ResizeFit.swift +48 -0
  74. package/nitrogen/generated/ios/swift/ResizeOptions.swift +31 -2
  75. package/nitrogen/generated/ios/swift/ViewOptions.swift +82 -1
  76. package/nitrogen/generated/shared/c++/ResizeFit.hpp +84 -0
  77. package/nitrogen/generated/shared/c++/ResizeOptions.hpp +14 -4
  78. package/nitrogen/generated/shared/c++/ViewOptions.hpp +24 -3
  79. package/package.json +1 -1
  80. package/src/NativePipelineImage.tsx +75 -6
  81. package/src/PipelineImage.tsx +46 -3
  82. package/src/index.ts +6 -0
  83. package/src/resizeForStyle.ts +3 -1
  84. package/src/resolveImageSource.ts +83 -0
  85. package/src/specs/nitro-image-toolkit.nitro.ts +96 -8
  86. package/src/useImage.ts +38 -17
  87. package/src/usePipelineImageLoader.ts +126 -11
@@ -0,0 +1,84 @@
1
+ ///
2
+ /// ResizeFit.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/NitroHash.hpp>)
11
+ #include <NitroModules/NitroHash.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+ #if __has_include(<NitroModules/JSIConverter.hpp>)
16
+ #include <NitroModules/JSIConverter.hpp>
17
+ #else
18
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
+ #endif
20
+ #if __has_include(<NitroModules/NitroDefines.hpp>)
21
+ #include <NitroModules/NitroDefines.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
25
+
26
+ namespace margelo::nitro::nitroimagepipeline {
27
+
28
+ /**
29
+ * An enum which can be represented as a JavaScript union (ResizeFit).
30
+ */
31
+ enum class ResizeFit {
32
+ COVER SWIFT_NAME(cover) = 0,
33
+ CONTAIN SWIFT_NAME(contain) = 1,
34
+ STRETCH SWIFT_NAME(stretch) = 2,
35
+ CENTER SWIFT_NAME(center) = 3,
36
+ } CLOSED_ENUM;
37
+
38
+ } // namespace margelo::nitro::nitroimagepipeline
39
+
40
+ namespace margelo::nitro {
41
+
42
+ // C++ ResizeFit <> JS ResizeFit (union)
43
+ template <>
44
+ struct JSIConverter<margelo::nitro::nitroimagepipeline::ResizeFit> final {
45
+ static inline margelo::nitro::nitroimagepipeline::ResizeFit fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
46
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
47
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
48
+ case hashString("cover"): return margelo::nitro::nitroimagepipeline::ResizeFit::COVER;
49
+ case hashString("contain"): return margelo::nitro::nitroimagepipeline::ResizeFit::CONTAIN;
50
+ case hashString("stretch"): return margelo::nitro::nitroimagepipeline::ResizeFit::STRETCH;
51
+ case hashString("center"): return margelo::nitro::nitroimagepipeline::ResizeFit::CENTER;
52
+ default: [[unlikely]]
53
+ throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum ResizeFit - invalid value!");
54
+ }
55
+ }
56
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::nitroimagepipeline::ResizeFit arg) {
57
+ switch (arg) {
58
+ case margelo::nitro::nitroimagepipeline::ResizeFit::COVER: return JSIConverter<std::string>::toJSI(runtime, "cover");
59
+ case margelo::nitro::nitroimagepipeline::ResizeFit::CONTAIN: return JSIConverter<std::string>::toJSI(runtime, "contain");
60
+ case margelo::nitro::nitroimagepipeline::ResizeFit::STRETCH: return JSIConverter<std::string>::toJSI(runtime, "stretch");
61
+ case margelo::nitro::nitroimagepipeline::ResizeFit::CENTER: return JSIConverter<std::string>::toJSI(runtime, "center");
62
+ default: [[unlikely]]
63
+ throw std::invalid_argument("Cannot convert ResizeFit to JS - invalid value: "
64
+ + std::to_string(static_cast<int>(arg)) + "!");
65
+ }
66
+ }
67
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
68
+ if (!value.isString()) {
69
+ return false;
70
+ }
71
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
72
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
73
+ case hashString("cover"):
74
+ case hashString("contain"):
75
+ case hashString("stretch"):
76
+ case hashString("center"):
77
+ return true;
78
+ default:
79
+ return false;
80
+ }
81
+ }
82
+ };
83
+
84
+ } // namespace margelo::nitro
@@ -28,9 +28,11 @@
28
28
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
29
29
  #endif
30
30
 
31
+ // Forward declaration of `ResizeFit` to properly resolve imports.
32
+ namespace margelo::nitro::nitroimagepipeline { enum class ResizeFit; }
31
33
 
32
-
33
-
34
+ #include "ResizeFit.hpp"
35
+ #include <optional>
34
36
 
35
37
  namespace margelo::nitro::nitroimagepipeline {
36
38
 
@@ -41,10 +43,12 @@ namespace margelo::nitro::nitroimagepipeline {
41
43
  public:
42
44
  double width SWIFT_PRIVATE;
43
45
  double height SWIFT_PRIVATE;
46
+ std::optional<ResizeFit> fit SWIFT_PRIVATE;
47
+ std::optional<bool> allowUpscale SWIFT_PRIVATE;
44
48
 
45
49
  public:
46
50
  ResizeOptions() = default;
47
- explicit ResizeOptions(double width, double height): width(width), height(height) {}
51
+ explicit ResizeOptions(double width, double height, std::optional<ResizeFit> fit, std::optional<bool> allowUpscale): width(width), height(height), fit(fit), allowUpscale(allowUpscale) {}
48
52
 
49
53
  public:
50
54
  friend bool operator==(const ResizeOptions& lhs, const ResizeOptions& rhs) = default;
@@ -61,13 +65,17 @@ namespace margelo::nitro {
61
65
  jsi::Object obj = arg.asObject(runtime);
62
66
  return margelo::nitro::nitroimagepipeline::ResizeOptions(
63
67
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width"))),
64
- JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))
68
+ JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height"))),
69
+ JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeFit>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "fit"))),
70
+ JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "allowUpscale")))
65
71
  );
66
72
  }
67
73
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitroimagepipeline::ResizeOptions& arg) {
68
74
  jsi::Object obj(runtime);
69
75
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "width"), JSIConverter<double>::toJSI(runtime, arg.width));
70
76
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "height"), JSIConverter<double>::toJSI(runtime, arg.height));
77
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "fit"), JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeFit>>::toJSI(runtime, arg.fit));
78
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "allowUpscale"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.allowUpscale));
71
79
  return obj;
72
80
  }
73
81
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
@@ -80,6 +88,8 @@ namespace margelo::nitro {
80
88
  }
81
89
  if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width")))) return false;
82
90
  if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))) return false;
91
+ if (!JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeFit>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "fit")))) return false;
92
+ if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "allowUpscale")))) return false;
83
93
  return true;
84
94
  }
85
95
  };
@@ -34,12 +34,17 @@ namespace margelo::nitro::nitroimagepipeline { enum class CacheOption; }
34
34
  namespace margelo::nitro::nitroimagepipeline { struct CornerRadii; }
35
35
  // Forward declaration of `ResizeOptions` to properly resolve imports.
36
36
  namespace margelo::nitro::nitroimagepipeline { struct ResizeOptions; }
37
+ // Forward declaration of `ResizeFit` to properly resolve imports.
38
+ namespace margelo::nitro::nitroimagepipeline { enum class ResizeFit; }
37
39
 
38
40
  #include <optional>
39
41
  #include "CacheOption.hpp"
40
42
  #include "CornerRadii.hpp"
41
43
  #include <variant>
42
44
  #include "ResizeOptions.hpp"
45
+ #include "ResizeFit.hpp"
46
+ #include <functional>
47
+ #include <string>
43
48
 
44
49
  namespace margelo::nitro::nitroimagepipeline {
45
50
 
@@ -52,13 +57,17 @@ namespace margelo::nitro::nitroimagepipeline {
52
57
  std::optional<CacheOption> cache SWIFT_PRIVATE;
53
58
  std::optional<std::variant<double, CornerRadii>> cornerRadius SWIFT_PRIVATE;
54
59
  std::optional<ResizeOptions> resize SWIFT_PRIVATE;
60
+ std::optional<ResizeFit> fit SWIFT_PRIVATE;
61
+ std::optional<bool> allowUpscale SWIFT_PRIVATE;
62
+ std::optional<std::function<void(double /* width */, double /* height */)>> onLoad SWIFT_PRIVATE;
63
+ std::optional<std::function<void(const std::string& /* message */)>> onError SWIFT_PRIVATE;
55
64
 
56
65
  public:
57
66
  ViewOptions() = default;
58
- explicit ViewOptions(std::optional<double> blur, std::optional<CacheOption> cache, std::optional<std::variant<double, CornerRadii>> cornerRadius, std::optional<ResizeOptions> resize): blur(blur), cache(cache), cornerRadius(cornerRadius), resize(resize) {}
67
+ explicit ViewOptions(std::optional<double> blur, std::optional<CacheOption> cache, std::optional<std::variant<double, CornerRadii>> cornerRadius, std::optional<ResizeOptions> resize, std::optional<ResizeFit> fit, std::optional<bool> allowUpscale, std::optional<std::function<void(double /* width */, double /* height */)>> onLoad, std::optional<std::function<void(const std::string& /* message */)>> onError): blur(blur), cache(cache), cornerRadius(cornerRadius), resize(resize), fit(fit), allowUpscale(allowUpscale), onLoad(onLoad), onError(onError) {}
59
68
 
60
69
  public:
61
- friend bool operator==(const ViewOptions& lhs, const ViewOptions& rhs) = default;
70
+ // ViewOptions is not equatable because these properties are not equatable: onLoad, onError
62
71
  };
63
72
 
64
73
  } // namespace margelo::nitro::nitroimagepipeline
@@ -74,7 +83,11 @@ namespace margelo::nitro {
74
83
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "blur"))),
75
84
  JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::CacheOption>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cache"))),
76
85
  JSIConverter<std::optional<std::variant<double, margelo::nitro::nitroimagepipeline::CornerRadii>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cornerRadius"))),
77
- JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeOptions>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "resize")))
86
+ JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeOptions>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "resize"))),
87
+ JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeFit>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "fit"))),
88
+ JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "allowUpscale"))),
89
+ JSIConverter<std::optional<std::function<void(double, double)>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onLoad"))),
90
+ JSIConverter<std::optional<std::function<void(const std::string&)>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onError")))
78
91
  );
79
92
  }
80
93
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitroimagepipeline::ViewOptions& arg) {
@@ -83,6 +96,10 @@ namespace margelo::nitro {
83
96
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "cache"), JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::CacheOption>>::toJSI(runtime, arg.cache));
84
97
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "cornerRadius"), JSIConverter<std::optional<std::variant<double, margelo::nitro::nitroimagepipeline::CornerRadii>>>::toJSI(runtime, arg.cornerRadius));
85
98
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "resize"), JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeOptions>>::toJSI(runtime, arg.resize));
99
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "fit"), JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeFit>>::toJSI(runtime, arg.fit));
100
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "allowUpscale"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.allowUpscale));
101
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "onLoad"), JSIConverter<std::optional<std::function<void(double, double)>>>::toJSI(runtime, arg.onLoad));
102
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "onError"), JSIConverter<std::optional<std::function<void(const std::string&)>>>::toJSI(runtime, arg.onError));
86
103
  return obj;
87
104
  }
88
105
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
@@ -97,6 +114,10 @@ namespace margelo::nitro {
97
114
  if (!JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::CacheOption>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cache")))) return false;
98
115
  if (!JSIConverter<std::optional<std::variant<double, margelo::nitro::nitroimagepipeline::CornerRadii>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "cornerRadius")))) return false;
99
116
  if (!JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeOptions>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "resize")))) return false;
117
+ if (!JSIConverter<std::optional<margelo::nitro::nitroimagepipeline::ResizeFit>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "fit")))) return false;
118
+ if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "allowUpscale")))) return false;
119
+ if (!JSIConverter<std::optional<std::function<void(double, double)>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onLoad")))) return false;
120
+ if (!JSIConverter<std::optional<std::function<void(const std::string&)>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onError")))) return false;
100
121
  return true;
101
122
  }
102
123
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-image-pipeline",
3
- "version": "1.5.0",
3
+ "version": "1.7.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",
@@ -3,9 +3,11 @@ import type { HostComponent } from 'react-native';
3
3
  import { NativeNitroImage } from 'react-native-nitro-image';
4
4
 
5
5
  import { cornerRadiusForStyle } from './resizeForStyle';
6
+ import { type ImageSource, recyclingKeyFor } from './resolveImageSource';
6
7
  import type {
7
8
  CacheOption,
8
9
  CornerRadii,
10
+ ResizeFit,
9
11
  ResizeOptions,
10
12
  } from './specs/nitro-image-toolkit.nitro';
11
13
  import { usePipelineImageLoader } from './usePipelineImageLoader';
@@ -24,8 +26,12 @@ export interface NativePipelineImageProps extends Omit<
24
26
  NativeImageProps,
25
27
  'image'
26
28
  > {
27
- /** URL of the image to load through the pipeline. */
28
- url: string;
29
+ /**
30
+ * The image to load through the pipeline: a URL string (`https://`,
31
+ * `file://`, an absolute path) or a `require()`d asset — see
32
+ * {@linkcode ImageSource}.
33
+ */
34
+ url: ImageSource;
29
35
  /**
30
36
  * Gaussian blur sigma in **points** (density-independent), like
31
37
  * `PipelineImage`. Applied natively with the screen scale.
@@ -46,6 +52,46 @@ export interface NativePipelineImageProps extends Omit<
46
52
  * @default undefined (measure the view natively)
47
53
  */
48
54
  resize?: ResizeOptions;
55
+ /**
56
+ * How the source is fitted into the measured view size when the bitmap is
57
+ * produced — see {@linkcode ResizeFit}. When omitted it follows
58
+ * `resizeMode`, like `PipelineImage`. Set this to decouple the two.
59
+ * @default undefined (derived from `resizeMode`, or `'cover'` if unset)
60
+ */
61
+ fit?: ResizeFit;
62
+ /**
63
+ * `false` never enlarges a source smaller than the view when the
64
+ * **bitmap** is produced — see {@linkcode ResizeOptions.allowUpscale}: it
65
+ * stays at its own pixel size, so nothing is decoded or blurred larger
66
+ * than the source. The **view** still displays that bitmap per
67
+ * `resizeMode`, so `cover`/`contain`/`stretch` scale it up on screen (and
68
+ * a baked `blur`/`cornerRadius` with it); `resizeMode="center"` shows it
69
+ * pixel for pixel instead, on both platforms.
70
+ * @default true
71
+ */
72
+ allowUpscale?: boolean;
73
+ /**
74
+ * Called when the view has displayed the image, with the size of the bitmap
75
+ * it displays in **pixels**.
76
+ *
77
+ * Unlike `PipelineImage`'s `onLoad` this doesn't hand you the `Image` — the
78
+ * bitmap stays native, which is the point of this component — and it means
79
+ * "the view is now showing this image" rather than firing exactly once: a
80
+ * recycled cell re-attaching calls it again, and a single view may call it
81
+ * more than once for the same image. Make it idempotent.
82
+ *
83
+ * Setting it means there is per-image JS work again; leave it unset and the
84
+ * component stays free of JS round trips.
85
+ */
86
+ onLoad?: (width: number, height: number) => void;
87
+ /**
88
+ * Called when loading fails, with the underlying error's message. A load
89
+ * cancelled by the view detaching is not a failure and doesn't call this.
90
+ *
91
+ * Without it, failures on this component are silent — there is no `Image`
92
+ * and no promise to surface them.
93
+ */
94
+ onError?: (message: string) => void;
49
95
  }
50
96
 
51
97
  /**
@@ -57,8 +103,9 @@ export interface NativePipelineImageProps extends Omit<
57
103
  * the same pipeline and caches as `useImage`/`preLoadImage`.
58
104
  *
59
105
  * Compared to `PipelineImage`:
60
- * - No `onLoad`/`onError` callbacks the loaded `Image` never crosses into
61
- * JS. Use `PipelineImage` (or `useImage`) when you need them.
106
+ * - `onLoad` reports the bitmap's pixel size rather than the `Image` itself,
107
+ * which never crosses into JS, and can fire more than once for a view. Use
108
+ * `PipelineImage` (or `useImage`) when you need the `Image`.
62
109
  * - The bitmap is loaded once at the size the view first has; if the view is
63
110
  * resized later, the bitmap scales with it instead of reloading.
64
111
  * @example
@@ -74,25 +121,47 @@ export const NativePipelineImage = forwardRef<
74
121
  NativePipelineImageRef,
75
122
  NativePipelineImageProps
76
123
  >(function NativePipelineImage(
77
- { url, blur, cornerRadius, cache, resize, style, ...viewProps },
124
+ {
125
+ url,
126
+ blur,
127
+ cornerRadius,
128
+ cache,
129
+ resize,
130
+ fit,
131
+ allowUpscale,
132
+ onLoad,
133
+ onError,
134
+ style,
135
+ resizeMode,
136
+ ...viewProps
137
+ },
78
138
  ref,
79
139
  ) {
80
140
  // Same precedence as PipelineImage: an explicit prop wins over style.
81
141
  const effectiveCornerRadius = cornerRadius ?? cornerRadiusForStyle(style);
142
+ // Inline arrows are fine here — the hook keeps the callbacks in a ref, so a
143
+ // new identity doesn't recreate the loader (which would reload the image).
82
144
  const loader = usePipelineImageLoader(url, {
83
145
  blur,
84
146
  cornerRadius: effectiveCornerRadius,
85
147
  cache,
86
148
  resize,
149
+ // Same four values as the view's resizeMode; an explicit `fit` decouples
150
+ // them.
151
+ fit: fit ?? resizeMode,
152
+ allowUpscale,
153
+ onLoad,
154
+ onError,
87
155
  });
88
156
 
89
157
  return (
90
158
  <NativeNitroImage
91
159
  // Before the spread so a caller-provided recyclingKey wins.
92
- recyclingKey={url}
160
+ recyclingKey={recyclingKeyFor(url)}
93
161
  {...viewProps}
94
162
  ref={ref}
95
163
  style={style}
164
+ resizeMode={resizeMode}
96
165
  image={loader}
97
166
  />
98
167
  );
@@ -17,9 +17,11 @@ import {
17
17
  resizeForLayout,
18
18
  resizeForStyle,
19
19
  } from './resizeForStyle';
20
+ import type { ImageSource } from './resolveImageSource';
20
21
  import type {
21
22
  CacheOption,
22
23
  CornerRadii,
24
+ ResizeFit,
23
25
  ResizeOptions,
24
26
  } from './specs/nitro-image-toolkit.nitro';
25
27
  import { useImage } from './useImage';
@@ -35,8 +37,12 @@ type NativeImageProps = ReactProps<typeof NativeNitroImage>;
35
37
  export type PipelineImageRef = ComponentRef<typeof NativeNitroImage>;
36
38
 
37
39
  export interface PipelineImageProps extends Omit<NativeImageProps, 'image'> {
38
- /** URL of the image to load through the pipeline. */
39
- url: string;
40
+ /**
41
+ * The image to load through the pipeline: a URL string (`https://`,
42
+ * `file://`, an absolute path) or a `require()`d asset — see
43
+ * {@linkcode ImageSource}.
44
+ */
45
+ url: ImageSource;
40
46
  /**
41
47
  * Gaussian blur sigma in **points** (density-independent). Unlike
42
48
  * `useImage`/`loadImage`, where it is bitmap pixels, the component
@@ -57,6 +63,33 @@ export interface PipelineImageProps extends Omit<NativeImageProps, 'image'> {
57
63
  */
58
64
  cornerRadius?: number | CornerRadii;
59
65
  cache?: CacheOption;
66
+ /**
67
+ * How the source is fitted into the display size when the bitmap is
68
+ * produced — see {@linkcode ResizeFit}. When omitted it follows
69
+ * `resizeMode`, so `resizeMode="contain"` decodes an aspect-fitted bitmap
70
+ * (no crop, no padding) instead of a center-cropped one the view then
71
+ * letterboxes. Set this to decouple the two.
72
+ *
73
+ * One iOS caveat, inherited from nitro-image's `Image`: an `Image` handed
74
+ * to the view is a scale-1 image, which `resizeMode="center"` — the one
75
+ * mode that draws an image at its own size — shows at 1 pt per pixel,
76
+ * i.e. `PixelRatio.get()`× larger than Android draws it. The bitmap is
77
+ * right; the display is not. `NativePipelineImage` draws `center` pixel
78
+ * for pixel on both platforms; use it when you need that mode.
79
+ * @default undefined (derived from `resizeMode`, or `'cover'` if unset)
80
+ */
81
+ fit?: ResizeFit;
82
+ /**
83
+ * `false` never enlarges a source smaller than the display size when the
84
+ * **bitmap** is produced — see {@linkcode ResizeOptions.allowUpscale}: it
85
+ * stays at its own pixel size, so nothing is decoded or blurred larger
86
+ * than the source. The **view** still displays that bitmap per
87
+ * `resizeMode`, so `cover`/`contain`/`stretch` scale it up on screen (and
88
+ * a baked `blur`/`cornerRadius` with it); `resizeMode="center"` shows it
89
+ * at its natural size instead.
90
+ * @default true
91
+ */
92
+ allowUpscale?: boolean;
60
93
  /** Called with the processed `Image` each time a new variant resolves. */
61
94
  onLoad?: (image: Image) => void;
62
95
  /** Called when loading fails. */
@@ -114,10 +147,13 @@ export const PipelineImage = forwardRef<PipelineImageRef, PipelineImageProps>(
114
147
  blur = 0,
115
148
  cornerRadius,
116
149
  cache,
150
+ fit,
151
+ allowUpscale,
117
152
  onLoad,
118
153
  onError,
119
154
  style,
120
155
  onLayout,
156
+ resizeMode,
121
157
  ...viewProps
122
158
  },
123
159
  ref,
@@ -129,7 +165,13 @@ export const PipelineImage = forwardRef<PipelineImageRef, PipelineImageProps>(
129
165
  );
130
166
  // A numeric style is what the caller declared, so it wins and starts the
131
167
  // request a frame earlier; the measured layout is the fallback.
132
- const resize = styleSize ?? layoutSize;
168
+ const size = styleSize ?? layoutSize;
169
+ // The view's resizeMode and the bitmap's fit are the same four values;
170
+ // an explicit `fit` decouples them.
171
+ const effectiveFit = fit ?? resizeMode;
172
+ const resize: ResizeOptions | undefined = size
173
+ ? { ...size, fit: effectiveFit, allowUpscale }
174
+ : undefined;
133
175
  // Same precedence: an explicit prop wins over what style implies.
134
176
  const effectiveCornerRadius =
135
177
  cornerRadius ?? cornerRadiusForStyle(style) ?? 0;
@@ -171,6 +213,7 @@ export const PipelineImage = forwardRef<PipelineImageRef, PipelineImageProps>(
171
213
  {...viewProps}
172
214
  ref={ref}
173
215
  style={style}
216
+ resizeMode={resizeMode}
174
217
  onLayout={handleLayout}
175
218
  image={image}
176
219
  />
package/src/index.ts CHANGED
@@ -9,6 +9,11 @@ export {
9
9
  type PipelineImageProps,
10
10
  type PipelineImageRef,
11
11
  } from './PipelineImage';
12
+ export {
13
+ type ImageSource,
14
+ resolveImageUrl,
15
+ UNREGISTERED_ASSET_URL,
16
+ } from './resolveImageSource';
12
17
  export {
13
18
  cornerRadiusForStyle,
14
19
  resizeForLayout,
@@ -18,6 +23,7 @@ export type {
18
23
  CacheOption,
19
24
  CornerRadii,
20
25
  Options,
26
+ ResizeFit,
21
27
  ResizeOptions,
22
28
  ViewOptions,
23
29
  } from './specs/nitro-image-toolkit.nitro';
@@ -13,7 +13,9 @@ import type {
13
13
  /**
14
14
  * Converts a layout size in points (dp) to the pipeline's `resize` option in
15
15
  * whole bitmap pixels using `PixelRatio.getPixelSizeForLayoutSize`. Returns
16
- * `undefined` unless both values are positive numbers.
16
+ * `undefined` unless both values are positive numbers. Spread a `fit` into
17
+ * the result for the other resize modes — under `contain` and `center` the
18
+ * size is the box the bitmap fits in, not necessarily its final size.
17
19
  */
18
20
  export function resizeForLayout(
19
21
  width: unknown,
@@ -0,0 +1,83 @@
1
+ import { Image as RNImage } from 'react-native';
2
+
3
+ /**
4
+ * What the pipeline can load: a URL string, or the id a `require()`d image
5
+ * asset evaluates to.
6
+ *
7
+ * As a string, `url` is passed to the native loader as-is. Accepted forms:
8
+ *
9
+ * - `https://` / `http://` — fetched over the network and cached on disk.
10
+ * - `file://` URLs and plain absolute paths (`/var/…/photo.jpg`) — read from
11
+ * the file system. The result is cached in memory only; there is nothing
12
+ * to gain from copying a local file into the disk cache.
13
+ * - Android also accepts `content://` URIs and, from a release build's
14
+ * resources, a bare drawable name — which is what `require()` resolves to
15
+ * there. iOS resolves `require()` to a `file://` URL into the app bundle.
16
+ *
17
+ * A `require('./photo.png')` is resolved with `Image.resolveAssetSource`, so
18
+ * it works the same in debug (streamed from Metro) and release (bundled).
19
+ */
20
+ export type ImageSource = string | number;
21
+
22
+ /**
23
+ * Turns an {@linkcode ImageSource} into the URL string the native pipeline
24
+ * loads: strings pass through unchanged, a `require()`d asset is resolved via
25
+ * `Image.resolveAssetSource` (the scale-matched variant, like `<Image>`).
26
+ *
27
+ * The components and hooks call this for you; use it when calling
28
+ * `NitroImagePipeline.loadImage`/`preLoadImage` directly with a `require()`.
29
+ * @throws If `source` is a number that is not a registered asset.
30
+ * @example
31
+ * ```ts
32
+ * const image = await NitroImagePipeline.loadImage(
33
+ * resolveImageUrl(require('./photo.png')),
34
+ * { blur: 4 },
35
+ * );
36
+ * ```
37
+ */
38
+ export function resolveImageUrl(source: ImageSource): string {
39
+ if (typeof source === 'string') {
40
+ return source;
41
+ }
42
+ const resolved = RNImage.resolveAssetSource(source);
43
+ if (resolved == null) {
44
+ throw new Error(`Not a registered image asset: require() id ${source}`);
45
+ }
46
+ return resolved.uri;
47
+ }
48
+
49
+ /**
50
+ * A URL no loader can resolve, standing in for a `require()` id that is not
51
+ * a registered asset. Its scheme has no fetcher on either platform, so the
52
+ * request fails at load time — the same way a missing file does — instead of
53
+ * throwing from a component's render.
54
+ */
55
+ export const UNREGISTERED_ASSET_URL = 'unregistered-asset://';
56
+
57
+ /**
58
+ * {@linkcode resolveImageUrl} for code that runs during render: an
59
+ * unregistered `require()` id yields {@linkcode UNREGISTERED_ASSET_URL} (and
60
+ * a warning in development) rather than throwing.
61
+ */
62
+ export function resolveImageUrlOrFallback(source: ImageSource): string {
63
+ try {
64
+ return resolveImageUrl(source);
65
+ } catch (error) {
66
+ if (__DEV__) {
67
+ console.warn(
68
+ `[react-native-nitro-image-pipeline] ${(error as Error).message}`,
69
+ );
70
+ }
71
+ return UNREGISTERED_ASSET_URL;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * A stable, per-source `recyclingKey` for `<NativeNitroImage>`: the URL
77
+ * itself for strings, and a tag derived from the asset id for a `require()`
78
+ * (resolving it would cost an asset-registry lookup per render for nothing
79
+ * more than a distinct key).
80
+ */
81
+ export function recyclingKeyFor(source: ImageSource): string {
82
+ return typeof source === 'string' ? source : `asset:${source}`;
83
+ }