Julian Steenbakker

imp: fix scan area on both ios and android

@@ -26,7 +26,7 @@ import io.flutter.view.TextureRegistry @@ -26,7 +26,7 @@ import io.flutter.view.TextureRegistry
26 import kotlin.math.roundToInt 26 import kotlin.math.roundToInt
27 27
28 28
29 -typealias MobileScannerCallback = (barcodes: List<Map<String, Any?>>, image: ByteArray?) -> Unit 29 +typealias MobileScannerCallback = (barcodes: List<Map<String, Any?>>, image: ByteArray?, width: Int?, height: Int?) -> Unit
30 typealias AnalyzerCallback = (barcodes: List<Map<String, Any?>>?) -> Unit 30 typealias AnalyzerCallback = (barcodes: List<Map<String, Any?>>?) -> Unit
31 typealias MobileScannerErrorCallback = (error: String) -> Unit 31 typealias MobileScannerErrorCallback = (error: String) -> Unit
32 typealias TorchStateCallback = (state: Int) -> Unit 32 typealias TorchStateCallback = (state: Int) -> Unit
@@ -151,17 +151,22 @@ class MobileScanner( @@ -151,17 +151,22 @@ class MobileScanner(
151 for ( barcode in barcodes) { 151 for ( barcode in barcodes) {
152 if(scanWindow != null) { 152 if(scanWindow != null) {
153 val match = isbarCodeInScanWindow(scanWindow!!, barcode, imageProxy) 153 val match = isbarCodeInScanWindow(scanWindow!!, barcode, imageProxy)
154 - if(!match) continue 154 + if(!match) {
  155 + continue
  156 + } else {
  157 + barcodeMap.add(barcode.data)
155 } 158 }
156 - Log.d("mobile_scanner: ", "width: ${inputImage.width}, height: ${inputImage.height}") 159 + } else {
157 barcodeMap.add(barcode.data) 160 barcodeMap.add(barcode.data)
158 } 161 }
  162 + }
159 163
160 if (barcodeMap.isNotEmpty()) { 164 if (barcodeMap.isNotEmpty()) {
161 -  
162 mobileScannerCallback( 165 mobileScannerCallback(
163 barcodeMap, 166 barcodeMap,
164 - if (returnImage) mediaImage.toByteArray() else null 167 + if (returnImage) mediaImage.toByteArray() else null,
  168 + if (returnImage) mediaImage.width else null,
  169 + if (returnImage) mediaImage.height else null
165 ) 170 )
166 } 171 }
167 } 172 }
@@ -194,12 +199,7 @@ class MobileScanner( @@ -194,12 +199,7 @@ class MobileScanner(
194 val bottom = (scanWindow[3] * imageHeight).roundToInt() 199 val bottom = (scanWindow[3] * imageHeight).roundToInt()
195 200
196 val scaledScanWindow = Rect(left, top, right, bottom) 201 val scaledScanWindow = Rect(left, top, right, bottom)
197 -  
198 -// Log.d("mobile_scanner: ", "scanWindow: $scaledScanWindow")  
199 -// Log.d("mobile_scanner: ", "bounding box: $barcodeBoundingBox")  
200 -// Log.d("mobile_scanner: ", "contains: ${scaledScanWindow.contains(barcodeBoundingBox)}")  
201 - return true  
202 -// return scaledScanWindow.contains(barcodeBoundingBox) 202 + return scaledScanWindow.contains(barcodeBoundingBox)
203 } 203 }
204 204
205 /** 205 /**
@@ -25,12 +25,14 @@ class MobileScannerPlugin : FlutterPlugin, ActivityAware, MethodChannel.MethodCa @@ -25,12 +25,14 @@ class MobileScannerPlugin : FlutterPlugin, ActivityAware, MethodChannel.MethodCa
25 25
26 private var analyzerResult: MethodChannel.Result? = null 26 private var analyzerResult: MethodChannel.Result? = null
27 27
28 - private val callback: MobileScannerCallback = { barcodes: List<Map<String, Any?>>, image: ByteArray? -> 28 + private val callback: MobileScannerCallback = { barcodes: List<Map<String, Any?>>, image: ByteArray?, width: Int?, height: Int? ->
29 if (image != null) { 29 if (image != null) {
30 barcodeHandler.publishEvent(mapOf( 30 barcodeHandler.publishEvent(mapOf(
31 "name" to "barcode", 31 "name" to "barcode",
32 "data" to barcodes, 32 "data" to barcodes,
33 - "image" to image 33 + "image" to image,
  34 + "width" to width!!.toDouble(),
  35 + "height" to height!!.toDouble()
34 )) 36 ))
35 } else { 37 } else {
36 barcodeHandler.publishEvent(mapOf( 38 barcodeHandler.publishEvent(mapOf(
  1 +import 'dart:io';
  2 +
1 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
2 import 'package:mobile_scanner/mobile_scanner.dart'; 4 import 'package:mobile_scanner/mobile_scanner.dart';
3 5
@@ -13,8 +15,10 @@ class _BarcodeScannerWithScanWindowState @@ -13,8 +15,10 @@ class _BarcodeScannerWithScanWindowState
13 extends State<BarcodeScannerWithScanWindow> { 15 extends State<BarcodeScannerWithScanWindow> {
14 late MobileScannerController controller = MobileScannerController(); 16 late MobileScannerController controller = MobileScannerController();
15 Barcode? barcode; 17 Barcode? barcode;
  18 + BarcodeCapture? capture;
16 19
17 Future<void> onDetect(BarcodeCapture barcode) async { 20 Future<void> onDetect(BarcodeCapture barcode) async {
  21 + capture = barcode;
18 setState(() => this.barcode = barcode.barcodes.first); 22 setState(() => this.barcode = barcode.barcodes.first);
19 } 23 }
20 24
@@ -50,7 +54,7 @@ class _BarcodeScannerWithScanWindowState @@ -50,7 +54,7 @@ class _BarcodeScannerWithScanWindowState
50 barcode?.corners != null && 54 barcode?.corners != null &&
51 arguments != null) 55 arguments != null)
52 CustomPaint( 56 CustomPaint(
53 - painter: BarcodeOverlay(barcode!, arguments!, BoxFit.contain, MediaQuery.of(context).devicePixelRatio), 57 + painter: BarcodeOverlay(barcode!, arguments!, BoxFit.contain, MediaQuery.of(context).devicePixelRatio, capture!),
54 ), 58 ),
55 CustomPaint( 59 CustomPaint(
56 painter: ScannerOverlay(scanWindow), 60 painter: ScannerOverlay(scanWindow),
@@ -122,8 +126,9 @@ class ScannerOverlay extends CustomPainter { @@ -122,8 +126,9 @@ class ScannerOverlay extends CustomPainter {
122 } 126 }
123 127
124 class BarcodeOverlay extends CustomPainter { 128 class BarcodeOverlay extends CustomPainter {
125 - BarcodeOverlay(this.barcode, this.arguments, this.boxFit, this.devicePixelRatio); 129 + BarcodeOverlay(this.barcode, this.arguments, this.boxFit, this.devicePixelRatio, this.capture);
126 130
  131 + final BarcodeCapture capture;
127 final Barcode barcode; 132 final Barcode barcode;
128 final MobileScannerArguments arguments; 133 final MobileScannerArguments arguments;
129 final BoxFit boxFit; 134 final BoxFit boxFit;
@@ -132,9 +137,6 @@ class BarcodeOverlay extends CustomPainter { @@ -132,9 +137,6 @@ class BarcodeOverlay extends CustomPainter {
132 @override 137 @override
133 void paint(Canvas canvas, Size size) { 138 void paint(Canvas canvas, Size size) {
134 if (barcode.corners == null) return; 139 if (barcode.corners == null) return;
135 -  
136 - // final realsize = Size(arguments.size.width * devicePixelRatio, arguments.size.height * devicePixelRatio);  
137 -  
138 final adjustedSize = applyBoxFit(boxFit, arguments.size, size); 140 final adjustedSize = applyBoxFit(boxFit, arguments.size, size);
139 141
140 double verticalPadding = size.height - adjustedSize.destination.height; 142 double verticalPadding = size.height - adjustedSize.destination.height;
@@ -151,8 +153,8 @@ class BarcodeOverlay extends CustomPainter { @@ -151,8 +153,8 @@ class BarcodeOverlay extends CustomPainter {
151 horizontalPadding = 0; 153 horizontalPadding = 0;
152 } 154 }
153 155
154 - final ratioWidth = arguments.size.width / adjustedSize.destination.width;  
155 - final ratioHeight = arguments.size.height / adjustedSize.destination.height; 156 + final ratioWidth = (Platform.isIOS ? capture.width! : arguments.size.width)/ adjustedSize.destination.width;
  157 + final ratioHeight = (Platform.isIOS ? capture.height! : arguments.size.height) / adjustedSize.destination.height;
156 158
157 final List<Offset> adjustedOffset = []; 159 final List<Offset> adjustedOffset = [];
158 for (final offset in barcode.corners!) { 160 for (final offset in barcode.corners!) {
@@ -12,16 +12,45 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { @@ -12,16 +12,45 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin {
12 /// The handler sends all information via an event channel back to Flutter 12 /// The handler sends all information via an event channel back to Flutter
13 private let barcodeHandler: BarcodeHandler 13 private let barcodeHandler: BarcodeHandler
14 14
15 - var scanWindow: CGRect? 15 + static var scanWindow: [CGFloat]?
  16 +
  17 + private static func isBarcodeInScanWindow(barcode: Barcode, imageSize: CGSize) -> Bool {
  18 + let scanwindow = SwiftMobileScannerPlugin.scanWindow!
  19 + let barcodeminX = barcode.cornerPoints![0].cgPointValue.x
  20 + let barcodeminY = barcode.cornerPoints![1].cgPointValue.y
  21 +
  22 + let barcodewidth = barcode.cornerPoints![2].cgPointValue.x - barcodeminX
  23 + let barcodeheight = barcode.cornerPoints![3].cgPointValue.y - barcodeminY
  24 + let barcodeBox = CGRect(x: barcodeminX, y: barcodeminY, width: barcodewidth, height: barcodeheight)
  25 +
  26 +
  27 + let minX = scanwindow[0] * imageSize.width
  28 + let minY = scanwindow[1] * imageSize.height
  29 +
  30 + let width = (scanwindow[2] * imageSize.width) - minX
  31 + let height = (scanwindow[3] * imageSize.height) - minY
  32 +
  33 + let scaledWindow = CGRect(x: minX, y: minY, width: width, height: height)
  34 +
  35 + return scaledWindow.contains(barcodeBox)
  36 + }
16 37
17 init(barcodeHandler: BarcodeHandler, registry: FlutterTextureRegistry) { 38 init(barcodeHandler: BarcodeHandler, registry: FlutterTextureRegistry) {
18 self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in 39 self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in
19 if barcodes != nil { 40 if barcodes != nil {
20 - let barcodesMap = barcodes!.map { barcode in 41 + let barcodesMap = barcodes!.compactMap { barcode in
  42 + if (SwiftMobileScannerPlugin.scanWindow != nil) {
  43 + if (SwiftMobileScannerPlugin.isBarcodeInScanWindow(barcode: barcode, imageSize: image.size)) {
  44 + return barcode.data
  45 + } else {
  46 + return nil
  47 + }
  48 + } else {
21 return barcode.data 49 return barcode.data
22 } 50 }
  51 + }
23 if (!barcodesMap.isEmpty) { 52 if (!barcodesMap.isEmpty) {
24 - barcodeHandler.publishEvent(["name": "barcode", "data": barcodesMap, "image": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!)]) 53 + barcodeHandler.publishEvent(["name": "barcode", "data": barcodesMap, "image": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!), "width": image.size.width, "height": image.size.height])
25 } 54 }
26 } else if (error != nil){ 55 } else if (error != nil){
27 barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription]) 56 barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription])
@@ -53,7 +82,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { @@ -53,7 +82,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin {
53 case "analyzeImage": 82 case "analyzeImage":
54 analyzeImage(call, result) 83 analyzeImage(call, result)
55 case "updateScanWindow": 84 case "updateScanWindow":
56 - updateScanWindow(call) 85 + updateScanWindow(call, result)
57 default: 86 default:
58 result(FlutterMethodNotImplemented) 87 result(FlutterMethodNotImplemented)
59 } 88 }
@@ -128,6 +157,28 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { @@ -128,6 +157,28 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin {
128 result(nil) 157 result(nil)
129 } 158 }
130 159
  160 + /// Toggles the torch
  161 + func updateScanWindow(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
  162 + let scanWindowData: Array? = (call.arguments as? [String: Any])?["rect"] as? [CGFloat]
  163 + SwiftMobileScannerPlugin.scanWindow = scanWindowData
  164 +
  165 + result(nil)
  166 + }
  167 +
  168 + static func arrayToRect(scanWindowData: [CGFloat]?) -> CGRect? {
  169 + if (scanWindowData == nil) {
  170 + return nil
  171 + }
  172 +
  173 + let minX = scanWindowData![0]
  174 + let minY = scanWindowData![1]
  175 +
  176 + let width = scanWindowData![2] - minX
  177 + let height = scanWindowData![3] - minY
  178 +
  179 + return CGRect(x: minX, y: minY, width: width, height: height)
  180 + }
  181 +
131 /// Analyzes a single image 182 /// Analyzes a single image
132 private func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 183 private func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
133 let uiImage = UIImage(contentsOfFile: call.arguments as? String ?? "") 184 let uiImage = UIImage(contentsOfFile: call.arguments as? String ?? "")
@@ -141,12 +192,6 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { @@ -141,12 +192,6 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin {
141 mobileScanner.analyzeImage(image: uiImage!, position: AVCaptureDevice.Position.back, callback: { [self] barcodes, error in 192 mobileScanner.analyzeImage(image: uiImage!, position: AVCaptureDevice.Position.back, callback: { [self] barcodes, error in
142 if error == nil && barcodes != nil { 193 if error == nil && barcodes != nil {
143 for barcode in barcodes! { 194 for barcode in barcodes! {
144 - if scanWindow != nil {  
145 - let match = isbarCodeInScanWindow(scanWindow!, barcode, buffer!.image)  
146 - if (!match) {  
147 - continue  
148 - }  
149 - }  
150 let event: [String: Any?] = ["name": "barcode", "data": barcode.data] 195 let event: [String: Any?] = ["name": "barcode", "data": barcode.data]
151 barcodeHandler.publishEvent(event) 196 barcodeHandler.publishEvent(event)
152 } 197 }
@@ -323,6 +323,8 @@ class MobileScannerController { @@ -323,6 +323,8 @@ class MobileScannerController {
323 BarcodeCapture( 323 BarcodeCapture(
324 barcodes: parsed, 324 barcodes: parsed,
325 image: event['image'] as Uint8List?, 325 image: event['image'] as Uint8List?,
  326 + width: event['width'] as double?,
  327 + height: event['height'] as double?,
326 ), 328 ),
327 ); 329 );
328 break; 330 break;
@@ -12,8 +12,14 @@ class BarcodeCapture { @@ -12,8 +12,14 @@ class BarcodeCapture {
12 12
13 final Uint8List? image; 13 final Uint8List? image;
14 14
  15 + final double? width;
  16 +
  17 + final double? height;
  18 +
15 BarcodeCapture({ 19 BarcodeCapture({
16 required this.barcodes, 20 required this.barcodes,
17 this.image, 21 this.image,
  22 + this.width,
  23 + this.height
18 }); 24 });
19 } 25 }