From 20fe022a3dea6dd666f27677839cbb0dbd142ebf Mon Sep 17 00:00:00 2001 From: Rahul Garg Date: Wed, 30 Sep 2026 13:36:16 +0530 Subject: [PATCH] feat: Add ML Kit zoom suggestions to the Barcode Scanner output (Android) --- docs/content/docs/barcode-scanner.mdx | 25 ++++++ ...cannerOptions+toMLBarcodeScannerOptions.kt | 20 ++++- .../VisionCameraBarcodeScannerOnLoad.cpp | 2 + .../c++/JBarcodeScannerOutputOptions.hpp | 19 ++++- .../android/c++/JFunc_void_double.hpp | 75 ++++++++++++++++++ .../c++/JHybridBarcodeScannerFactorySpec.cpp | 5 +- .../BarcodeScannerOutputOptions.kt | 18 ++++- .../camera/barcodescanner/Func_void_double.kt | 78 +++++++++++++++++++ ...nCameraBarcodeScanner-Swift-Cxx-Bridge.cpp | 8 ++ ...nCameraBarcodeScanner-Swift-Cxx-Bridge.hpp | 52 +++++++++++++ .../HybridBarcodeScannerFactorySpecSwift.hpp | 2 +- .../swift/BarcodeScannerOutputOptions.swift | 46 ++++++++++- .../ios/swift/Func_void_double.swift | 46 +++++++++++ .../c++/BarcodeScannerOutputOptions.hpp | 14 +++- .../src/specs/BarcodeScannerFactory.nitro.ts | 18 +++++ .../src/useBarcodeScannerOutput.ts | 19 ++++- 16 files changed, 430 insertions(+), 17 deletions(-) create mode 100644 packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JFunc_void_double.hpp create mode 100644 packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/barcodescanner/Func_void_double.kt create mode 100644 packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/swift/Func_void_double.swift diff --git a/docs/content/docs/barcode-scanner.mdx b/docs/content/docs/barcode-scanner.mdx index 1287044049..7ab44b72bb 100644 --- a/docs/content/docs/barcode-scanner.mdx +++ b/docs/content/docs/barcode-scanner.mdx @@ -124,6 +124,31 @@ async function scanBarcodeImage() { +### Zoom suggestions (Android) + +When a barcode is detected but too far away to be decoded, ML Kit suggests a zoom ratio. Pass [`onZoomSuggested`](/api/react-native-vision-camera-barcode-scanner/interfaces/BarcodeScannerOutputOptions#onzoomsuggested) to apply it to the Camera's [`zoom`](/api/react-native-vision-camera/interfaces/CameraViewProps#zoom), and [`maxZoomRatio`](/api/react-native-vision-camera-barcode-scanner/interfaces/BarcodeScannerOutputOptions#maxzoomratio) to cap suggestions at the device's maximum zoom: + +```tsx +const device = useCameraDevice('back') +const [zoom, setZoom] = useState(1) +const scannerOutput = useBarcodeScannerOutput({ + barcodeFormats: ['qr-code'], + onBarcodeScanned(barcodes) { + console.log(`Scanned ${barcodes.length} barcodes!`) + }, + onError(error) { + console.error(`Failed to scan barcodes!`, error) + }, + maxZoomRatio: device.maxZoom, + onZoomSuggested(zoomRatio) { + setZoom(zoomRatio) + }, +}) +``` + +> [!NOTE] +> Zoom suggestions are only available on Android - ML Kit on iOS does not provide them. + ### Configuring Barcode Formats The examples above are configured to detect **all** [`BarcodeFormat`](/api/react-native-vision-camera-barcode-scanner/type-aliases/BarcodeFormat)s. diff --git a/packages/react-native-vision-camera-barcode-scanner/android/src/main/java/com/margelo/nitro/camera/barcodescanner/extensions/BarcodeScannerOptions+toMLBarcodeScannerOptions.kt b/packages/react-native-vision-camera-barcode-scanner/android/src/main/java/com/margelo/nitro/camera/barcodescanner/extensions/BarcodeScannerOptions+toMLBarcodeScannerOptions.kt index c5ca96d1d6..1cc44b6b83 100644 --- a/packages/react-native-vision-camera-barcode-scanner/android/src/main/java/com/margelo/nitro/camera/barcodescanner/extensions/BarcodeScannerOptions+toMLBarcodeScannerOptions.kt +++ b/packages/react-native-vision-camera-barcode-scanner/android/src/main/java/com/margelo/nitro/camera/barcodescanner/extensions/BarcodeScannerOptions+toMLBarcodeScannerOptions.kt @@ -1,5 +1,6 @@ package com.margelo.nitro.camera.barcodescanner.extensions +import com.google.mlkit.vision.barcode.ZoomSuggestionOptions import com.margelo.nitro.camera.barcodescanner.BarcodeScannerOptions import com.margelo.nitro.camera.barcodescanner.BarcodeScannerOutputOptions import com.margelo.nitro.camera.barcodescanner.TargetBarcodeFormat @@ -23,8 +24,19 @@ fun BarcodeScannerOptions.toMLBarcodeScannerOptions(): com.google.mlkit.vision.b } fun BarcodeScannerOutputOptions.toMLBarcodeScannerOptions(): com.google.mlkit.vision.barcode.BarcodeScannerOptions { - return com.google.mlkit.vision.barcode.BarcodeScannerOptions - .Builder() - .setBarcodeFormats(this.barcodeFormats) - .build() + val builder = + com.google.mlkit.vision.barcode.BarcodeScannerOptions + .Builder() + .setBarcodeFormats(this.barcodeFormats) + val onZoomSuggested = this.onZoomSuggested + if (onZoomSuggested != null) { + val zoomSuggestionOptions = + ZoomSuggestionOptions.Builder { zoomRatio -> + onZoomSuggested(zoomRatio.toDouble()) + true + } + this.maxZoomRatio?.let { zoomSuggestionOptions.setMaxSupportedZoomRatio(it.toFloat()) } + builder.setZoomSuggestionOptions(zoomSuggestionOptions.build()) + } + return builder.build() } diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/VisionCameraBarcodeScannerOnLoad.cpp b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/VisionCameraBarcodeScannerOnLoad.cpp index 7103bd9610..6f158e0f15 100644 --- a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/VisionCameraBarcodeScannerOnLoad.cpp +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/VisionCameraBarcodeScannerOnLoad.cpp @@ -18,6 +18,7 @@ #include "JHybridBarcodeSpec.hpp" #include "JHybridBarcodeScannerSpec.hpp" #include "JHybridBarcodeScannerFactorySpec.hpp" +#include "JFunc_void_double.hpp" #include "JFunc_void_std__vector_std__shared_ptr_HybridBarcodeSpec__.hpp" #include "JFunc_void_std__exception_ptr.hpp" #include @@ -47,6 +48,7 @@ void registerAllNatives() { margelo::nitro::camera::barcodescanner::JHybridBarcodeSpec::CxxPart::registerNatives(); margelo::nitro::camera::barcodescanner::JHybridBarcodeScannerSpec::CxxPart::registerNatives(); margelo::nitro::camera::barcodescanner::JHybridBarcodeScannerFactorySpec::CxxPart::registerNatives(); + margelo::nitro::camera::barcodescanner::JFunc_void_double_cxx::registerNatives(); margelo::nitro::camera::barcodescanner::JFunc_void_std__vector_std__shared_ptr_HybridBarcodeSpec___cxx::registerNatives(); margelo::nitro::camera::barcodescanner::JFunc_void_std__exception_ptr_cxx::registerNatives(); diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JBarcodeScannerOutputOptions.hpp b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JBarcodeScannerOutputOptions.hpp index 49b210560d..3a2047a1b7 100644 --- a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JBarcodeScannerOutputOptions.hpp +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JBarcodeScannerOutputOptions.hpp @@ -13,6 +13,7 @@ #include "BarcodeScannerOutputResolution.hpp" #include "HybridBarcodeSpec.hpp" #include "JBarcodeScannerOutputResolution.hpp" +#include "JFunc_void_double.hpp" #include "JFunc_void_std__exception_ptr.hpp" #include "JFunc_void_std__vector_std__shared_ptr_HybridBarcodeSpec__.hpp" #include "JHybridBarcodeSpec.hpp" @@ -48,6 +49,10 @@ namespace margelo::nitro::camera::barcodescanner { jni::local_ref> barcodeFormats = this->getFieldValue(fieldBarcodeFormats); static const auto fieldOutputResolution = clazz->getField("outputResolution"); jni::local_ref outputResolution = this->getFieldValue(fieldOutputResolution); + static const auto fieldMaxZoomRatio = clazz->getField("maxZoomRatio"); + jni::local_ref maxZoomRatio = this->getFieldValue(fieldMaxZoomRatio); + static const auto fieldOnZoomSuggested = clazz->getField("onZoomSuggested"); + jni::local_ref onZoomSuggested = this->getFieldValue(fieldOnZoomSuggested); static const auto fieldOnBarcodeScanned = clazz->getField("onBarcodeScanned"); jni::local_ref onBarcodeScanned = this->getFieldValue(fieldOnBarcodeScanned); static const auto fieldOnError = clazz->getField("onError"); @@ -64,6 +69,16 @@ namespace margelo::nitro::camera::barcodescanner { return __vector; }(barcodeFormats), outputResolution != nullptr ? std::make_optional(outputResolution->toCpp()) : std::nullopt, + maxZoomRatio != nullptr ? std::make_optional(maxZoomRatio->value()) : std::nullopt, + onZoomSuggested != nullptr ? std::make_optional([&]() -> std::function { + if (onZoomSuggested->isInstanceOf(JFunc_void_double_cxx::javaClassStatic())) [[likely]] { + auto downcast = jni::static_ref_cast(onZoomSuggested); + return downcast->cthis()->getFunction(); + } else { + auto onZoomSuggestedRef = jni::make_global(onZoomSuggested); + return JNICallable(std::move(onZoomSuggestedRef)); + } + }()) : std::nullopt, [&]() -> std::function>& /* barcodes */)> { if (onBarcodeScanned->isInstanceOf(JFunc_void_std__vector_std__shared_ptr_HybridBarcodeSpec___cxx::javaClassStatic())) [[likely]] { auto downcast = jni::static_ref_cast(onBarcodeScanned); @@ -91,7 +106,7 @@ namespace margelo::nitro::camera::barcodescanner { */ [[maybe_unused]] static jni::local_ref fromCpp(const BarcodeScannerOutputOptions& value) { - using JSignature = JBarcodeScannerOutputOptions(jni::alias_ref>, jni::alias_ref, jni::alias_ref, jni::alias_ref); + using JSignature = JBarcodeScannerOutputOptions(jni::alias_ref>, jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref, jni::alias_ref); static const auto clazz = javaClassStatic(); static const auto create = clazz->getStaticMethod("fromCpp"); return create( @@ -107,6 +122,8 @@ namespace margelo::nitro::camera::barcodescanner { return __array; }(value.barcodeFormats), value.outputResolution.has_value() ? JBarcodeScannerOutputResolution::fromCpp(value.outputResolution.value()) : nullptr, + value.maxZoomRatio.has_value() ? jni::JDouble::valueOf(value.maxZoomRatio.value()) : nullptr, + value.onZoomSuggested.has_value() ? JFunc_void_double_cxx::fromCpp(value.onZoomSuggested.value()) : nullptr, JFunc_void_std__vector_std__shared_ptr_HybridBarcodeSpec___cxx::fromCpp(value.onBarcodeScanned), JFunc_void_std__exception_ptr_cxx::fromCpp(value.onError) ); diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JFunc_void_double.hpp b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JFunc_void_double.hpp new file mode 100644 index 0000000000..5c239cda5c --- /dev/null +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JFunc_void_double.hpp @@ -0,0 +1,75 @@ +/// +/// JFunc_void_double.hpp +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +#pragma once + +#include +#include + +#include +#include + +namespace margelo::nitro::camera::barcodescanner { + + using namespace facebook; + + /** + * Represents the Java/Kotlin callback `(zoomRatio: Double) -> Unit`. + * This can be passed around between C++ and Java/Kotlin. + */ + struct JFunc_void_double: public jni::JavaClass { + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/camera/barcodescanner/Func_void_double;"; + + public: + /** + * Invokes the function this `JFunc_void_double` instance holds through JNI. + */ + void invoke(double zoomRatio) const { + static const auto method = javaClassStatic()->getMethod("invoke"); + method(self(), zoomRatio); + } + }; + + /** + * An implementation of Func_void_double that is backed by a C++ implementation (using `std::function<...>`) + */ + class JFunc_void_double_cxx final: public jni::HybridClass { + public: + static jni::local_ref fromCpp(const std::function& func) { + return JFunc_void_double_cxx::newObjectCxxArgs(func); + } + + public: + /** + * Invokes the C++ `std::function<...>` this `JFunc_void_double_cxx` instance holds. + */ + void invoke_cxx(double zoomRatio) { + _func(zoomRatio); + } + + public: + [[nodiscard]] + inline const std::function& getFunction() const { + return _func; + } + + public: + static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/camera/barcodescanner/Func_void_double_cxx;"; + static void registerNatives() { + registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_double_cxx::invoke_cxx)}); + } + + private: + explicit JFunc_void_double_cxx(const std::function& func): _func(func) { } + + private: + friend HybridBase; + std::function _func; + }; + +} // namespace margelo::nitro::camera::barcodescanner diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JHybridBarcodeScannerFactorySpec.cpp b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JHybridBarcodeScannerFactorySpec.cpp index 3a1983b97c..2437e10277 100644 --- a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JHybridBarcodeScannerFactorySpec.cpp +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/c++/JHybridBarcodeScannerFactorySpec.cpp @@ -37,10 +37,11 @@ namespace margelo::nitro::camera::barcodescanner { class HybridBarcodeSpec; } #include "BarcodeScannerOutputResolution.hpp" #include #include "JBarcodeScannerOutputResolution.hpp" -#include "HybridBarcodeSpec.hpp" #include -#include "JFunc_void_std__vector_std__shared_ptr_HybridBarcodeSpec__.hpp" +#include "JFunc_void_double.hpp" #include +#include "HybridBarcodeSpec.hpp" +#include "JFunc_void_std__vector_std__shared_ptr_HybridBarcodeSpec__.hpp" #include "JHybridBarcodeSpec.hpp" #include #include "JFunc_void_std__exception_ptr.hpp" diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/barcodescanner/BarcodeScannerOutputOptions.kt b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/barcodescanner/BarcodeScannerOutputOptions.kt index eeb811bc6f..8151d35436 100644 --- a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/barcodescanner/BarcodeScannerOutputOptions.kt +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/barcodescanner/BarcodeScannerOutputOptions.kt @@ -26,6 +26,12 @@ data class BarcodeScannerOutputOptions( val outputResolution: BarcodeScannerOutputResolution?, @DoNotStrip @Keep + val maxZoomRatio: Double?, + @DoNotStrip + @Keep + val onZoomSuggested: Func_void_double?, + @DoNotStrip + @Keep val onBarcodeScanned: Func_void_std__vector_std__shared_ptr_HybridBarcodeSpec__, @DoNotStrip @Keep @@ -34,14 +40,16 @@ data class BarcodeScannerOutputOptions( /** * Create a new instance of BarcodeScannerOutputOptions from Kotlin */ - constructor(barcodeFormats: Array, outputResolution: BarcodeScannerOutputResolution?, onBarcodeScanned: (barcodes: Array) -> Unit, onError: (error: Throwable) -> Unit): - this(barcodeFormats, outputResolution, Func_void_std__vector_std__shared_ptr_HybridBarcodeSpec___java(onBarcodeScanned), Func_void_std__exception_ptr_java(onError)) + constructor(barcodeFormats: Array, outputResolution: BarcodeScannerOutputResolution?, maxZoomRatio: Double?, onZoomSuggested: ((zoomRatio: Double) -> Unit)?, onBarcodeScanned: (barcodes: Array) -> Unit, onError: (error: Throwable) -> Unit): + this(barcodeFormats, outputResolution, maxZoomRatio, onZoomSuggested?.let { Func_void_double_java(it) }, Func_void_std__vector_std__shared_ptr_HybridBarcodeSpec___java(onBarcodeScanned), Func_void_std__exception_ptr_java(onError)) override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is BarcodeScannerOutputOptions) return false return Objects.deepEquals(this.barcodeFormats, other.barcodeFormats) && Objects.deepEquals(this.outputResolution, other.outputResolution) + && Objects.deepEquals(this.maxZoomRatio, other.maxZoomRatio) + && Objects.deepEquals(this.onZoomSuggested, other.onZoomSuggested) && Objects.deepEquals(this.onBarcodeScanned, other.onBarcodeScanned) && Objects.deepEquals(this.onError, other.onError) } @@ -50,6 +58,8 @@ data class BarcodeScannerOutputOptions( return arrayOf( barcodeFormats, outputResolution, + maxZoomRatio, + onZoomSuggested, onBarcodeScanned, onError ).contentDeepHashCode() @@ -63,8 +73,8 @@ data class BarcodeScannerOutputOptions( @Keep @Suppress("unused") @JvmStatic - private fun fromCpp(barcodeFormats: Array, outputResolution: BarcodeScannerOutputResolution?, onBarcodeScanned: Func_void_std__vector_std__shared_ptr_HybridBarcodeSpec__, onError: Func_void_std__exception_ptr): BarcodeScannerOutputOptions { - return BarcodeScannerOutputOptions(barcodeFormats, outputResolution, onBarcodeScanned, onError) + private fun fromCpp(barcodeFormats: Array, outputResolution: BarcodeScannerOutputResolution?, maxZoomRatio: Double?, onZoomSuggested: Func_void_double?, onBarcodeScanned: Func_void_std__vector_std__shared_ptr_HybridBarcodeSpec__, onError: Func_void_std__exception_ptr): BarcodeScannerOutputOptions { + return BarcodeScannerOutputOptions(barcodeFormats, outputResolution, maxZoomRatio, onZoomSuggested, onBarcodeScanned, onError) } } } diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/barcodescanner/Func_void_double.kt b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/barcodescanner/Func_void_double.kt new file mode 100644 index 0000000000..64951a6624 --- /dev/null +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/android/kotlin/com/margelo/nitro/camera/barcodescanner/Func_void_double.kt @@ -0,0 +1,78 @@ +/// +/// Func_void_double.kt +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +package com.margelo.nitro.camera.barcodescanner + +import androidx.annotation.Keep +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip + + +/** + * Represents the JavaScript callback `(zoomRatio: number) => void`. + * This can be either implemented in C++ (in which case it might be a callback coming from JS), + * or in Kotlin/Java (in which case it is a native callback). + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType") +fun interface Func_void_double: (Double) -> Unit { + /** + * Call the given JS callback. + * @throws Throwable if the JS function itself throws an error, or if the JS function/runtime has already been deleted. + */ + @DoNotStrip + @Keep + override fun invoke(zoomRatio: Double): Unit +} + +/** + * Represents the JavaScript callback `(zoomRatio: number) => void`. + * This is implemented in C++, via a `std::function<...>`. + * The callback might be coming from JS. + */ +@DoNotStrip +@Keep +@Suppress( + "KotlinJniMissingFunction", "unused", + "RedundantSuppression", "RedundantUnitReturnType", "FunctionName", + "ConvertSecondaryConstructorToPrimary", "ClassName", "LocalVariableName", +) +class Func_void_double_cxx: Func_void_double { + @DoNotStrip + @Keep + private val mHybridData: HybridData + + @DoNotStrip + @Keep + private constructor(hybridData: HybridData) { + mHybridData = hybridData + } + + @DoNotStrip + @Keep + override fun invoke(zoomRatio: Double): Unit + = invoke_cxx(zoomRatio) + + private external fun invoke_cxx(zoomRatio: Double): Unit +} + +/** + * Represents the JavaScript callback `(zoomRatio: number) => void`. + * This is implemented in Java/Kotlin, via a `(Double) -> Unit`. + * The callback is always coming from native. + */ +@DoNotStrip +@Keep +@Suppress("ClassName", "RedundantUnitReturnType", "unused") +class Func_void_double_java(private val function: (Double) -> Unit): Func_void_double { + @DoNotStrip + @Keep + override fun invoke(zoomRatio: Double): Unit { + return this.function(zoomRatio) + } +} diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/VisionCameraBarcodeScanner-Swift-Cxx-Bridge.cpp b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/VisionCameraBarcodeScanner-Swift-Cxx-Bridge.cpp index dab18e97a2..ed1fa0fd59 100644 --- a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/VisionCameraBarcodeScanner-Swift-Cxx-Bridge.cpp +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/VisionCameraBarcodeScanner-Swift-Cxx-Bridge.cpp @@ -96,6 +96,14 @@ namespace margelo::nitro::camera::barcodescanner::bridge::swift { return margelo::nitro::camera::bridge::swift::get_std__shared_ptr_HybridCameraOutputSpec_(cppType); } + // pragma MARK: std::function + Func_void_double create_Func_void_double(void* NON_NULL swiftClosureWrapper) noexcept { + auto swiftClosure = VisionCameraBarcodeScanner::Func_void_double::fromUnsafe(swiftClosureWrapper); + return [swiftClosure = std::move(swiftClosure)](double zoomRatio) mutable -> void { + swiftClosure.call(zoomRatio); + }; + } + // pragma MARK: std::shared_ptr std::shared_ptr create_std__shared_ptr_HybridBarcodeScannerFactorySpec_(void* NON_NULL swiftUnsafePointer) noexcept { VisionCameraBarcodeScanner::HybridBarcodeScannerFactorySpec_cxx swiftPart = VisionCameraBarcodeScanner::HybridBarcodeScannerFactorySpec_cxx::fromUnsafe(swiftUnsafePointer); diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/VisionCameraBarcodeScanner-Swift-Cxx-Bridge.hpp b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/VisionCameraBarcodeScanner-Swift-Cxx-Bridge.hpp index 6e5b168c8e..4f6e15a79d 100644 --- a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/VisionCameraBarcodeScanner-Swift-Cxx-Bridge.hpp +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/VisionCameraBarcodeScanner-Swift-Cxx-Bridge.hpp @@ -283,6 +283,58 @@ namespace margelo::nitro::camera::barcodescanner::bridge::swift { return optional.value(); } + // pragma MARK: std::optional + /** + * Specialized version of `std::optional`. + */ + using std__optional_double_ = std::optional; + inline std::optional create_std__optional_double_(const double& value) noexcept { + return std::optional(value); + } + inline bool has_value_std__optional_double_(const std::optional& optional) noexcept { + return optional.has_value(); + } + inline double get_std__optional_double_(const std::optional& optional) noexcept { + return optional.value(); + } + + // pragma MARK: std::function + /** + * Specialized version of `std::function`. + */ + using Func_void_double = std::function; + /** + * Wrapper class for a `std::function`, this can be used from Swift. + */ + class Func_void_double_Wrapper final { + public: + explicit Func_void_double_Wrapper(std::function&& func): _function(std::make_unique>(std::move(func))) {} + inline void call(double zoomRatio) const noexcept { + _function->operator()(zoomRatio); + } + private: + std::unique_ptr> _function; + } SWIFT_NONCOPYABLE; + Func_void_double create_Func_void_double(void* NON_NULL swiftClosureWrapper) noexcept; + inline Func_void_double_Wrapper wrap_Func_void_double(Func_void_double value) noexcept { + return Func_void_double_Wrapper(std::move(value)); + } + + // pragma MARK: std::optional> + /** + * Specialized version of `std::optional>`. + */ + using std__optional_std__function_void_double____zoomRatio______ = std::optional>; + inline std::optional> create_std__optional_std__function_void_double____zoomRatio______(const std::function& value) noexcept { + return std::optional>(value); + } + inline bool has_value_std__optional_std__function_void_double____zoomRatio______(const std::optional>& optional) noexcept { + return optional.has_value(); + } + inline std::function get_std__optional_std__function_void_double____zoomRatio______(const std::optional>& optional) noexcept { + return optional.value(); + } + // pragma MARK: std::shared_ptr /** * Specialized version of `std::shared_ptr`. diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/c++/HybridBarcodeScannerFactorySpecSwift.hpp b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/c++/HybridBarcodeScannerFactorySpecSwift.hpp index a1b4397f2e..5722cfaeb7 100644 --- a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/c++/HybridBarcodeScannerFactorySpecSwift.hpp +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/c++/HybridBarcodeScannerFactorySpecSwift.hpp @@ -36,8 +36,8 @@ namespace margelo::nitro::camera::barcodescanner { class HybridBarcodeSpec; } #include "BarcodeScannerOutputOptions.hpp" #include "BarcodeScannerOutputResolution.hpp" #include -#include "HybridBarcodeSpec.hpp" #include +#include "HybridBarcodeSpec.hpp" #include #include "VisionCameraBarcodeScanner-Swift-Cxx-Umbrella.hpp" diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/swift/BarcodeScannerOutputOptions.swift b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/swift/BarcodeScannerOutputOptions.swift index d340323790..9b3ea727e0 100644 --- a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/swift/BarcodeScannerOutputOptions.swift +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/swift/BarcodeScannerOutputOptions.swift @@ -18,7 +18,7 @@ public extension BarcodeScannerOutputOptions { /** * Create a new instance of `BarcodeScannerOutputOptions`. */ - init(barcodeFormats: [TargetBarcodeFormat], outputResolution: BarcodeScannerOutputResolution?, onBarcodeScanned: @escaping (_ barcodes: [(any HybridBarcodeSpec)]) -> Void, onError: @escaping (_ error: Error) -> Void) { + init(barcodeFormats: [TargetBarcodeFormat], outputResolution: BarcodeScannerOutputResolution?, maxZoomRatio: Double?, onZoomSuggested: ((_ zoomRatio: Double) -> Void)?, onBarcodeScanned: @escaping (_ barcodes: [(any HybridBarcodeSpec)]) -> Void, onError: @escaping (_ error: Error) -> Void) { self.init({ () -> bridge.std__vector_TargetBarcodeFormat_ in var __vector = bridge.create_std__vector_TargetBarcodeFormat_(barcodeFormats.count) for __item in barcodeFormats { @@ -31,6 +31,21 @@ public extension BarcodeScannerOutputOptions { } else { return .init() } + }(), { () -> bridge.std__optional_double_ in + if let __unwrappedValue = maxZoomRatio { + return bridge.create_std__optional_double_(__unwrappedValue) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__function_void_double____zoomRatio______ in + if let __unwrappedValue = onZoomSuggested { + return bridge.create_std__optional_std__function_void_double____zoomRatio______({ () -> bridge.Func_void_double in + let __closureWrapper = Func_void_double(__unwrappedValue) + return bridge.create_Func_void_double(__closureWrapper.toUnsafe()) + }()) + } else { + return .init() + } }(), { () -> bridge.Func_void_std__vector_std__shared_ptr_HybridBarcodeSpec__ in let __closureWrapper = Func_void_std__vector_std__shared_ptr_HybridBarcodeSpec__(onBarcodeScanned) return bridge.create_Func_void_std__vector_std__shared_ptr_HybridBarcodeSpec__(__closureWrapper.toUnsafe()) @@ -50,6 +65,35 @@ public extension BarcodeScannerOutputOptions { return self.__outputResolution.value } + @inline(__always) + var maxZoomRatio: Double? { + return { () -> Double? in + if bridge.has_value_std__optional_double_(self.__maxZoomRatio) { + let __unwrapped = bridge.get_std__optional_double_(self.__maxZoomRatio) + return __unwrapped + } else { + return nil + } + }() + } + + @inline(__always) + var onZoomSuggested: ((_ zoomRatio: Double) -> Void)? { + return { () -> ((_ zoomRatio: Double) -> Void)? in + if bridge.has_value_std__optional_std__function_void_double____zoomRatio______(self.__onZoomSuggested) { + let __unwrapped = bridge.get_std__optional_std__function_void_double____zoomRatio______(self.__onZoomSuggested) + return { () -> (Double) -> Void in + let __wrappedFunction = bridge.wrap_Func_void_double(__unwrapped) + return { (__zoomRatio: Double) -> Void in + __wrappedFunction.call(__zoomRatio) + } + }() + } else { + return nil + } + }() + } + @inline(__always) var onBarcodeScanned: (_ barcodes: [(any HybridBarcodeSpec)]) -> Void { return { () -> ([(any HybridBarcodeSpec)]) -> Void in diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/swift/Func_void_double.swift b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/swift/Func_void_double.swift new file mode 100644 index 0000000000..878291bcd2 --- /dev/null +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/ios/swift/Func_void_double.swift @@ -0,0 +1,46 @@ +/// +/// Func_void_double.swift +/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. +/// https://github.com/mrousavy/nitro +/// Copyright © Marc Rousavy @ Margelo +/// + +import NitroModules + +/** + * Wraps a Swift `(_ zoomRatio: Double) -> Void` as a class. + * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`. + */ +public final class Func_void_double { + public typealias bridge = margelo.nitro.camera.barcodescanner.bridge.swift + + private let closure: (_ zoomRatio: Double) -> Void + + public init(_ closure: @escaping (_ zoomRatio: Double) -> Void) { + self.closure = closure + } + + @inline(__always) + public func call(zoomRatio: Double) -> Void { + self.closure(zoomRatio) + } + + /** + * Casts this instance to a retained unsafe raw pointer. + * This acquires one additional strong reference on the object! + */ + @inline(__always) + public func toUnsafe() -> UnsafeMutableRawPointer { + return Unmanaged.passRetained(self).toOpaque() + } + + /** + * Casts an unsafe pointer to a `Func_void_double`. + * The pointer has to be a retained opaque `Unmanaged`. + * This removes one strong reference from the object! + */ + @inline(__always) + public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_double { + return Unmanaged.fromOpaque(pointer).takeRetainedValue() + } +} diff --git a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/shared/c++/BarcodeScannerOutputOptions.hpp b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/shared/c++/BarcodeScannerOutputOptions.hpp index b0604e0db4..fe7ec55e83 100644 --- a/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/shared/c++/BarcodeScannerOutputOptions.hpp +++ b/packages/react-native-vision-camera-barcode-scanner/nitrogen/generated/shared/c++/BarcodeScannerOutputOptions.hpp @@ -39,9 +39,9 @@ namespace margelo::nitro::camera::barcodescanner { class HybridBarcodeSpec; } #include #include "BarcodeScannerOutputResolution.hpp" #include +#include #include #include "HybridBarcodeSpec.hpp" -#include #include namespace margelo::nitro::camera::barcodescanner { @@ -53,15 +53,17 @@ namespace margelo::nitro::camera::barcodescanner { public: std::vector barcodeFormats SWIFT_PRIVATE; std::optional outputResolution SWIFT_PRIVATE; + std::optional maxZoomRatio SWIFT_PRIVATE; + std::optional> onZoomSuggested SWIFT_PRIVATE; std::function>& /* barcodes */)> onBarcodeScanned SWIFT_PRIVATE; std::function onError SWIFT_PRIVATE; public: BarcodeScannerOutputOptions() = default; - explicit BarcodeScannerOutputOptions(std::vector barcodeFormats, std::optional outputResolution, std::function>& /* barcodes */)> onBarcodeScanned, std::function onError): barcodeFormats(barcodeFormats), outputResolution(outputResolution), onBarcodeScanned(onBarcodeScanned), onError(onError) {} + explicit BarcodeScannerOutputOptions(std::vector barcodeFormats, std::optional outputResolution, std::optional maxZoomRatio, std::optional> onZoomSuggested, std::function>& /* barcodes */)> onBarcodeScanned, std::function onError): barcodeFormats(barcodeFormats), outputResolution(outputResolution), maxZoomRatio(maxZoomRatio), onZoomSuggested(onZoomSuggested), onBarcodeScanned(onBarcodeScanned), onError(onError) {} public: - // BarcodeScannerOutputOptions is not equatable because these properties are not equatable: onBarcodeScanned, onError + // BarcodeScannerOutputOptions is not equatable because these properties are not equatable: onZoomSuggested, onBarcodeScanned, onError }; } // namespace margelo::nitro::camera::barcodescanner @@ -76,6 +78,8 @@ namespace margelo::nitro { return margelo::nitro::camera::barcodescanner::BarcodeScannerOutputOptions( JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "barcodeFormats"))), JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "outputResolution"))), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "maxZoomRatio"))), + JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onZoomSuggested"))), JSIConverter>&)>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onBarcodeScanned"))), JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onError"))) ); @@ -84,6 +88,8 @@ namespace margelo::nitro { jsi::Object obj(runtime); obj.setProperty(runtime, PropNameIDCache::get(runtime, "barcodeFormats"), JSIConverter>::toJSI(runtime, arg.barcodeFormats)); obj.setProperty(runtime, PropNameIDCache::get(runtime, "outputResolution"), JSIConverter>::toJSI(runtime, arg.outputResolution)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "maxZoomRatio"), JSIConverter>::toJSI(runtime, arg.maxZoomRatio)); + obj.setProperty(runtime, PropNameIDCache::get(runtime, "onZoomSuggested"), JSIConverter>>::toJSI(runtime, arg.onZoomSuggested)); obj.setProperty(runtime, PropNameIDCache::get(runtime, "onBarcodeScanned"), JSIConverter>&)>>::toJSI(runtime, arg.onBarcodeScanned)); obj.setProperty(runtime, PropNameIDCache::get(runtime, "onError"), JSIConverter>::toJSI(runtime, arg.onError)); return obj; @@ -98,6 +104,8 @@ namespace margelo::nitro { } if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "barcodeFormats")))) return false; if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "outputResolution")))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "maxZoomRatio")))) return false; + if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onZoomSuggested")))) return false; if (!JSIConverter>&)>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onBarcodeScanned")))) return false; if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onError")))) return false; return true; diff --git a/packages/react-native-vision-camera-barcode-scanner/src/specs/BarcodeScannerFactory.nitro.ts b/packages/react-native-vision-camera-barcode-scanner/src/specs/BarcodeScannerFactory.nitro.ts index 00e2519ce8..cc3a402913 100644 --- a/packages/react-native-vision-camera-barcode-scanner/src/specs/BarcodeScannerFactory.nitro.ts +++ b/packages/react-native-vision-camera-barcode-scanner/src/specs/BarcodeScannerFactory.nitro.ts @@ -34,6 +34,24 @@ export interface BarcodeScannerOutputOptions { * @default 'preview' */ outputResolution?: BarcodeScannerOutputResolution + /** + * The maximum zoom ratio of the Camera this output is attached to. + * Zoom suggestions from {@linkcode onZoomSuggested} never exceed it. + * If omitted, ML Kit may suggest an unbounded ratio. + * + * @platform Android + */ + maxZoomRatio?: number + /** + * Called when a barcode has been detected but is too far away + * to be decoded. Apply the suggested `zoomRatio` to the Camera's + * `zoom` to make it decodable. + * + * @platform Android + * @note iOS does not support zoom suggestions, as MLKit on iOS + * does not provide them. + */ + onZoomSuggested?: (zoomRatio: number) => void /** * Called whenever barcodes have been detected. */ diff --git a/packages/react-native-vision-camera-barcode-scanner/src/useBarcodeScannerOutput.ts b/packages/react-native-vision-camera-barcode-scanner/src/useBarcodeScannerOutput.ts index f822c1be6a..50270c754b 100644 --- a/packages/react-native-vision-camera-barcode-scanner/src/useBarcodeScannerOutput.ts +++ b/packages/react-native-vision-camera-barcode-scanner/src/useBarcodeScannerOutput.ts @@ -25,6 +25,11 @@ import type { BarcodeScannerOutputOptions } from './specs/BarcodeScannerFactory. * }, * onError(error) { * console.error(`Failed to scan barcodes!`, error) + * }, + * maxZoomRatio: device.maxZoom, + * onZoomSuggested(zoomRatio) { + * // Android only - apply to the Camera's `zoom` + * setZoom(zoomRatio) * } * }) * @@ -59,8 +64,10 @@ import type { BarcodeScannerOutputOptions } from './specs/BarcodeScannerFactory. export function useBarcodeScannerOutput({ barcodeFormats, outputResolution = 'preview', + maxZoomRatio, onBarcodeScanned, onError, + onZoomSuggested, }: BarcodeScannerOutputOptions): CameraOutput { const stableOnBarcodeScanned = useRef(onBarcodeScanned) stableOnBarcodeScanned.current = onBarcodeScanned @@ -68,18 +75,28 @@ export function useBarcodeScannerOutput({ const stableOnError = useRef(onError) stableOnError.current = onError + const stableOnZoomSuggested = useRef(onZoomSuggested) + stableOnZoomSuggested.current = onZoomSuggested + const hasZoomSuggestions = onZoomSuggested != null + return useMemo( () => createBarcodeScannerOutput({ barcodeFormats: barcodeFormats, outputResolution: outputResolution, + maxZoomRatio: maxZoomRatio, onBarcodeScanned(barcodes) { stableOnBarcodeScanned.current(barcodes) }, onError(error) { stableOnError.current(error) }, + onZoomSuggested: hasZoomSuggestions + ? (zoomRatio) => { + stableOnZoomSuggested.current?.(zoomRatio) + } + : undefined, }), - [barcodeFormats, outputResolution], + [barcodeFormats, outputResolution, maxZoomRatio, hasZoomSuggestions], ) }