react-native-nitro-modules 0.26.2 → 0.26.4

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.
@@ -1,4 +1,5 @@
1
1
  require "json"
2
+ require "./nitro_pod_utils"
2
3
 
3
4
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
5
 
@@ -47,25 +48,33 @@ Pod::Spec.new do |s|
47
48
  "cpp/views/CachedProp.hpp",
48
49
  # Public iOS-specific headers that will be exposed in modulemap (for Swift)
49
50
  "ios/core/ArrayBufferHolder.hpp",
50
- "ios/core/AnyMapHolder.hpp",
51
51
  "ios/core/PromiseHolder.hpp",
52
+ "ios/utils/AnyMapUtils.hpp",
52
53
  "ios/utils/Result.hpp",
53
54
  "ios/utils/DateToChronoDate.hpp",
54
55
  "ios/utils/RuntimeError.hpp",
55
56
  "ios/utils/SwiftClosure.hpp",
56
57
  ]
57
58
 
58
- s.pod_target_xcconfig = {
59
+ xcconfig = {
59
60
  # Use C++ 20
60
61
  "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
61
62
  # Enables C++ <-> Swift interop (by default it's only C)
62
63
  "SWIFT_OBJC_INTEROP_MODE" => "objcxx",
63
64
  # Enables stricter modular headers
64
65
  "DEFINES_MODULE" => "YES",
65
- # C++ compiler flags, mainly for RN version and folly.
66
- "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
67
66
  }
68
67
 
68
+ if has_react_native()
69
+ react_native_version = get_react_native_version()
70
+ if (react_native_version < 80)
71
+ # C++ compiler flags, for folly when building as static framework:
72
+ xcconfig["GCC_PREPROCESSOR_DEFINITIONS"] = "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
73
+ end
74
+ end
75
+
76
+ s.pod_target_xcconfig = xcconfig
77
+
69
78
  # Nitro depends on JSI.
70
79
  s.dependency 'React-jsi'
71
80
  # For React Native, we implement nitro::Dispatcher using react::CallInvoker
package/README.md CHANGED
@@ -202,13 +202,13 @@ The following C++ / JS types are supported out of the box:
202
202
  <tr>
203
203
  <td><code>{ ... }</code></td>
204
204
  <td><code>std::shared_ptr&lt;<a href="./cpp/core/AnyMap.hpp">AnyMap</a>&gt;</code></td>
205
- <td><code><a href="./ios/core/AnyMapHolder.swift">AnyMapHolder</a></code></td>
205
+ <td><code><a href="./ios/core/AnyMap.swift">AnyMap</a></code></td>
206
206
  <td><code><a href="./android/src/main/java/com/margelo/nitro/core/AnyMap.kt">AnyMap</a></code></td>
207
207
  </tr>
208
208
  <tr>
209
209
  <td><code>ArrayBuffer</code></td>
210
210
  <td><code>std::shared_ptr&lt;<a href="./cpp/core/ArrayBuffer.hpp">ArrayBuffer</a>&gt;</code></td>
211
- <td><code><a href="./ios/core/ArrayBufferHolder.swift">ArrayBufferHolder</a></code></td>
211
+ <td><code><a href="./ios/core/ArrayBuffer.swift">ArrayBuffer</a></code></td>
212
212
  <td><code><a href="./android/src/main/java/com/margelo/nitro/core/ArrayBuffer.kt">ArrayBuffer</a></code></td>
213
213
  </tr>
214
214
  <tr>
@@ -7,7 +7,7 @@ buildscript {
7
7
  }
8
8
 
9
9
  dependencies {
10
- classpath "com.android.tools.build:gradle:8.10.1"
10
+ classpath "com.android.tools.build:gradle:8.11.1"
11
11
  }
12
12
  }
13
13
 
@@ -1,4 +1,4 @@
1
- Nitro_kotlinVersion=2.0.21
1
+ Nitro_kotlinVersion=2.1.20
2
2
  Nitro_minSdkVersion=23
3
3
  Nitro_targetSdkVersion=35
4
4
  Nitro_compileSdkVersion=34
@@ -3,6 +3,7 @@
3
3
  #include "JAnyMap.hpp"
4
4
  #include "JAnyValue.hpp"
5
5
  #include "JArrayBuffer.hpp"
6
+ #include "JHardwareBufferUtils.hpp"
6
7
  #include "JNitroModules.hpp"
7
8
  #include "JPromise.hpp"
8
9
  #include <fbjni/fbjni.h>
@@ -16,6 +17,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
16
17
  JArrayBuffer::registerNatives();
17
18
  JAnyMap::registerNatives();
18
19
  JAnyValue::registerNatives();
20
+ JHardwareBufferUtils::registerNatives();
19
21
  JPromise::registerNatives();
20
22
 
21
23
  // 2. Initialize the React Native TurboModule C++ part
@@ -0,0 +1,93 @@
1
+ //
2
+ // HardwareBufferArrayBuffer.hpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 14.07.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include "ArrayBuffer.hpp"
11
+ #include "JHardwareBufferUtils.hpp"
12
+ #include <android/hardware_buffer.h>
13
+ #include <android/hardware_buffer_jni.h>
14
+ #include <fbjni/ByteBuffer.h>
15
+ #include <fbjni/fbjni.h>
16
+
17
+ namespace margelo::nitro {
18
+
19
+ using namespace facebook;
20
+
21
+ #if __ANDROID_API__ >= 26
22
+ /**
23
+ * Represents an `ArrayBuffer` that holds a `HardwareBuffer`.
24
+ */
25
+ class HardwareBufferArrayBuffer final : public ArrayBuffer {
26
+ public:
27
+ /**
28
+ * Create a new `HardwareBufferArrayBuffer` instance that wraps the given `HardwareBuffer`.
29
+ * This constructor will add a +1 retain count on the given `hardwareBuffer` using
30
+ * `AHardwareBuffer_acquire(...)`, and release it again once it is destructured.
31
+ */
32
+ explicit HardwareBufferArrayBuffer(AHardwareBuffer* hardwareBuffer)
33
+ : _hardwareBuffer(hardwareBuffer), _dataCached(nullptr), _isLocked(false) {
34
+ AHardwareBuffer_acquire(hardwareBuffer);
35
+ }
36
+
37
+ ~HardwareBufferArrayBuffer() override {
38
+ // Hermes GC can destroy JS objects on a non-JNI Thread.
39
+ unlock();
40
+ jni::ThreadScope::WithClassLoader([&] { AHardwareBuffer_release(_hardwareBuffer); });
41
+ }
42
+
43
+ public:
44
+ /**
45
+ * Unlocks the HardwareBuffer if it was locked.
46
+ * Subsequent calls to `data()` will have to lock the buffer again.
47
+ *
48
+ * It is a good practice to call this when the buffer is likely not being
49
+ * read from using this `HardwareBufferArrayBuffer` instance again anytime soon.
50
+ */
51
+ void unlock() {
52
+ if (_isLocked) {
53
+ AHardwareBuffer_unlock(_hardwareBuffer, nullptr);
54
+ _isLocked = false;
55
+ }
56
+ _dataCached = nullptr;
57
+ }
58
+
59
+ public:
60
+ [[nodiscard]] uint8_t* data() override {
61
+ if (_isLocked && _dataCached != nullptr) {
62
+ // We are still locked on the AHardwareBuffer* and have a valid buffer.
63
+ return _dataCached;
64
+ }
65
+ void* buffer;
66
+ int result = AHardwareBuffer_lock(_hardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_MASK, -1, nullptr, &buffer);
67
+ if (result != 0) {
68
+ throw std::runtime_error("Failed to read HardwareBuffer bytes!");
69
+ }
70
+ _dataCached = static_cast<uint8_t*>(buffer);
71
+ _isLocked = true;
72
+ return _dataCached;
73
+ }
74
+ [[nodiscard]] size_t size() const override {
75
+ return JHardwareBufferUtils::getHardwareBufferSize(_hardwareBuffer);
76
+ }
77
+ [[nodiscard]] bool isOwner() const noexcept override {
78
+ return true;
79
+ }
80
+
81
+ public:
82
+ [[nodiscard]] AHardwareBuffer* getBuffer() const {
83
+ return _hardwareBuffer;
84
+ }
85
+
86
+ private:
87
+ AHardwareBuffer* _hardwareBuffer;
88
+ uint8_t* _dataCached;
89
+ bool _isLocked;
90
+ };
91
+ #endif
92
+
93
+ } // namespace margelo::nitro
@@ -9,6 +9,9 @@
9
9
 
10
10
  #include "ArrayBuffer.hpp"
11
11
  #include "ByteBufferArrayBuffer.hpp"
12
+ #include "HardwareBufferArrayBuffer.hpp"
13
+ #include <android/hardware_buffer.h>
14
+ #include <android/hardware_buffer_jni.h>
12
15
  #include <fbjni/ByteBuffer.h>
13
16
  #include <fbjni/fbjni.h>
14
17
  #include <functional>
@@ -38,9 +41,23 @@ public:
38
41
  /**
39
42
  * Create a new `JArrayBuffer` that wraps the given `ByteBuffer` from Java.
40
43
  */
41
- static jni::local_ref<JArrayBuffer::jhybriddata> initHybrid(jni::alias_ref<jhybridobject>, jni::alias_ref<jni::JByteBuffer> buffer) {
44
+ static jni::local_ref<JArrayBuffer::jhybriddata> initHybridByteBuffer(jni::alias_ref<jhybridobject>,
45
+ jni::alias_ref<jni::JByteBuffer> buffer) {
42
46
  return makeCxxInstance(buffer);
43
47
  }
48
+ /**
49
+ * Create a new `JArrayBuffer` that wraps the given `HardwareBuffer` from Java.
50
+ */
51
+ static jni::local_ref<JArrayBuffer::jhybriddata>
52
+ initHybridHardwareBuffer(jni::alias_ref<jhybridobject>, [[maybe_unused]] jni::alias_ref<jni::JObject> boxedHardwareBuffer) {
53
+ #if __ANDROID_API__ >= 26
54
+ // Cast jobject* to AHardwareBuffer*. It has a retain count of 0 which will be retained in `HardwareBufferArrayBuffer(..)`.
55
+ AHardwareBuffer* hardwareBuffer = AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedHardwareBuffer.get());
56
+ return makeCxxInstance(hardwareBuffer);
57
+ #else
58
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
59
+ #endif
60
+ }
44
61
 
45
62
  public:
46
63
  /**
@@ -51,6 +68,18 @@ public:
51
68
  return byteBufferArrayBuffer != nullptr;
52
69
  }
53
70
 
71
+ /**
72
+ * Get whether the `ArrayBuffer` is holding data from a `HardwareBuffer`.
73
+ */
74
+ bool getIsHardwareBuffer() {
75
+ #if __ANDROID_API__ >= 26
76
+ auto hardwareBufferArrayBuffer = std::dynamic_pointer_cast<HardwareBufferArrayBuffer>(_arrayBuffer);
77
+ return hardwareBufferArrayBuffer != nullptr;
78
+ #else
79
+ return false;
80
+ #endif
81
+ }
82
+
54
83
  /**
55
84
  * Get whether the `ArrayBuffer` is owning the data and can safely hold onto it longer.
56
85
  */
@@ -67,7 +96,7 @@ public:
67
96
  * `ByteBuffer`. In this case, `getBuffer()` will **copy** the data into a new `ByteBuffer` if
68
97
  * `copyIfNeeded` is `true`, and **wrap** the data into a new `ByteBuffer` if `copyIfNeeded` is false.
69
98
  */
70
- jni::local_ref<jni::JByteBuffer> getByteBuffer(bool copyIfNeeded) {
99
+ [[nodiscard]] jni::local_ref<jni::JByteBuffer> getByteBuffer(bool copyIfNeeded) {
71
100
  auto byteBufferArrayBuffer = std::dynamic_pointer_cast<ByteBufferArrayBuffer>(_arrayBuffer);
72
101
  if (byteBufferArrayBuffer != nullptr) {
73
102
  // It is a `ByteBufferArrayBuffer`, which has a `ByteBuffer` underneath!
@@ -88,6 +117,16 @@ public:
88
117
  }
89
118
  }
90
119
 
120
+ [[nodiscard]] jni::local_ref<jni::JObject> getHardwareBufferBoxed() {
121
+ #if __ANDROID_API__ >= 26
122
+ AHardwareBuffer* buffer = getHardwareBuffer();
123
+ jobject boxed = AHardwareBuffer_toHardwareBuffer(jni::Environment::current(), buffer);
124
+ return jni::make_local(boxed);
125
+ #else
126
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
127
+ #endif
128
+ }
129
+
91
130
  int getBufferSize() {
92
131
  return static_cast<int>(_arrayBuffer->size());
93
132
  }
@@ -96,16 +135,38 @@ public:
96
135
  /**
97
136
  * Get the underlying `ArrayBuffer`.
98
137
  */
99
- std::shared_ptr<ArrayBuffer> getArrayBuffer() const {
138
+ [[nodiscard]] std::shared_ptr<ArrayBuffer> getArrayBuffer() const {
100
139
  return _arrayBuffer;
101
140
  }
141
+ /**
142
+ * Get the underlying `HardwareBuffer` if it has one.
143
+ * This method will throw if this `ArrayBuffer` was not created with a `HardwareBuffer`.
144
+ */
145
+ [[nodiscard]] AHardwareBuffer* getHardwareBuffer() const {
146
+ #if __ANDROID_API__ >= 26
147
+ auto hardwareBufferArrayBuffer = std::dynamic_pointer_cast<HardwareBufferArrayBuffer>(_arrayBuffer);
148
+ if (hardwareBufferArrayBuffer != nullptr) {
149
+ return hardwareBufferArrayBuffer->getBuffer();
150
+ } else {
151
+ throw std::runtime_error("The underlying buffer is not a HardwareBuffer!");
152
+ }
153
+ #else
154
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
155
+ #endif
156
+ }
102
157
 
103
158
  private:
104
- JArrayBuffer(const std::shared_ptr<ArrayBuffer>& arrayBuffer) : _arrayBuffer(arrayBuffer) {}
105
- JArrayBuffer(jni::alias_ref<jni::JByteBuffer> byteBuffer) {
159
+ explicit JArrayBuffer(const std::shared_ptr<ArrayBuffer>& arrayBuffer) : _arrayBuffer(arrayBuffer) {}
160
+ explicit JArrayBuffer(const jni::alias_ref<jni::JByteBuffer>& byteBuffer) {
106
161
  _arrayBuffer = std::make_shared<ByteBufferArrayBuffer>(byteBuffer);
107
162
  }
108
163
 
164
+ #if __ANDROID_API__ >= 26
165
+ explicit JArrayBuffer(AHardwareBuffer* /* 0 retain */ hardwareBuffer) {
166
+ _arrayBuffer = std::make_shared<HardwareBufferArrayBuffer>(hardwareBuffer);
167
+ }
168
+ #endif
169
+
109
170
  private:
110
171
  friend HybridBase;
111
172
  using HybridBase::HybridBase;
@@ -114,9 +175,12 @@ private:
114
175
  public:
115
176
  static void registerNatives() {
116
177
  registerHybrid(
117
- {makeNativeMethod("initHybrid", JArrayBuffer::initHybrid), makeNativeMethod("getByteBuffer", JArrayBuffer::getByteBuffer),
118
- makeNativeMethod("getIsByteBuffer", JArrayBuffer::getIsByteBuffer), makeNativeMethod("getIsOwner", JArrayBuffer::getIsOwner),
119
- makeNativeMethod("getBufferSize", JArrayBuffer::getBufferSize)});
178
+ {makeNativeMethod("initHybrid", JArrayBuffer::initHybridByteBuffer),
179
+ makeNativeMethod("initHybridBoxedHardwareBuffer", JArrayBuffer::initHybridHardwareBuffer),
180
+ makeNativeMethod("getByteBuffer", JArrayBuffer::getByteBuffer), makeNativeMethod("getIsByteBuffer", JArrayBuffer::getIsByteBuffer),
181
+ makeNativeMethod("getHardwareBufferBoxed", JArrayBuffer::getHardwareBufferBoxed),
182
+ makeNativeMethod("getIsHardwareBuffer", JArrayBuffer::getIsHardwareBuffer),
183
+ makeNativeMethod("getIsOwner", JArrayBuffer::getIsOwner), makeNativeMethod("getBufferSize", JArrayBuffer::getBufferSize)});
120
184
  }
121
185
  };
122
186
 
@@ -0,0 +1,98 @@
1
+ //
2
+ // JHardwareBufferUtils.cpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 11.07.25.
6
+ //
7
+
8
+ #include "JHardwareBufferUtils.hpp"
9
+ #include "NitroDefines.hpp"
10
+ #include <android/hardware_buffer_jni.h>
11
+
12
+ namespace margelo::nitro {
13
+
14
+ size_t JHardwareBufferUtils::getHardwareBufferSize([[maybe_unused]] AHardwareBuffer* hardwareBuffer) {
15
+ #if __ANDROID_API__ >= 26
16
+ AHardwareBuffer_Desc description;
17
+ AHardwareBuffer_describe(hardwareBuffer, &description);
18
+ size_t sourceSize = description.height * description.stride;
19
+ return sourceSize;
20
+ #else
21
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
22
+ #endif
23
+ }
24
+
25
+ jni::local_ref<jni::JObject>
26
+ JHardwareBufferUtils::copyHardwareBufferBoxedNew(jni::alias_ref<jni::JClass>,
27
+ [[maybe_unused]] jni::alias_ref<jni::JObject> boxedHardwareBuffer) {
28
+ #if __ANDROID_API__ >= 26
29
+ // 1. Unbox HardwareBuffer from jobject
30
+ AHardwareBuffer* sourceHardwareBuffer = AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedHardwareBuffer.get());
31
+ // 2. Describe the buffer
32
+ AHardwareBuffer_Desc description;
33
+ AHardwareBuffer_describe(sourceHardwareBuffer, &description);
34
+ // 3. Create a new buffer from the same description
35
+ AHardwareBuffer* destinationHardwareBuffer;
36
+ AHardwareBuffer_allocate(&description, &destinationHardwareBuffer);
37
+ // 4. Copy the data over
38
+ copyHardwareBuffer(sourceHardwareBuffer, destinationHardwareBuffer);
39
+ // 5. Box it into jobject again
40
+ jobject boxed = AHardwareBuffer_toHardwareBuffer(jni::Environment::current(), destinationHardwareBuffer);
41
+ return jni::make_local(boxed);
42
+ #else
43
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
44
+ #endif
45
+ }
46
+
47
+ void JHardwareBufferUtils::copyHardwareBufferBoxed(jni::alias_ref<jni::JClass>,
48
+ [[maybe_unused]] jni::alias_ref<jni::JObject> boxedSourceHardwareBuffer,
49
+ [[maybe_unused]] jni::alias_ref<jni::JObject> boxedDestinationHardwareBuffer) {
50
+ #if __ANDROID_API__ >= 26
51
+ // 1. Unbox HardwareBuffer from jobject
52
+ AHardwareBuffer* sourceHardwareBuffer = AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedSourceHardwareBuffer.get());
53
+ AHardwareBuffer* destinationHardwareBuffer =
54
+ AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedDestinationHardwareBuffer.get());
55
+ // 2. Copy data over from source -> destination
56
+ copyHardwareBuffer(sourceHardwareBuffer, destinationHardwareBuffer);
57
+ #else
58
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
59
+ #endif
60
+ }
61
+
62
+ void JHardwareBufferUtils::copyHardwareBuffer([[maybe_unused]] AHardwareBuffer* sourceHardwareBuffer,
63
+ [[maybe_unused]] AHardwareBuffer* destinationHardwareBuffer) {
64
+ #if __ANDROID_API__ >= 26
65
+ // 1. Get info about source buffer
66
+ size_t sourceSize = getHardwareBufferSize(sourceHardwareBuffer);
67
+
68
+ // 2. Get info about the destination buffer
69
+ #ifdef NITRO_DEBUG
70
+ size_t destinationSize = getHardwareBufferSize(sourceHardwareBuffer);
71
+ if (sourceSize != destinationSize) {
72
+ throw std::runtime_error("Source HardwareBuffer (" + std::to_string(sourceSize) + " bytes) and destination HardwareBuffer (" +
73
+ std::to_string(destinationSize) + " bytes) are not the same size!");
74
+ }
75
+ #endif
76
+
77
+ // 3. Copy data over
78
+ void* sourceData;
79
+ void* destinationData;
80
+ int lockSource = AHardwareBuffer_lock(sourceHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_MASK, -1, nullptr, &sourceData);
81
+ if (lockSource != 0) {
82
+ throw std::runtime_error("Failed to lock source HardwareBuffer! Error: " + std::to_string(lockSource));
83
+ }
84
+ int lockDestination =
85
+ AHardwareBuffer_lock(destinationHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK, -1, nullptr, &destinationData);
86
+ if (lockDestination != 0) {
87
+ AHardwareBuffer_unlock(sourceHardwareBuffer, nullptr);
88
+ throw std::runtime_error("Failed to lock destination HardwareBuffer! Error: " + std::to_string(lockDestination));
89
+ }
90
+ memcpy(destinationData, sourceData, sourceSize);
91
+ AHardwareBuffer_unlock(sourceHardwareBuffer, nullptr);
92
+ AHardwareBuffer_unlock(destinationHardwareBuffer, nullptr);
93
+ #else
94
+ throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
95
+ #endif
96
+ }
97
+
98
+ } // namespace margelo::nitro
@@ -0,0 +1,39 @@
1
+ //
2
+ // JHardwareBufferUtils.hpp
3
+ // react-native-nitro
4
+ //
5
+ // Created by Marc Rousavy on 11.07.25.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include <android/hardware_buffer.h>
11
+ #include <fbjni/fbjni.h>
12
+
13
+ namespace margelo::nitro {
14
+
15
+ using namespace facebook;
16
+
17
+ class JHardwareBufferUtils final : public jni::JavaClass<JHardwareBufferUtils> {
18
+ public:
19
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/utils/HardwareBufferUtils;";
20
+
21
+ public:
22
+ static size_t getHardwareBufferSize(AHardwareBuffer* hardwareBuffer);
23
+
24
+ static void copyHardwareBufferBoxed(jni::alias_ref<jni::JClass>, jni::alias_ref<jni::JObject> boxedSourceHardwareBuffer,
25
+ jni::alias_ref<jni::JObject> boxedDestinationHardwareBuffer);
26
+
27
+ static jni::local_ref<jni::JObject> copyHardwareBufferBoxedNew(jni::alias_ref<jni::JClass>,
28
+ jni::alias_ref<jni::JObject> boxedSourceHardwareBuffer);
29
+
30
+ static void copyHardwareBuffer(AHardwareBuffer* sourceHardwareBuffer, AHardwareBuffer* destinationHardwareBuffer);
31
+
32
+ public:
33
+ static void registerNatives() {
34
+ javaClassStatic()->registerNatives({makeNativeMethod("copyHardwareBuffer", JHardwareBufferUtils::copyHardwareBufferBoxed),
35
+ makeNativeMethod("copyHardwareBuffer", JHardwareBufferUtils::copyHardwareBufferBoxedNew)});
36
+ }
37
+ };
38
+
39
+ } // namespace margelo::nitro
@@ -1,11 +1,18 @@
1
1
  package com.margelo.nitro.core
2
2
 
3
3
  import androidx.annotation.Keep
4
+ import android.hardware.HardwareBuffer
5
+ import android.os.Build
6
+ import androidx.annotation.RequiresApi
4
7
  import com.facebook.jni.HybridData
5
8
  import com.facebook.proguard.annotations.DoNotStrip
9
+ import com.margelo.nitro.utils.HardwareBufferUtils
6
10
  import dalvik.annotation.optimization.FastNative
7
11
  import java.nio.ByteBuffer
8
12
 
13
+ // AHardwareBuffer* needs to be boxed in jobject*
14
+ typealias BoxedHardwareBuffer = Any
15
+
9
16
  /**
10
17
  * An ArrayBuffer instance shared between native (Kotlin/C++) and JS.
11
18
  *
@@ -41,6 +48,15 @@ class ArrayBuffer {
41
48
  val isByteBuffer: Boolean
42
49
  get() = getIsByteBuffer()
43
50
 
51
+ /**
52
+ * Whether this `ArrayBuffer` is holding a `HardwareBuffer`, or not.
53
+ * - If the `ArrayBuffer` holds a `HardwareBuffer`, `getHardwareBuffer()` can safely be called without copy.
54
+ * - If the `ArrayBuffer` doesn't hold a `HardwareBuffer`, `getHardwareBuffer()` will throw.
55
+ * You will need to call `getByteBuffer(copyIfNeeded)` instead.
56
+ */
57
+ val isHardwareBuffer: Boolean
58
+ get() = getIsHardwareBuffer()
59
+
44
60
  /**
45
61
  * Get the size of bytes in this `ArrayBuffer`.
46
62
  */
@@ -64,6 +80,16 @@ class ArrayBuffer {
64
80
  return getByteBuffer(copyIfNeeded)
65
81
  }
66
82
 
83
+ /**
84
+ * Get the underlying `HardwareBuffer` if this `ArrayBuffer` was created with one.
85
+ * @throws Error if this `ArrayBuffer` was not created with a `HardwareBuffer`.
86
+ */
87
+ @RequiresApi(Build.VERSION_CODES.O)
88
+ fun getHardwareBuffer(): HardwareBuffer {
89
+ val boxed = getHardwareBufferBoxed()
90
+ return boxed as HardwareBuffer
91
+ }
92
+
67
93
  /**
68
94
  * Create a new **owning-** `ArrayBuffer` that holds the given `ByteBuffer`.
69
95
  * The `ByteBuffer` needs to remain valid for as long as the `ArrayBuffer` is alive.
@@ -76,6 +102,18 @@ class ArrayBuffer {
76
102
  mHybridData = initHybrid(byteBuffer)
77
103
  }
78
104
 
105
+ /**
106
+ * Create a new **owning-** `ArrayBuffer` that holds the given `HardwareBuffer`.
107
+ * The `HardwareBuffer` needs to remain valid for as long as the `ArrayBuffer` is alive.
108
+ */
109
+ @RequiresApi(Build.VERSION_CODES.O)
110
+ constructor(hardwareBuffer: HardwareBuffer) {
111
+ if (hardwareBuffer.isClosed) {
112
+ throw Error("Cannot create ArrayBuffer from an already-closed HardwareBuffer!")
113
+ }
114
+ mHybridData = initHybridBoxedHardwareBuffer(hardwareBuffer)
115
+ }
116
+
79
117
  /**
80
118
  * Create a new **non-owning-** `ArrayBuffer` that holds foreign data, potentially coming from JS.
81
119
  */
@@ -87,12 +125,19 @@ class ArrayBuffer {
87
125
  }
88
126
 
89
127
  private external fun initHybrid(buffer: ByteBuffer): HybridData
128
+ @RequiresApi(Build.VERSION_CODES.O)
129
+ private external fun initHybridBoxedHardwareBuffer(hardwareBufferBoxed: BoxedHardwareBuffer): HybridData
130
+
90
131
  private external fun getByteBuffer(copyIfNeeded: Boolean): ByteBuffer
132
+ private external fun getHardwareBufferBoxed(): BoxedHardwareBuffer
133
+
91
134
  @FastNative
92
135
  private external fun getIsOwner(): Boolean
93
136
  @FastNative
94
137
  private external fun getIsByteBuffer(): Boolean
95
138
  @FastNative
139
+ private external fun getIsHardwareBuffer(): Boolean
140
+ @FastNative
96
141
  private external fun getBufferSize(): Int
97
142
 
98
143
  companion object {
@@ -108,8 +153,13 @@ class ArrayBuffer {
108
153
  * Copy the given `ArrayBuffer` into a new **owning** `ArrayBuffer`.
109
154
  */
110
155
  fun copy(other: ArrayBuffer): ArrayBuffer {
111
- val byteBuffer = other.getBuffer(false)
112
- return copy(byteBuffer)
156
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && other.isHardwareBuffer) {
157
+ val hardwareBuffer = other.getHardwareBuffer()
158
+ return copy(hardwareBuffer)
159
+ } else {
160
+ val byteBuffer = other.getBuffer(false)
161
+ return copy(byteBuffer)
162
+ }
113
163
  }
114
164
 
115
165
  /**
@@ -129,6 +179,14 @@ class ArrayBuffer {
129
179
  // 5. Create a new `ArrayBuffer`
130
180
  return ArrayBuffer(newBuffer)
131
181
  }
182
+ /**
183
+ * Copy the given `HardwareBuffer` into a new **owning** `ArrayBuffer`.
184
+ */
185
+ @RequiresApi(Build.VERSION_CODES.O)
186
+ fun copy(hardwareBuffer: HardwareBuffer): ArrayBuffer {
187
+ val copy = HardwareBufferUtils.copyHardwareBuffer(hardwareBuffer)
188
+ return ArrayBuffer(copy)
189
+ }
132
190
 
133
191
  /**
134
192
  * Wrap the given `ByteBuffer` in a new **owning** `ArrayBuffer`.
@@ -137,5 +195,13 @@ class ArrayBuffer {
137
195
  byteBuffer.rewind()
138
196
  return ArrayBuffer(byteBuffer)
139
197
  }
198
+
199
+ /**
200
+ * Wrap the given `HardwareBuffer` in a new **owning** `ArrayBuffer`.
201
+ */
202
+ @RequiresApi(Build.VERSION_CODES.O)
203
+ fun wrap(hardwareBuffer: HardwareBuffer): ArrayBuffer {
204
+ return ArrayBuffer(hardwareBuffer)
205
+ }
140
206
  }
141
207
  }
@@ -0,0 +1,15 @@
1
+ package com.margelo.nitro.utils
2
+
3
+ import android.hardware.HardwareBuffer
4
+ import android.os.Build
5
+ import androidx.annotation.RequiresApi
6
+
7
+ /**
8
+ * Updates this [[HardwareBuffer]] with the data in the given [[hardwareBuffer]].
9
+ * The given [[hardwareBuffer]] has to have the same description and shape as this [[HardwareBuffer]].
10
+ */
11
+ @RequiresApi(Build.VERSION_CODES.O)
12
+ fun HardwareBuffer.updateFrom(hardwareBuffer: HardwareBuffer) {
13
+ HardwareBufferUtils.copyHardwareBuffer(hardwareBuffer, this)
14
+ }
15
+
@@ -0,0 +1,46 @@
1
+ package com.margelo.nitro.utils
2
+
3
+ import androidx.annotation.Keep
4
+ import com.facebook.proguard.annotations.DoNotStrip
5
+ import android.hardware.HardwareBuffer
6
+ import android.os.Build
7
+ import androidx.annotation.RequiresApi
8
+
9
+ /**
10
+ * HardwareBuffers are special types in JNI (`AHardwareBuffer*`)
11
+ * and have to be boxed to `jobject*`.
12
+ */
13
+ typealias BoxedHardwareBuffer = Any
14
+
15
+ @Suppress("KotlinJniMissingFunction")
16
+ @Keep
17
+ @DoNotStrip
18
+ class HardwareBufferUtils {
19
+ companion object {
20
+ @JvmStatic
21
+ @RequiresApi(Build.VERSION_CODES.O)
22
+ private external fun copyHardwareBuffer(sourceHardwareBuffer: BoxedHardwareBuffer): BoxedHardwareBuffer
23
+ @JvmStatic
24
+ @RequiresApi(Build.VERSION_CODES.O)
25
+ private external fun copyHardwareBuffer(sourceHardwareBuffer: BoxedHardwareBuffer, destinationHardwareBuffer: BoxedHardwareBuffer)
26
+
27
+ /**
28
+ * Copies the given [[hardwareBuffer]] into a new, identically shaped [[HardwareBuffer]].
29
+ */
30
+ @Throws
31
+ @RequiresApi(Build.VERSION_CODES.O)
32
+ fun copyHardwareBuffer(hardwareBuffer: HardwareBuffer): HardwareBuffer {
33
+ val resultBoxed = copyHardwareBuffer(hardwareBuffer as Any)
34
+ return resultBoxed as HardwareBuffer
35
+ }
36
+
37
+ /**
38
+ * Copies the given [[source]] [[HardwareBuffer]] into the given [[destination]] [[HardwareBuffer]].
39
+ */
40
+ @Throws
41
+ @RequiresApi(Build.VERSION_CODES.O)
42
+ fun copyHardwareBuffer(source: HardwareBuffer, destination: HardwareBuffer) {
43
+ copyHardwareBuffer(source as Any, destination as Any)
44
+ }
45
+ }
46
+ }
@@ -31,6 +31,10 @@ std::shared_ptr<ArrayBuffer> ArrayBuffer::copy(const std::vector<uint8_t>& data)
31
31
  return ArrayBuffer::copy(data.data(), data.size());
32
32
  }
33
33
 
34
+ std::shared_ptr<ArrayBuffer> ArrayBuffer::copy(const std::shared_ptr<ArrayBuffer>& buffer) {
35
+ return ArrayBuffer::copy(buffer->data(), buffer->size());
36
+ }
37
+
34
38
  std::shared_ptr<ArrayBuffer> ArrayBuffer::allocate(size_t size) {
35
39
  uint8_t* data = new uint8_t[size];
36
40
  return ArrayBuffer::wrap(data, size, [=]() { delete[] data; });