react-native-nitro-modules 0.26.1 → 0.26.3
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/android/build.gradle +2 -2
- package/android/src/main/cpp/JNIOnLoad.cpp +2 -0
- package/android/src/main/cpp/core/HardwareBufferArrayBuffer.hpp +93 -0
- package/android/src/main/cpp/core/JAnyMap.hpp +13 -1
- package/android/src/main/cpp/core/JArrayBuffer.hpp +72 -8
- package/android/src/main/cpp/utils/JHardwareBufferUtils.cpp +95 -0
- package/android/src/main/cpp/utils/JHardwareBufferUtils.hpp +39 -0
- package/android/src/main/java/com/margelo/nitro/core/AnyMap.kt +1 -0
- package/android/src/main/java/com/margelo/nitro/core/ArrayBuffer.kt +68 -2
- package/android/src/main/java/com/margelo/nitro/utils/HardwareBuffer+updateFrom.kt +15 -0
- package/android/src/main/java/com/margelo/nitro/utils/HardwareBufferUtils.kt +42 -0
- package/cpp/core/AnyMap.cpp +8 -0
- package/cpp/core/AnyMap.hpp +4 -0
- package/cpp/core/ArrayBuffer.cpp +4 -0
- package/cpp/core/ArrayBuffer.hpp +4 -0
- package/cpp/core/HybridFunction.hpp +3 -2
- package/cpp/platform/NitroLogger.hpp +2 -1
- package/cpp/utils/NitroDefines.hpp +1 -1
- package/ios/core/AnyMapHolder.swift +13 -0
- package/package.json +1 -1
package/android/build.gradle
CHANGED
|
@@ -7,7 +7,7 @@ buildscript {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
dependencies {
|
|
10
|
-
classpath "com.android.tools.build:gradle:8.
|
|
10
|
+
classpath "com.android.tools.build:gradle:8.11.0"
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -51,7 +51,7 @@ android {
|
|
|
51
51
|
externalNativeBuild {
|
|
52
52
|
cmake {
|
|
53
53
|
cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
|
|
54
|
-
arguments "-DANDROID_STL=c++_shared"
|
|
54
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
55
55
|
abiFilters (*reactNativeArchitectures())
|
|
56
56
|
|
|
57
57
|
buildTypes {
|
|
@@ -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
|
|
@@ -40,7 +40,7 @@ private:
|
|
|
40
40
|
JAnyMap() {
|
|
41
41
|
_map = std::make_shared<AnyMap>();
|
|
42
42
|
}
|
|
43
|
-
JAnyMap(const std::shared_ptr<AnyMap>& map) : _map(map) {}
|
|
43
|
+
explicit JAnyMap(const std::shared_ptr<AnyMap>& map) : _map(map) {}
|
|
44
44
|
|
|
45
45
|
protected:
|
|
46
46
|
bool contains(const std::string& key) {
|
|
@@ -52,6 +52,17 @@ protected:
|
|
|
52
52
|
void clear() {
|
|
53
53
|
_map->clear();
|
|
54
54
|
}
|
|
55
|
+
jni::local_ref<jni::JArrayClass<jni::JString>> getAllKeys() {
|
|
56
|
+
auto& map = _map->getMap();
|
|
57
|
+
auto array = jni::JArrayClass<jni::JString>::newArray(map.size());
|
|
58
|
+
size_t index = 0;
|
|
59
|
+
for (const auto& pair : map) {
|
|
60
|
+
auto jKey = jni::make_jstring(pair.first);
|
|
61
|
+
array->setElement(index, *jKey);
|
|
62
|
+
index++;
|
|
63
|
+
}
|
|
64
|
+
return array;
|
|
65
|
+
}
|
|
55
66
|
|
|
56
67
|
protected:
|
|
57
68
|
bool isNull(const std::string& key) {
|
|
@@ -163,6 +174,7 @@ public:
|
|
|
163
174
|
makeNativeMethod("contains", JAnyMap::contains),
|
|
164
175
|
makeNativeMethod("remove", JAnyMap::remove),
|
|
165
176
|
makeNativeMethod("clear", JAnyMap::clear),
|
|
177
|
+
makeNativeMethod("getAllKeys", JAnyMap::getAllKeys),
|
|
166
178
|
// is
|
|
167
179
|
makeNativeMethod("isNull", JAnyMap::isNull),
|
|
168
180
|
makeNativeMethod("isDouble", JAnyMap::isDouble),
|
|
@@ -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>
|
|
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> initHybridHardwareBuffer(jni::alias_ref<jhybridobject>,
|
|
52
|
+
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
|
|
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::
|
|
118
|
-
makeNativeMethod("
|
|
119
|
-
makeNativeMethod("
|
|
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,95 @@
|
|
|
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(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> JHardwareBufferUtils::copyHardwareBufferBoxedNew(jni::alias_ref<jni::JClass>,
|
|
26
|
+
jni::alias_ref<jni::JObject> boxedHardwareBuffer) {
|
|
27
|
+
#if __ANDROID_API__ >= 26
|
|
28
|
+
// 1. Unbox HardwareBuffer from jobject
|
|
29
|
+
AHardwareBuffer* sourceHardwareBuffer = AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedHardwareBuffer.get());
|
|
30
|
+
// 2. Describe the buffer
|
|
31
|
+
AHardwareBuffer_Desc description;
|
|
32
|
+
AHardwareBuffer_describe(sourceHardwareBuffer, &description);
|
|
33
|
+
// 3. Create a new buffer from the same description
|
|
34
|
+
AHardwareBuffer* destinationHardwareBuffer;
|
|
35
|
+
AHardwareBuffer_allocate(&description, &destinationHardwareBuffer);
|
|
36
|
+
// 4. Copy the data over
|
|
37
|
+
copyHardwareBuffer(sourceHardwareBuffer, destinationHardwareBuffer);
|
|
38
|
+
// 5. Box it into jobject again
|
|
39
|
+
jobject boxed = AHardwareBuffer_toHardwareBuffer(jni::Environment::current(), destinationHardwareBuffer);
|
|
40
|
+
return jni::make_local(boxed);
|
|
41
|
+
#else
|
|
42
|
+
throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
|
|
43
|
+
#endif
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void JHardwareBufferUtils::copyHardwareBufferBoxed(jni::alias_ref<jni::JClass>, jni::alias_ref<jni::JObject> boxedSourceHardwareBuffer,
|
|
47
|
+
jni::alias_ref<jni::JObject> boxedDestinationHardwareBuffer) {
|
|
48
|
+
#if __ANDROID_API__ >= 26
|
|
49
|
+
// 1. Unbox HardwareBuffer from jobject
|
|
50
|
+
AHardwareBuffer* sourceHardwareBuffer = AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedSourceHardwareBuffer.get());
|
|
51
|
+
AHardwareBuffer* destinationHardwareBuffer =
|
|
52
|
+
AHardwareBuffer_fromHardwareBuffer(jni::Environment::current(), boxedDestinationHardwareBuffer.get());
|
|
53
|
+
// 2. Copy data over from source -> destination
|
|
54
|
+
copyHardwareBuffer(sourceHardwareBuffer, destinationHardwareBuffer);
|
|
55
|
+
#else
|
|
56
|
+
throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
|
|
57
|
+
#endif
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void JHardwareBufferUtils::copyHardwareBuffer(AHardwareBuffer* sourceHardwareBuffer, AHardwareBuffer* destinationHardwareBuffer) {
|
|
61
|
+
#if __ANDROID_API__ >= 26
|
|
62
|
+
// 1. Get info about source buffer
|
|
63
|
+
size_t sourceSize = getHardwareBufferSize(sourceHardwareBuffer);
|
|
64
|
+
|
|
65
|
+
// 2. Get info about the destination buffer
|
|
66
|
+
#ifdef NITRO_DEBUG
|
|
67
|
+
size_t destinationSize = getHardwareBufferSize(sourceHardwareBuffer);
|
|
68
|
+
if (sourceSize != destinationSize) {
|
|
69
|
+
throw std::runtime_error("Source HardwareBuffer (" + std::to_string(sourceSize) + " bytes) and destination HardwareBuffer (" +
|
|
70
|
+
std::to_string(destinationSize) + " bytes) are not the same size!");
|
|
71
|
+
}
|
|
72
|
+
#endif
|
|
73
|
+
|
|
74
|
+
// 3. Copy data over
|
|
75
|
+
void* sourceData;
|
|
76
|
+
void* destinationData;
|
|
77
|
+
int lockSource = AHardwareBuffer_lock(sourceHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_MASK, -1, nullptr, &sourceData);
|
|
78
|
+
if (lockSource != 0) {
|
|
79
|
+
throw std::runtime_error("Failed to lock source HardwareBuffer! Error: " + std::to_string(lockSource));
|
|
80
|
+
}
|
|
81
|
+
int lockDestination =
|
|
82
|
+
AHardwareBuffer_lock(destinationHardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK, -1, nullptr, &destinationData);
|
|
83
|
+
if (lockDestination != 0) {
|
|
84
|
+
AHardwareBuffer_unlock(sourceHardwareBuffer, nullptr);
|
|
85
|
+
throw std::runtime_error("Failed to lock destination HardwareBuffer! Error: " + std::to_string(lockDestination));
|
|
86
|
+
}
|
|
87
|
+
memcpy(destinationData, sourceData, sourceSize);
|
|
88
|
+
AHardwareBuffer_unlock(sourceHardwareBuffer, nullptr);
|
|
89
|
+
AHardwareBuffer_unlock(destinationHardwareBuffer, nullptr);
|
|
90
|
+
#else
|
|
91
|
+
throw std::runtime_error("ArrayBuffer(HardwareBuffer) requires NDK API 26 or above! (minSdk >= 26)");
|
|
92
|
+
#endif
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
} // 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
|
-
|
|
112
|
-
|
|
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,42 @@
|
|
|
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
|
+
* HardwareBuffers are special types in JNI (`AHardwareBuffer*`)
|
|
9
|
+
* and have to be boxed to `jobject*`.
|
|
10
|
+
*/
|
|
11
|
+
typealias BoxedHardwareBuffer = Any
|
|
12
|
+
|
|
13
|
+
@Suppress("KotlinJniMissingFunction")
|
|
14
|
+
class HardwareBufferUtils {
|
|
15
|
+
companion object {
|
|
16
|
+
@JvmStatic
|
|
17
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
18
|
+
private external fun copyHardwareBuffer(sourceHardwareBuffer: BoxedHardwareBuffer): BoxedHardwareBuffer
|
|
19
|
+
@JvmStatic
|
|
20
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
21
|
+
private external fun copyHardwareBuffer(sourceHardwareBuffer: BoxedHardwareBuffer, destinationHardwareBuffer: BoxedHardwareBuffer)
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Copies the given [[hardwareBuffer]] into a new, identically shaped [[HardwareBuffer]].
|
|
25
|
+
*/
|
|
26
|
+
@Throws
|
|
27
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
28
|
+
fun copyHardwareBuffer(hardwareBuffer: HardwareBuffer): HardwareBuffer {
|
|
29
|
+
val resultBoxed = copyHardwareBuffer(hardwareBuffer as Any)
|
|
30
|
+
return resultBoxed as HardwareBuffer
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Copies the given [[source]] [[HardwareBuffer]] into the given [[destination]] [[HardwareBuffer]].
|
|
35
|
+
*/
|
|
36
|
+
@Throws
|
|
37
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
38
|
+
fun copyHardwareBuffer(source: HardwareBuffer, destination: HardwareBuffer) {
|
|
39
|
+
copyHardwareBuffer(source as Any, destination as Any)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
package/cpp/core/AnyMap.cpp
CHANGED
|
@@ -16,6 +16,14 @@ void AnyMap::remove(const std::string& key) {
|
|
|
16
16
|
void AnyMap::clear() noexcept {
|
|
17
17
|
_map.clear();
|
|
18
18
|
}
|
|
19
|
+
std::vector<std::string> AnyMap::getAllKeys() const {
|
|
20
|
+
std::vector<std::string> keys;
|
|
21
|
+
keys.reserve(_map.size());
|
|
22
|
+
for (const auto& pair : _map) {
|
|
23
|
+
keys.push_back(pair.first);
|
|
24
|
+
}
|
|
25
|
+
return keys;
|
|
26
|
+
}
|
|
19
27
|
|
|
20
28
|
// Is
|
|
21
29
|
bool AnyMap::isNull(const std::string& key) const {
|
package/cpp/core/AnyMap.hpp
CHANGED
package/cpp/core/ArrayBuffer.cpp
CHANGED
|
@@ -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; });
|
package/cpp/core/ArrayBuffer.hpp
CHANGED
|
@@ -63,6 +63,10 @@ public:
|
|
|
63
63
|
* Create a new `NativeArrayBuffer` that copies the given `std::vector`.
|
|
64
64
|
*/
|
|
65
65
|
static std::shared_ptr<ArrayBuffer> copy(const std::vector<uint8_t>& data);
|
|
66
|
+
/**
|
|
67
|
+
* Create a new `NativeArrayBuffer` that copies the given `std::shared_ptr<ArrayBuffer>`.
|
|
68
|
+
*/
|
|
69
|
+
static std::shared_ptr<ArrayBuffer> copy(const std::shared_ptr<ArrayBuffer>& buffer);
|
|
66
70
|
/**
|
|
67
71
|
* Create a new `NativeArrayBuffer` that allocates a new buffer of the given size.
|
|
68
72
|
*/
|
|
@@ -181,8 +181,9 @@ private:
|
|
|
181
181
|
* Get the `NativeState` of the given `value`.
|
|
182
182
|
*/
|
|
183
183
|
template <typename THybrid>
|
|
184
|
-
static inline std::shared_ptr<THybrid> getHybridObjectNativeState(jsi::Runtime& runtime, const jsi::Value& value,
|
|
185
|
-
|
|
184
|
+
static inline std::shared_ptr<THybrid> getHybridObjectNativeState(jsi::Runtime& runtime, const jsi::Value& value,
|
|
185
|
+
[[maybe_unused]] FunctionKind funcKind,
|
|
186
|
+
[[maybe_unused]] const std::string& funcName) {
|
|
186
187
|
// 1. Convert jsi::Value to jsi::Object
|
|
187
188
|
#ifdef NITRO_DEBUG
|
|
188
189
|
if (!value.isObject()) [[unlikely]] {
|
|
@@ -22,7 +22,8 @@ private:
|
|
|
22
22
|
|
|
23
23
|
public:
|
|
24
24
|
template <typename... Args>
|
|
25
|
-
static void log(LogLevel level, const char* tag, const char* format,
|
|
25
|
+
static void log([[maybe_unused]] LogLevel level, [[maybe_unused]] const char* tag, [[maybe_unused]] const char* format,
|
|
26
|
+
[[maybe_unused]] Args... args) {
|
|
26
27
|
#ifdef NITRO_DEBUG
|
|
27
28
|
// 1. Make sure args can be passed to sprintf(..)
|
|
28
29
|
static_assert(all_are_trivially_copyable<Args...>(), "All arguments passed to Logger::log(..) must be trivially copyable! "
|
|
@@ -82,6 +82,19 @@ public final class AnyMapHolder {
|
|
|
82
82
|
cppPart.pointee.clear()
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Get all keys in this map.
|
|
87
|
+
*/
|
|
88
|
+
public func getAllKeys() -> [String] {
|
|
89
|
+
let cppKeys = cppPart.pointee.getAllKeys()
|
|
90
|
+
var keys = [String]()
|
|
91
|
+
keys.reserveCapacity(cppKeys.count)
|
|
92
|
+
for cppKey in cppKeys {
|
|
93
|
+
keys.append(String(cppKey))
|
|
94
|
+
}
|
|
95
|
+
return keys
|
|
96
|
+
}
|
|
97
|
+
|
|
85
98
|
// pragma MARK: Getters
|
|
86
99
|
|
|
87
100
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-modules",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.3",
|
|
4
4
|
"description": "Insanely fast native C++, Swift or Kotlin modules with a statically compiled binding layer to JSI.",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|