react-native-nitro-pose-exercises 1.1.2 → 1.1.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.
@@ -28,4 +28,11 @@ find_package(react-native-vision-camera REQUIRED)
28
28
  target_link_libraries(
29
29
  ${PACKAGE_NAME}
30
30
  react-native-vision-camera::VisionCamera
31
+ )
32
+
33
+ find_library(android-lib android)
34
+
35
+ target_link_libraries(
36
+ ${PACKAGE_NAME}
37
+ ${android-lib}
31
38
  )
@@ -0,0 +1,37 @@
1
+ #include <jni.h>
2
+ #include <android/hardware_buffer_jni.h>
3
+
4
+ extern "C"
5
+ JNIEXPORT jobject JNICALL
6
+ Java_com_margelo_nitro_nitroposeexercises_FrameHelper_hardwareBufferToBitmap(
7
+ JNIEnv *env,
8
+ jclass clazz,
9
+ jlong pointer
10
+ ) {
11
+ AHardwareBuffer *buffer = reinterpret_cast<AHardwareBuffer *>(pointer);
12
+ if (!buffer) return nullptr;
13
+
14
+ // Convert AHardwareBuffer* to Java HardwareBuffer
15
+ jobject hardwareBuffer = AHardwareBuffer_toHardwareBuffer(env, buffer);
16
+ if (!hardwareBuffer) return nullptr;
17
+
18
+ // Call Bitmap.wrapHardwareBuffer(hardwareBuffer, null) to create a hardware-backed Bitmap
19
+ jclass bitmapClass = env->FindClass("android/graphics/Bitmap");
20
+ jmethodID wrapMethod = env->GetStaticMethodID(
21
+ bitmapClass,
22
+ "wrapHardwareBuffer",
23
+ "(Landroid/hardware/HardwareBuffer;Landroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;"
24
+ );
25
+ jobject hwBitmap = env->CallStaticObjectMethod(bitmapClass, wrapMethod, hardwareBuffer, nullptr);
26
+ if (!hwBitmap) return nullptr;
27
+
28
+ // Copy to a software ARGB_8888 Bitmap (ML Kit needs software bitmap)
29
+ jclass configClass = env->FindClass("android/graphics/Bitmap$Config");
30
+ jfieldID argbField = env->GetStaticFieldID(configClass, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
31
+ jobject argbConfig = env->GetStaticObjectField(configClass, argbField);
32
+
33
+ jmethodID copyMethod = env->GetMethodID(bitmapClass, "copy", "(Landroid/graphics/Bitmap$Config;Z)Landroid/graphics/Bitmap;");
34
+ jobject softBitmap = env->CallObjectMethod(hwBitmap, copyMethod, argbConfig, JNI_FALSE);
35
+
36
+ return softBitmap;
37
+ }
@@ -0,0 +1,12 @@
1
+ package com.margelo.nitro.nitroposeexercises
2
+
3
+ import android.graphics.Bitmap
4
+
5
+ object FrameHelper {
6
+ init {
7
+ System.loadLibrary("nitroposeexercises")
8
+ }
9
+
10
+ @JvmStatic
11
+ external fun hardwareBufferToBitmap(pointer: Long): Bitmap?
12
+ }
@@ -20,7 +20,7 @@ import kotlin.math.sqrt
20
20
 
21
21
  @Keep
22
22
  @DoNotStrip
23
- class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
23
+ class NitroPoseExercises : HybridNitroPoseExercisesSpec() {
24
24
 
25
25
  // ─── ML Kit ─────────────────────────────────────────────────
26
26
  private var poseDetector: PoseDetector? = null
@@ -190,34 +190,21 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
190
190
  // Frame Processing (ML Kit — async with cached results)
191
191
  // ═══════════════════════════════════════════════════════════
192
192
 
193
- override fun processFrame(frame: HybridFrameSpec) {
193
+ override fun processFrame(frame: HybridFrameSpec) {
194
194
  if (_status != SessionStatus.ACTIVE && _status != SessionStatus.COUNTDOWN) return
195
195
  if (!isInitialized || poseDetector == null) return
196
196
 
197
- // Frame throttle
198
197
  frameCount++
199
198
  if (frameCount % processEveryNFrames != 0) return
200
199
 
201
200
  try {
202
- // Get raw buffer from VisionCamera frame
203
201
  val nativeBuffer = frame.getNativeBuffer()
204
- val width = frame.getWidth()
205
- val height = frame.getHeight()
206
- val bytesPerRow = frame.getBytesPerRow()
207
-
208
- // Create Bitmap from the native buffer
209
- val buffer = java.nio.ByteBuffer.allocateDirect(height * bytesPerRow)
210
- val pointer = nativeBuffer.pointer
211
- // Copy from native pointer to ByteBuffer
212
- NativeBufferHelper.copyToByteBuffer(pointer, buffer, height * bytesPerRow)
213
-
214
- val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
215
- buffer.rewind()
216
- bitmap.copyPixelsFromBuffer(buffer)
202
+ val bitmap = FrameHelper.hardwareBufferToBitmap(nativeBuffer.pointer) ?: return
217
203
 
218
204
  val inputImage = InputImage.fromBitmap(bitmap, 0)
205
+ val imageWidth = bitmap.width.toDouble()
206
+ val imageHeight = bitmap.height.toDouble()
219
207
 
220
- // ML Kit is async — fire detection, cache result
221
208
  poseDetector!!.process(inputImage)
222
209
  .addOnSuccessListener { pose ->
223
210
  val poseLandmarks = pose.allPoseLandmarks
@@ -230,9 +217,6 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
230
217
 
231
218
  val landmarkArray = Array(34) { Landmark(x = 0.0, y = 0.0, z = 0.0, visibility = 0.0) }
232
219
 
233
- val imageWidth = width.toDouble()
234
- val imageHeight = height.toDouble()
235
-
236
220
  for (poseLandmark in poseLandmarks) {
237
221
  val mediaPipeIndex = mlKitToMediaPipeMap[poseLandmark.landmarkType] ?: continue
238
222
  if (mediaPipeIndex >= 34) continue
@@ -265,7 +249,7 @@ class HybridPoseExercise : HybridNitroPoseExercisesSpec() {
265
249
  bitmap.recycle()
266
250
  }
267
251
 
268
- // Use cached landmarks for exercise logic
252
+ // Use cached landmarks from previous frame
269
253
  val currentLandmarks: Array<Landmark>
270
254
  synchronized(landmarkLock) {
271
255
  currentLandmarks = cachedLandmarks.copyOf()
@@ -1,4 +1,4 @@
1
- // ios/HybridPoseExercise.swift
1
+ // ios/NitroPoseExercises.swift
2
2
 
3
3
  import Foundation
4
4
  import NitroModules
@@ -6,7 +6,7 @@ import VisionCamera
6
6
  import AVFoundation
7
7
  import Vision
8
8
 
9
- class HybridPoseExercise: HybridNitroPoseExercisesSpec {
9
+ class NitroPoseExercises: HybridNitroPoseExercisesSpec {
10
10
 
11
11
  // ─── Vision Framework ───────────────────────────────────────
12
12
  private var isInitialized = false
@@ -199,7 +199,7 @@ class HybridPoseExercise: HybridNitroPoseExercisesSpec {
199
199
  // Fill all slots with zero-visibility first
200
200
  var landmarkArray = [Landmark](repeating: Landmark(x: 0, y: 0, z: 0, visibility: 0), count: 34)
201
201
 
202
- for (jointName, mediaPipeIndex) in HybridPoseExercise.visionToMediaPipeMap {
202
+ for (jointName, mediaPipeIndex) in NitroPoseExercises.visionToMediaPipeMap {
203
203
  guard mediaPipeIndex < 34 else { continue }
204
204
 
205
205
  do {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-pose-exercises",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Real-time on-device exercise tracking for React Native. Rep counting, form validation, and skeleton overlay powered by Apple Vision (iOS) and Google ML Kit (Android) with VisionCamera v5 via Nitro Modules.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",