Navaron Bracke
Committed by GitHub

Merge pull request #1177 from navaronbracke/macos_extras

fix: support formats for analyze image
## NEXT
* [MacOS] Added the corners and size information to barcode results.
* [MacOS] Added support for `analyzeImage`.
* [web] Added the size information to barcode results.
* Added support for barcode formats to image analysis.
## 5.2.3
Deprecations:
... ...
... ... @@ -35,8 +35,8 @@ See the example app for detailed implementation information.
| Features | Android | iOS | macOS | Web |
|------------------------|--------------------|--------------------|----------------------|-----|
| analyzeImage (Gallery) | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: |
| returnImage | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: |
| analyzeImage (Gallery) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| returnImage | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| scanWindow | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
## Platform Support
... ... @@ -83,8 +83,8 @@ Ensure that you granted camera permission in XCode -> Signing & Capabilities:
## Web
As of version 5.0.0 adding the library to the `index.html` is no longer required,
as the library is automatically loaded on first use.
As of version 5.0.0 adding the barcode scanning library script to the `index.html` is no longer required,
as the script is automatically loaded on first use.
### Providing a mirror for the barcode scanning library
... ...
... ... @@ -67,7 +67,7 @@ dependencies {
def useUnbundled = project.findProperty('dev.steenbakker.mobile_scanner.useUnbundled') ?: false
if (useUnbundled.toBoolean()) {
// Dynamically downloaded model via Google Play Services
implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.0'
implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1'
} else {
// Bundled model in app
implementation 'com.google.mlkit:barcode-scanning:17.2.0'
... ... @@ -77,8 +77,8 @@ dependencies {
// See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22"))
implementation 'androidx.camera:camera-lifecycle:1.3.3'
implementation 'androidx.camera:camera-camera2:1.3.3'
implementation 'androidx.camera:camera-lifecycle:1.3.4'
implementation 'androidx.camera:camera-camera2:1.3.4'
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.12.0'
... ...
... ... @@ -151,28 +151,16 @@ class MobileScannerHandler(
null
}
var barcodeScannerOptions: BarcodeScannerOptions? = null
if (formats != null) {
val formatsList: MutableList<Int> = mutableListOf()
for (formatValue in formats) {
formatsList.add(BarcodeFormats.fromRawValue(formatValue).intValue)
}
barcodeScannerOptions = if (formatsList.size == 1) {
BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first())
.build()
} else {
BarcodeScannerOptions.Builder().setBarcodeFormats(
formatsList.first(),
*formatsList.subList(1, formatsList.size).toIntArray()
).build()
}
}
val barcodeScannerOptions: BarcodeScannerOptions? = buildBarcodeScannerOptions(formats)
val position =
if (facing == 0) CameraSelector.DEFAULT_FRONT_CAMERA else CameraSelector.DEFAULT_BACK_CAMERA
val detectionSpeed: DetectionSpeed = if (speed == 0) DetectionSpeed.NO_DUPLICATES
else if (speed ==1) DetectionSpeed.NORMAL else DetectionSpeed.UNRESTRICTED
val detectionSpeed: DetectionSpeed = when (speed) {
0 -> DetectionSpeed.NO_DUPLICATES
1 -> DetectionSpeed.NORMAL
else -> DetectionSpeed.UNRESTRICTED
}
mobileScanner!!.start(
barcodeScannerOptions,
... ... @@ -243,13 +231,13 @@ class MobileScannerHandler(
private fun analyzeImage(call: MethodCall, result: MethodChannel.Result) {
analyzerResult = result
val uri = Uri.fromFile(File(call.arguments.toString()))
// TODO: parse options from the method call
// See https://github.com/juliansteenbakker/mobile_scanner/issues/1069
val formats: List<Int>? = call.argument<List<Int>>("formats")
val filePath: String = call.argument<String>("filePath")!!
mobileScanner!!.analyzeImage(
uri,
null,
Uri.fromFile(File(filePath)),
buildBarcodeScannerOptions(formats),
analyzeImageSuccessCallback,
analyzeImageErrorCallback)
}
... ... @@ -284,4 +272,26 @@ class MobileScannerHandler(
result.success(null)
}
private fun buildBarcodeScannerOptions(formats: List<Int>?): BarcodeScannerOptions? {
if (formats == null) {
return null
}
val formatsList: MutableList<Int> = mutableListOf()
for (formatValue in formats) {
formatsList.add(BarcodeFormats.fromRawValue(formatValue).intValue)
}
if (formatsList.size == 1) {
return BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first())
.build()
}
return BarcodeScannerOptions.Builder().setBarcodeFormats(
formatsList.first(),
*formatsList.subList(1, formatsList.size).toIntArray()
).build()
}
}
... ...
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
class BarcodeScannerAnalyzeImage extends StatefulWidget {
const BarcodeScannerAnalyzeImage({super.key});
@override
State<BarcodeScannerAnalyzeImage> createState() =>
_BarcodeScannerAnalyzeImageState();
}
class _BarcodeScannerAnalyzeImageState
extends State<BarcodeScannerAnalyzeImage> {
final MobileScannerController _controller = MobileScannerController();
BarcodeCapture? _barcodeCapture;
Future<void> _analyzeImageFromFile() async {
try {
final XFile? file =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (!mounted || file == null) {
return;
}
final BarcodeCapture? barcodeCapture =
await _controller.analyzeImage(file.path);
if (mounted) {
setState(() {
_barcodeCapture = barcodeCapture;
});
}
} catch (_) {}
}
@override
Widget build(BuildContext context) {
Widget label = const Text('Pick a file to detect barcode');
if (_barcodeCapture != null) {
label = Text(
_barcodeCapture?.barcodes.firstOrNull?.displayValue ??
'No barcode detected',
);
}
return Scaffold(
appBar: AppBar(title: const Text('Analyze image from file')),
body: Column(
children: [
Expanded(
child: Center(
child: ElevatedButton(
onPressed: kIsWeb ? null : _analyzeImageFromFile,
child: kIsWeb
? const Text('Analyze image is not supported on web')
: const Text('Choose file'),
),
),
),
Expanded(child: Center(child: label)),
],
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
... ...
... ... @@ -39,16 +39,16 @@ class _BarcodeScannerWithScanWindowState
final scannedBarcode = barcodeCapture.barcodes.first;
// No barcode corners, or size, or no camera preview size.
if (scannedBarcode.corners.isEmpty ||
value.size.isEmpty ||
barcodeCapture.size.isEmpty) {
if (value.size.isEmpty ||
scannedBarcode.size.isEmpty ||
scannedBarcode.corners.isEmpty) {
return const SizedBox();
}
return CustomPaint(
painter: BarcodeOverlay(
barcodeCorners: scannedBarcode.corners,
barcodeSize: barcodeCapture.size,
barcodeSize: scannedBarcode.size,
boxFit: BoxFit.contain,
cameraPreviewSize: value.size,
),
... ...
import 'package:flutter/material.dart';
import 'package:mobile_scanner_example/barcode_scanner_analyze_image.dart';
import 'package:mobile_scanner_example/barcode_scanner_controller.dart';
import 'package:mobile_scanner_example/barcode_scanner_listview.dart';
import 'package:mobile_scanner_example/barcode_scanner_pageview.dart';
... ... @@ -20,95 +21,75 @@ void main() {
class MyHome extends StatelessWidget {
const MyHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Mobile Scanner Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerSimple(),
),
);
},
child: const Text('MobileScanner Simple'),
),
ElevatedButton(
Widget _buildItem(BuildContext context, String label, Widget page) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerListView(),
builder: (context) => page,
),
);
},
child: const Text('MobileScanner with ListView'),
child: Text(label),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerWithController(),
),
);
},
child: const Text('MobileScanner with Controller'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerWithScanWindow(),
),
);
},
child: const Text('MobileScanner with ScanWindow'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerReturningImage(),
),
);
},
child: const Text(
'MobileScanner with Controller (returning image)',
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerWithZoom(),
),
);
},
child: const Text('MobileScanner with zoom slider'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerPageView(),
),
);
},
child: const Text('MobileScanner pageView'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BarcodeScannerWithOverlay(),
),
);
},
child: const Text('MobileScanner with Overlay'),
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Mobile Scanner Example')),
body: Center(
child: ListView(
children: [
_buildItem(
context,
'MobileScanner Simple',
const BarcodeScannerSimple(),
),
_buildItem(
context,
'MobileScanner with ListView',
const BarcodeScannerListView(),
),
_buildItem(
context,
'MobileScanner with Controller',
const BarcodeScannerWithController(),
),
_buildItem(
context,
'MobileScanner with ScanWindow',
const BarcodeScannerWithScanWindow(),
),
_buildItem(
context,
'MobileScanner with Controller (return image)',
const BarcodeScannerReturningImage(),
),
_buildItem(
context,
'MobileScanner with zoom slider',
const BarcodeScannerWithZoom(),
),
_buildItem(
context,
'MobileScanner with PageView',
const BarcodeScannerPageView(),
),
_buildItem(
context,
'MobileScanner with Overlay',
const BarcodeScannerWithOverlay(),
),
_buildItem(
context,
'Analyze image from file',
const BarcodeScannerAnalyzeImage(),
),
],
),
... ...
... ... @@ -5,6 +5,8 @@ import 'package:mobile_scanner_example/scanner_button_widgets.dart';
import 'package:mobile_scanner_example/scanner_error_widget.dart';
class BarcodeScannerWithOverlay extends StatefulWidget {
const BarcodeScannerWithOverlay({super.key});
@override
_BarcodeScannerWithOverlayState createState() =>
_BarcodeScannerWithOverlayState();
... ...
... ... @@ -22,8 +22,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
/// The selected camera
var device: AVCaptureDevice!
/// Barcode scanner for results
var scanner = BarcodeScanner.barcodeScanner()
/// The long lived barcode scanner for scanning barcodes from a camera preview.
var scanner: BarcodeScanner? = nil
/// Default position of camera
var videoPosition: AVCaptureDevice.Position = AVCaptureDevice.Position.back
... ... @@ -146,7 +146,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
position: videoPosition
)
scanner.process(image) { [self] barcodes, error in
scanner?.process(image) { [self] barcodes, error in
imagesCurrentlyBeingProcessed = false
if (detectionSpeed == DetectionSpeed.noDuplicates) {
... ... @@ -314,6 +314,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
textureId = nil
captureSession = nil
device = nil
scanner = nil
}
/// Toggle the torch.
... ... @@ -431,7 +432,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
}
/// Analyze a single image
func analyzeImage(image: UIImage, position: AVCaptureDevice.Position, callback: @escaping BarcodeScanningCallback) {
func analyzeImage(image: UIImage, position: AVCaptureDevice.Position,
barcodeScannerOptions: BarcodeScannerOptions?, callback: @escaping BarcodeScanningCallback) {
let image = VisionImage(image: image)
image.orientation = imageOrientation(
deviceOrientation: UIDevice.current.orientation,
... ... @@ -439,6 +441,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
position: position
)
let scanner: BarcodeScanner = barcodeScannerOptions != nil ? BarcodeScanner.barcodeScanner(options: barcodeScannerOptions!) : BarcodeScanner.barcodeScanner()
scanner.process(image, completion: callback)
}
... ...
... ... @@ -134,16 +134,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
self.mobileScanner.timeoutSeconds = Double(timeoutMs) / Double(1000)
MobileScannerPlugin.returnImage = returnImage
let formatList = formats.map { format in return BarcodeFormat(rawValue: format)}
var barcodeOptions: BarcodeScannerOptions? = nil
if (formatList.count != 0) {
var barcodeFormats: BarcodeFormat = []
for index in formats {
barcodeFormats.insert(BarcodeFormat(rawValue: index))
}
barcodeOptions = BarcodeScannerOptions(formats: barcodeFormats)
}
let barcodeOptions: BarcodeScannerOptions? = buildBarcodeScannerOptions(formats)
let position = facing == 0 ? AVCaptureDevice.Position.front : .back
let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)!
... ... @@ -262,7 +253,9 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
/// Analyzes a single image.
private func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
let uiImage = UIImage(contentsOfFile: call.arguments as? String ?? "")
let formats: Array<Int> = (call.arguments as! Dictionary<String, Any?>)["formats"] as? Array ?? []
let scannerOptions: BarcodeScannerOptions? = buildBarcodeScannerOptions(formats)
let uiImage = UIImage(contentsOfFile: (call.arguments as! Dictionary<String, Any?>)["filePath"] as? String ?? "")
if (uiImage == nil) {
result(FlutterError(code: "MobileScanner",
... ... @@ -271,7 +264,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
return
}
mobileScanner.analyzeImage(image: uiImage!, position: AVCaptureDevice.Position.back, callback: { barcodes, error in
mobileScanner.analyzeImage(image: uiImage!, position: AVCaptureDevice.Position.back,
barcodeScannerOptions: scannerOptions, callback: { barcodes, error in
if error != nil {
DispatchQueue.main.async {
result(FlutterError(code: "MobileScanner",
... ... @@ -297,4 +291,18 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
}
})
}
private func buildBarcodeScannerOptions(_ formats: [Int]) -> BarcodeScannerOptions? {
guard !formats.isEmpty else {
return nil
}
var barcodeFormats: BarcodeFormat = []
for format in formats {
barcodeFormats.insert(BarcodeFormat(rawValue: format))
}
return BarcodeScannerOptions(formats: barcodeFormats)
}
}
... ...
... ... @@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:mobile_scanner/src/enums/barcode_format.dart';
import 'package:mobile_scanner/src/enums/mobile_scanner_authorization_state.dart';
import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart';
import 'package:mobile_scanner/src/enums/torch_state.dart';
... ... @@ -140,11 +141,22 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
}
@override
Future<BarcodeCapture?> analyzeImage(String path) async {
Future<BarcodeCapture?> analyzeImage(
String path, {
List<BarcodeFormat> formats = const <BarcodeFormat>[],
}) async {
final Map<Object?, Object?>? result =
await methodChannel.invokeMapMethod<Object?, Object?>(
'analyzeImage',
path,
{
'filePath': path,
'formats': formats.isEmpty
? null
: [
for (final BarcodeFormat format in formats)
if (format != BarcodeFormat.unknown) format.rawValue,
],
},
);
return _parseBarcode(result);
... ...
... ... @@ -174,7 +174,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
///
/// The [path] points to a file on the device.
///
/// This is only supported on Android and iOS.
/// This is only supported on Android, iOS and MacOS.
///
/// Returns the [BarcodeCapture] that was found in the image.
Future<BarcodeCapture?> analyzeImage(String path) {
... ...
import 'package:flutter/widgets.dart';
import 'package:mobile_scanner/src/enums/barcode_format.dart';
import 'package:mobile_scanner/src/enums/torch_state.dart';
import 'package:mobile_scanner/src/method_channel/mobile_scanner_method_channel.dart';
import 'package:mobile_scanner/src/mobile_scanner_view_attributes.dart';
... ... @@ -46,9 +47,15 @@ abstract class MobileScannerPlatform extends PlatformInterface {
/// Analyze a local image file for barcodes.
///
/// The [path] is the path to the file on disk.
/// The [formats] specify the barcode formats that should be detected.
///
/// If [formats] is empty, all barcode formats will be detected.
///
/// Returns the barcodes that were found in the image.
Future<BarcodeCapture?> analyzeImage(String path) {
Future<BarcodeCapture?> analyzeImage(
String path, {
List<BarcodeFormat> formats = const <BarcodeFormat>[],
}) {
throw UnimplementedError('analyzeImage() has not been implemented.');
}
... ...
... ... @@ -152,7 +152,7 @@ class Barcode {
/// This is null if the raw value is not available.
final String? rawValue;
/// The size of the barcode bounding box.
/// The normalized size of the barcode bounding box.
///
/// If the bounding box is unavailable, this will be [Size.zero].
final Size size;
... ...
... ... @@ -4,6 +4,7 @@ import 'dart:ui_web' as ui_web;
import 'package:flutter/widgets.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:mobile_scanner/src/enums/barcode_format.dart';
import 'package:mobile_scanner/src/enums/camera_facing.dart';
import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart';
import 'package:mobile_scanner/src/enums/torch_state.dart';
... ... @@ -232,7 +233,10 @@ class MobileScannerWeb extends MobileScannerPlatform {
}
@override
Future<BarcodeCapture?> analyzeImage(String path) {
Future<BarcodeCapture?> analyzeImage(
String path, {
List<BarcodeFormat> formats = const <BarcodeFormat>[],
}) {
throw UnsupportedError('analyzeImage() is not supported on the web.');
}
... ...
... ... @@ -75,13 +75,33 @@ extension type Result(JSObject _) implements JSObject {
/// Convert this result to a [Barcode].
Barcode get toBarcode {
final List<Offset> corners = resultPoints;
return Barcode(
corners: resultPoints,
corners: corners,
format: barcodeFormat,
displayValue: text,
rawBytes: rawBytes,
rawValue: text,
size: _computeSize(corners),
type: BarcodeType.text,
);
}
Size _computeSize(List<Offset> points) {
if (points.length != 4) {
return Size.zero;
}
final Iterable<double> xCoords = points.map((p) => p.dx);
final Iterable<double> yCoords = points.map((p) => p.dy);
// Find the minimum and maximum x and y coordinates.
final double xMin = xCoords.reduce((a, b) => a < b ? a : b);
final double xMax = xCoords.reduce((a, b) => a > b ? a : b);
final double yMin = yCoords.reduce((a, b) => a < b ? a : b);
final double yMax = yCoords.reduce((a, b) => a > b ? a : b);
return Size(xMax - xMin, yMax - yMin);
}
}
... ...
... ... @@ -138,11 +138,10 @@ final class ZXingBarcodeReader extends BarcodeReader {
required web.MediaStream videoStream,
}) async {
final int detectionTimeoutMs = options.detectionTimeoutMs;
final List<BarcodeFormat> formats = options.formats;
if (formats.contains(BarcodeFormat.unknown)) {
formats.removeWhere((element) => element == BarcodeFormat.unknown);
}
final List<BarcodeFormat> formats = [
for (final BarcodeFormat format in options.formats)
if (format != BarcodeFormat.unknown) format,
];
_reader = ZXingBrowserMultiFormatReader(
_createReaderHints(formats),
... ...
... ... @@ -71,6 +71,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
stop(result)
case "updateScanWindow":
updateScanWindow(call, result)
case "analyzeImage":
analyzeImage(call, result)
default:
result(FlutterMethodNotImplemented)
}
... ... @@ -124,7 +126,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
VTCreateCGImageFromCVPixelBuffer(self!.latestBuffer, options: nil, imageOut: &cgImage)
let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage!)
do {
let barcodeRequest:VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [weak self] (request, error) in
let barcodeRequest: VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [weak self] (request, error) in
self?.imagesCurrentlyBeingProcessed = false
if error != nil {
... ... @@ -452,6 +454,70 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
result(nil)
}
func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
let argReader = MapArgumentReader(call.arguments as? [String: Any])
let symbologies:[VNBarcodeSymbology] = argReader.toSymbology()
guard let filePath: String = argReader.string(key: "filePath") else {
// TODO: fix error code
result(FlutterError(code: "MobileScanner",
message: "No image found in analyzeImage!",
details: nil))
return
}
let fileUrl = URL(fileURLWithPath: filePath)
guard let ciImage = CIImage(contentsOf: fileUrl) else {
// TODO: fix error code
result(FlutterError(code: "MobileScanner",
message: "No image found in analyzeImage!",
details: nil))
return
}
let imageRequestHandler = VNImageRequestHandler(ciImage: ciImage, orientation: CGImagePropertyOrientation.up, options: [:])
do {
let barcodeRequest: VNDetectBarcodesRequest = VNDetectBarcodesRequest(
completionHandler: { [] (request, error) in
if error != nil {
DispatchQueue.main.async {
// TODO: fix error code
result(FlutterError(code: "MobileScanner", message: error?.localizedDescription, details: nil))
}
return
}
guard let barcodes: [VNBarcodeObservation] = request.results as? [VNBarcodeObservation] else {
return
}
if barcodes.isEmpty {
return
}
result([
"name": "barcode",
"data": barcodes.map({ $0.toMap() }),
])
})
if !symbologies.isEmpty {
// Add the symbologies the user wishes to support.
barcodeRequest.symbologies = symbologies
}
try imageRequestHandler.perform([barcodeRequest])
} catch let e {
// TODO: fix error code
DispatchQueue.main.async {
result(FlutterError(code: "MobileScanner", message: e.localizedDescription, details: nil))
}
}
}
// Observer for torch state
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
switch keyPath {
... ... @@ -542,10 +608,24 @@ extension CGImage {
}
extension VNBarcodeObservation {
private func distanceBetween(_ p1: CGPoint, _ p2: CGPoint) -> CGFloat {
return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2))
}
public func toMap() -> [String: Any?] {
return [
"rawValue": self.payloadStringValue ?? "",
"format": self.symbology.toInt ?? -1,
"corners": [
["x": topLeft.x, "y": topLeft.y],
["x": topRight.x, "y": topRight.y],
["x": bottomRight.x, "y": bottomRight.y],
["x": bottomLeft.x, "y": bottomLeft.y],
],
"format": symbology.toInt ?? -1,
"rawValue": payloadStringValue ?? "",
"size": [
"width": distanceBetween(topLeft, topRight),
"height": distanceBetween(topLeft, bottomLeft),
],
]
}
}
... ... @@ -585,7 +665,7 @@ extension VNBarcodeSymbology {
}
}
var toInt:Int? {
var toInt: Int? {
if #available(macOS 12.0, *) {
if(self == VNBarcodeSymbology.codabar){
return 8
... ...