Showing
6 changed files
with
196 additions
and
56 deletions
| @@ -58,6 +58,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | @@ -58,6 +58,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | ||
| 58 | // "analyze" -> switchAnalyzeMode(call, result) | 58 | // "analyze" -> switchAnalyzeMode(call, result) |
| 59 | "stop" -> stop(result) | 59 | "stop" -> stop(result) |
| 60 | "analyzeImage" -> analyzeImage(call, result) | 60 | "analyzeImage" -> analyzeImage(call, result) |
| 61 | + "updateScanWindow" -> updateScanWindow(call) | ||
| 61 | else -> result.notImplemented() | 62 | else -> result.notImplemented() |
| 62 | } | 63 | } |
| 63 | } | 64 | } |
| @@ -108,9 +109,10 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | @@ -108,9 +109,10 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | ||
| 108 | scanner.process(inputImage) | 109 | scanner.process(inputImage) |
| 109 | .addOnSuccessListener { barcodes -> | 110 | .addOnSuccessListener { barcodes -> |
| 110 | for (barcode in barcodes) { | 111 | for (barcode in barcodes) { |
| 112 | + | ||
| 111 | if(scanWindow != null) { | 113 | if(scanWindow != null) { |
| 112 | - val boundingBox = barcode.getBoundingBox() | ||
| 113 | - if(boundingBox == null || !scanWindow!!.contains(boundingBox!!)) continue | 114 | + val match = isbarCodeInScanWindow(scanWindow!!, barcode, inputImage) |
| 115 | + if(!match) continue | ||
| 114 | } | 116 | } |
| 115 | 117 | ||
| 116 | val event = mapOf("name" to "barcode", "data" to barcode.data) | 118 | val event = mapOf("name" to "barcode", "data" to barcode.data) |
| @@ -126,6 +128,34 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | @@ -126,6 +128,34 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | ||
| 126 | 128 | ||
| 127 | private var scanner = BarcodeScanning.getClient() | 129 | private var scanner = BarcodeScanning.getClient() |
| 128 | 130 | ||
| 131 | + | ||
| 132 | + private fun updateScanWindow(call: MethodCall) { | ||
| 133 | + val scanWindowData: List<Int>? = call.argument("rect") | ||
| 134 | + if(scanWindowData == null) return | ||
| 135 | + | ||
| 136 | + scanWindow = Rect(scanWindowData!![0], scanWindowData!![1], scanWindowData!![2], scanWindowData!![3]) | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + // scales the scanWindow to the provided inputImage and checks if that scaled | ||
| 140 | + // scanWindow contains the barcode | ||
| 141 | + private fun isbarCodeInScanWindow(scanWindow: Rect, barcode: Barcode, inputImage: InputImage): Boolean { | ||
| 142 | + val barcodeBoundingBox = barcode.getBoundingBox() | ||
| 143 | + if(barcodeBoundingBox == null) return false | ||
| 144 | + | ||
| 145 | + val imageWidth = inputImage.getWidth(); | ||
| 146 | + val imageHeight = inputImage.getHeight(); | ||
| 147 | + | ||
| 148 | + val left = scanWindow.left * imageWidth | ||
| 149 | + val top = scanWindow.top * imageHeight | ||
| 150 | + val right = scanWindow.right * imageWidth | ||
| 151 | + val bottom = scanWindow.bottom * imageHeight | ||
| 152 | + | ||
| 153 | + val scaledScanWindow = Rect(left, top, right, bottom) | ||
| 154 | + return scaledScanWindow.contains(barcodeBoundingBox) | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + | ||
| 158 | + | ||
| 129 | @ExperimentalGetImage | 159 | @ExperimentalGetImage |
| 130 | private fun start(call: MethodCall, result: MethodChannel.Result) { | 160 | private fun start(call: MethodCall, result: MethodChannel.Result) { |
| 131 | if (camera?.cameraInfo != null && preview != null && textureEntry != null) { | 161 | if (camera?.cameraInfo != null && preview != null && textureEntry != null) { |
| @@ -142,9 +172,6 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | @@ -142,9 +172,6 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | ||
| 142 | val torch: Boolean = call.argument<Boolean>("torch") ?: false | 172 | val torch: Boolean = call.argument<Boolean>("torch") ?: false |
| 143 | val formats: List<Int>? = call.argument<List<Int>>("formats") | 173 | val formats: List<Int>? = call.argument<List<Int>>("formats") |
| 144 | 174 | ||
| 145 | - val scanWindowData: List<Int>? = call.argument("scanWindow") | ||
| 146 | - if(scanWindowData != null) scanWindow = Rect(scanWindowData!![0], scanWindowData!![1], scanWindowData!![2], scanWindowData!![3]) | ||
| 147 | - | ||
| 148 | if (formats != null) { | 175 | if (formats != null) { |
| 149 | val formatsList: MutableList<Int> = mutableListOf() | 176 | val formatsList: MutableList<Int> = mutableListOf() |
| 150 | for (index in formats) { | 177 | for (index in formats) { |
| @@ -253,8 +280,8 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | @@ -253,8 +280,8 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: | ||
| 253 | .addOnSuccessListener { barcodes -> | 280 | .addOnSuccessListener { barcodes -> |
| 254 | for (barcode in barcodes) { | 281 | for (barcode in barcodes) { |
| 255 | if(scanWindow != null) { | 282 | if(scanWindow != null) { |
| 256 | - val boundingBox = barcode.getBoundingBox() | ||
| 257 | - if(boundingBox == null || !scanWindow!!.contains(boundingBox!!)) continue | 283 | + val match = isbarCodeInScanWindow(scanWindow!!, barcode, inputImage) |
| 284 | + if(!match) continue | ||
| 258 | } | 285 | } |
| 259 | 286 | ||
| 260 | barcodeFound = true | 287 | barcodeFound = true |
| @@ -36,7 +36,6 @@ class _BarcodeScannerWithScanWindowState | @@ -36,7 +36,6 @@ class _BarcodeScannerWithScanWindowState | ||
| 36 | 36 | ||
| 37 | controller = MobileScannerController( | 37 | controller = MobileScannerController( |
| 38 | torchEnabled: true, | 38 | torchEnabled: true, |
| 39 | - scanWindow: scanWindow, | ||
| 40 | ); | 39 | ); |
| 41 | } | 40 | } |
| 42 | 41 | ||
| @@ -52,6 +51,7 @@ class _BarcodeScannerWithScanWindowState | @@ -52,6 +51,7 @@ class _BarcodeScannerWithScanWindowState | ||
| 52 | children: [ | 51 | children: [ |
| 53 | MobileScanner( | 52 | MobileScanner( |
| 54 | fit: BoxFit.contain, | 53 | fit: BoxFit.contain, |
| 54 | + scanWindow: scanWindow, | ||
| 55 | controller: controller, | 55 | controller: controller, |
| 56 | onDetect: (barcode, args) { | 56 | onDetect: (barcode, args) { |
| 57 | setState(() { | 57 | setState(() { |
| @@ -64,6 +64,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -64,6 +64,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 64 | stop(result) | 64 | stop(result) |
| 65 | case "analyzeImage": | 65 | case "analyzeImage": |
| 66 | analyzeImage(call, result) | 66 | analyzeImage(call, result) |
| 67 | + case "updateScanWindow": | ||
| 68 | + updateScanWindow(call) | ||
| 67 | default: | 69 | default: |
| 68 | result(FlutterMethodNotImplemented) | 70 | result(FlutterMethodNotImplemented) |
| 69 | } | 71 | } |
| @@ -114,8 +116,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -114,8 +116,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 114 | for barcode in barcodes! { | 116 | for barcode in barcodes! { |
| 115 | 117 | ||
| 116 | if scanWindow != nil { | 118 | if scanWindow != nil { |
| 117 | - let boundingBox = barcode.frame | ||
| 118 | - if !scanWindow!.contains(boundingBox) { | 119 | + let match = isbarCodeInScanWindow(scanWindow!, barcode, buffer!.image) |
| 120 | + if (!match) { | ||
| 119 | continue | 121 | continue |
| 120 | } | 122 | } |
| 121 | } | 123 | } |
| @@ -167,6 +169,38 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -167,6 +169,38 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 167 | AVCaptureDevice.requestAccess(for: .video, completionHandler: { result($0) }) | 169 | AVCaptureDevice.requestAccess(for: .video, completionHandler: { result($0) }) |
| 168 | } | 170 | } |
| 169 | 171 | ||
| 172 | + func updateScanWindow(_ call: FlutterMethodCall) { | ||
| 173 | + let argReader = MapArgumentReader(call.arguments as? [String: Any]) | ||
| 174 | + let scanWindowData: Array? = argReader.floatArray(key: "rect") | ||
| 175 | + | ||
| 176 | + if (scanWindowData == nil) { | ||
| 177 | + return | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + let minX = scanWindowData![0] | ||
| 181 | + let minY = scanWindowData![1] | ||
| 182 | + | ||
| 183 | + let width = scanWindowData![2] - minX | ||
| 184 | + let height = scanWindowData![3] - minY | ||
| 185 | + | ||
| 186 | + scanWindow = CGRect(x: minX, y: minY, width: width, height: height) | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + func isbarCodeInScanWindow(_ scanWindow: CGRect, _ barcode: Barcode, _ inputImage: UIImage) -> Bool { | ||
| 190 | + let barcodeBoundingBox = barcode.frame | ||
| 191 | + | ||
| 192 | + let imageWidth = inputImage.size.width; | ||
| 193 | + let imageHeight = inputImage.size.height; | ||
| 194 | + | ||
| 195 | + let minX = scanWindow.minX * imageWidth | ||
| 196 | + let minY = scanWindow.minY * imageHeight | ||
| 197 | + let width = scanWindow.width * imageWidth | ||
| 198 | + let height = scanWindow.height * imageHeight | ||
| 199 | + | ||
| 200 | + let scaledScanWindow = CGRect(x: minX, y: minY, width: width, height: height) | ||
| 201 | + return scaledScanWindow.contains(barcodeBoundingBox) | ||
| 202 | + } | ||
| 203 | + | ||
| 170 | func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 204 | func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 171 | if (device != nil) { | 205 | if (device != nil) { |
| 172 | result(FlutterError(code: "MobileScanner", | 206 | result(FlutterError(code: "MobileScanner", |
| @@ -184,21 +218,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -184,21 +218,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 184 | let torch: Bool = argReader.bool(key: "torch") ?? false | 218 | let torch: Bool = argReader.bool(key: "torch") ?? false |
| 185 | let facing: Int = argReader.int(key: "facing") ?? 1 | 219 | let facing: Int = argReader.int(key: "facing") ?? 1 |
| 186 | let formats: Array = argReader.intArray(key: "formats") ?? [] | 220 | let formats: Array = argReader.intArray(key: "formats") ?? [] |
| 187 | - let scanWindowData: Array? = argReader.floatArray(key: "scanWindow") | ||
| 188 | - | ||
| 189 | - if(scanWindowData != nil) { | ||
| 190 | - | ||
| 191 | - let minX = scanWindowData![0] | ||
| 192 | - let minY = scanWindowData![1] | ||
| 193 | - | ||
| 194 | - let width = scanWindowData![2] - minX | ||
| 195 | - let height = scanWindowData![3] - minY | ||
| 196 | - | ||
| 197 | - scanWindow = CGRect(x: minX, y: minY, width: width, height: height) | ||
| 198 | - } | ||
| 199 | - | ||
| 200 | - | ||
| 201 | - | 221 | + |
| 202 | let formatList: NSMutableArray = [] | 222 | let formatList: NSMutableArray = [] |
| 203 | for index in formats { | 223 | for index in formats { |
| 204 | formatList.add(BarcodeFormat(rawValue: index)) | 224 | formatList.add(BarcodeFormat(rawValue: index)) |
| @@ -319,8 +339,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | @@ -319,8 +339,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan | ||
| 319 | for barcode in barcodes! { | 339 | for barcode in barcodes! { |
| 320 | 340 | ||
| 321 | if scanWindow != nil { | 341 | if scanWindow != nil { |
| 322 | - let boundingBox = barcode.frame | ||
| 323 | - if !scanWindow!.contains(boundingBox) { | 342 | + let match = isbarCodeInScanWindow(scanWindow!, barcode, uiImage!) |
| 343 | + if (!match) { | ||
| 324 | continue | 344 | continue |
| 325 | } | 345 | } |
| 326 | } | 346 | } |
| @@ -27,6 +27,13 @@ class MobileScanner extends StatefulWidget { | @@ -27,6 +27,13 @@ class MobileScanner extends StatefulWidget { | ||
| 27 | /// Set to false if you don't want duplicate scans. | 27 | /// Set to false if you don't want duplicate scans. |
| 28 | final bool allowDuplicates; | 28 | final bool allowDuplicates; |
| 29 | 29 | ||
| 30 | + /// if set barcodes will only be scanned if they fall within this [Rect] | ||
| 31 | + /// useful for having a cut-out overlay for example. these [Rect] | ||
| 32 | + /// coordinates are relative to the widget size, so by how much your | ||
| 33 | + /// rectangle overlays the actual image can depend on things like the | ||
| 34 | + /// [BoxFit] | ||
| 35 | + final Rect? scanWindow; | ||
| 36 | + | ||
| 30 | /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized. | 37 | /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized. |
| 31 | const MobileScanner({ | 38 | const MobileScanner({ |
| 32 | Key? key, | 39 | Key? key, |
| @@ -34,6 +41,7 @@ class MobileScanner extends StatefulWidget { | @@ -34,6 +41,7 @@ class MobileScanner extends StatefulWidget { | ||
| 34 | this.controller, | 41 | this.controller, |
| 35 | this.fit = BoxFit.cover, | 42 | this.fit = BoxFit.cover, |
| 36 | this.allowDuplicates = false, | 43 | this.allowDuplicates = false, |
| 44 | + this.scanWindow, | ||
| 37 | }) : super(key: key); | 45 | }) : super(key: key); |
| 38 | 46 | ||
| 39 | @override | 47 | @override |
| @@ -67,6 +75,58 @@ class _MobileScannerState extends State<MobileScanner> | @@ -67,6 +75,58 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 67 | 75 | ||
| 68 | String? lastScanned; | 76 | String? lastScanned; |
| 69 | 77 | ||
| 78 | + /// the [scanWindow] rect will be relative and scaled to the [widgetSize] not the texture. so it is possible, | ||
| 79 | + /// depending on the [fit], for the [scanWindow] to partially or not at all overlap the [textureSize] | ||
| 80 | + /// | ||
| 81 | + /// since when using a [BoxFit] the content will always be centered on its parent. we can convert the rect | ||
| 82 | + /// to be relative to the texture. | ||
| 83 | + /// | ||
| 84 | + /// since the textures size and the actuall image (on the texture size) might not be the same, we also need to | ||
| 85 | + /// calculate the scanWindow in terms of percentages of the texture, not pixels. | ||
| 86 | + Rect calculateScanWindowRelativeToTextureInPercentage( | ||
| 87 | + BoxFit fit, | ||
| 88 | + Rect scanWindow, | ||
| 89 | + Size textureSize, | ||
| 90 | + Size widgetSize, | ||
| 91 | + ) { | ||
| 92 | + /// map the texture size to get its new size after fitted to screen | ||
| 93 | + final fittedSizes = applyBoxFit(fit, textureSize, widgetSize); | ||
| 94 | + final fittedTextureSize = fittedSizes.destination; | ||
| 95 | + | ||
| 96 | + final minX = widgetSize.width / 2 - fittedTextureSize.width / 2; | ||
| 97 | + final minY = widgetSize.height / 2 - fittedTextureSize.height / 2; | ||
| 98 | + final width = fittedTextureSize.width; | ||
| 99 | + final height = fittedTextureSize.height; | ||
| 100 | + | ||
| 101 | + final textureWindow = Rect.fromLTWH(minX, minY, width, height); | ||
| 102 | + | ||
| 103 | + /// create a new scan window and with only the area of the rect intersecting the texture | ||
| 104 | + final scanWindowInTexture = scanWindow.intersect(textureWindow); | ||
| 105 | + | ||
| 106 | + /// update the scanWindow left and top to be relative to the texture not the widget | ||
| 107 | + final newLeft = scanWindowInTexture.left - minX; | ||
| 108 | + final newTop = scanWindowInTexture.top - minY; | ||
| 109 | + final newWidth = scanWindowInTexture.width; | ||
| 110 | + final newHeight = scanWindowInTexture.height; | ||
| 111 | + | ||
| 112 | + /// new scanWindow that is adapted to the boxfit and relative to the texture | ||
| 113 | + final windowInTexture = Rect.fromLTWH(newLeft, newTop, newWidth, newHeight); | ||
| 114 | + | ||
| 115 | + /// get the scanWindow as a percentage of the texture | ||
| 116 | + final percentageLeft = windowInTexture.left / fittedTextureSize.width; | ||
| 117 | + final percentageRight = windowInTexture.right / fittedTextureSize.width; | ||
| 118 | + final percentageTop = windowInTexture.top / fittedTextureSize.height; | ||
| 119 | + final percentagebottom = windowInTexture.bottom / fittedTextureSize.height; | ||
| 120 | + | ||
| 121 | + /// this rectangle can be send to native code and used to cut out a rectangle of the scan image | ||
| 122 | + return Rect.fromLTRB( | ||
| 123 | + percentageLeft, | ||
| 124 | + percentageRight, | ||
| 125 | + percentageTop, | ||
| 126 | + percentagebottom, | ||
| 127 | + ); | ||
| 128 | + } | ||
| 129 | + | ||
| 70 | @override | 130 | @override |
| 71 | Widget build(BuildContext context) { | 131 | Widget build(BuildContext context) { |
| 72 | return LayoutBuilder( | 132 | return LayoutBuilder( |
| @@ -78,6 +138,17 @@ class _MobileScannerState extends State<MobileScanner> | @@ -78,6 +138,17 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 78 | if (value == null) { | 138 | if (value == null) { |
| 79 | return Container(color: Colors.black); | 139 | return Container(color: Colors.black); |
| 80 | } else { | 140 | } else { |
| 141 | + if (widget.scanWindow != null) { | ||
| 142 | + final window = calculateScanWindowRelativeToTextureInPercentage( | ||
| 143 | + widget.fit, | ||
| 144 | + widget.scanWindow!, | ||
| 145 | + value.size, | ||
| 146 | + Size(constraints.maxWidth, constraints.maxHeight), | ||
| 147 | + ); | ||
| 148 | + print(window); | ||
| 149 | + controller.updateScanWindow(window); | ||
| 150 | + } | ||
| 151 | + | ||
| 81 | controller.barcodes.listen((barcode) { | 152 | controller.barcodes.listen((barcode) { |
| 82 | if (!widget.allowDuplicates) { | 153 | if (!widget.allowDuplicates) { |
| 83 | if (lastScanned != barcode.rawValue) { | 154 | if (lastScanned != barcode.rawValue) { |
| @@ -49,9 +49,6 @@ class MobileScannerController { | @@ -49,9 +49,6 @@ class MobileScannerController { | ||
| 49 | /// WARNING: On iOS, only 1 format is supported. | 49 | /// WARNING: On iOS, only 1 format is supported. |
| 50 | final List<BarcodeFormat>? formats; | 50 | final List<BarcodeFormat>? formats; |
| 51 | 51 | ||
| 52 | - /// can be used to limit the scan area to a portion of the screen | ||
| 53 | - final Rect? scanWindow; | ||
| 54 | - | ||
| 55 | CameraFacing facing; | 52 | CameraFacing facing; |
| 56 | bool hasTorch = false; | 53 | bool hasTorch = false; |
| 57 | late StreamController<Barcode> barcodesController; | 54 | late StreamController<Barcode> barcodesController; |
| @@ -63,7 +60,6 @@ class MobileScannerController { | @@ -63,7 +60,6 @@ class MobileScannerController { | ||
| 63 | this.ratio, | 60 | this.ratio, |
| 64 | this.torchEnabled, | 61 | this.torchEnabled, |
| 65 | this.formats, | 62 | this.formats, |
| 66 | - this.scanWindow, | ||
| 67 | }) { | 63 | }) { |
| 68 | // In case a new instance is created before calling dispose() | 64 | // In case a new instance is created before calling dispose() |
| 69 | if (_controllerHashcode != null) { | 65 | if (_controllerHashcode != null) { |
| @@ -160,14 +156,14 @@ class MobileScannerController { | @@ -160,14 +156,14 @@ class MobileScannerController { | ||
| 160 | // Set the starting arguments for the camera | 156 | // Set the starting arguments for the camera |
| 161 | final Map arguments = {}; | 157 | final Map arguments = {}; |
| 162 | arguments['facing'] = facing.index; | 158 | arguments['facing'] = facing.index; |
| 163 | - if (scanWindow != null) { | 159 | + /* if (scanWindow != null) { |
| 164 | arguments['scanWindow'] = [ | 160 | arguments['scanWindow'] = [ |
| 165 | scanWindow!.left, | 161 | scanWindow!.left, |
| 166 | scanWindow!.top, | 162 | scanWindow!.top, |
| 167 | scanWindow!.right, | 163 | scanWindow!.right, |
| 168 | scanWindow!.bottom, | 164 | scanWindow!.bottom, |
| 169 | ]; | 165 | ]; |
| 170 | - } | 166 | + } */ |
| 171 | if (ratio != null) arguments['ratio'] = ratio; | 167 | if (ratio != null) arguments['ratio'] = ratio; |
| 172 | if (torchEnabled != null) arguments['torch'] = torchEnabled; | 168 | if (torchEnabled != null) arguments['torch'] = torchEnabled; |
| 173 | 169 | ||
| @@ -295,4 +291,10 @@ class MobileScannerController { | @@ -295,4 +291,10 @@ class MobileScannerController { | ||
| 295 | 'MobileScannerController methods should not be used after calling dispose.'; | 291 | 'MobileScannerController methods should not be used after calling dispose.'; |
| 296 | assert(hashCode == _controllerHashcode, message); | 292 | assert(hashCode == _controllerHashcode, message); |
| 297 | } | 293 | } |
| 294 | + | ||
| 295 | + /// updates the native scanwindow | ||
| 296 | + Future<void> updateScanWindow(Rect window) async { | ||
| 297 | + final data = [window.left, window.right, window.top, window.bottom]; | ||
| 298 | + await methodChannel.invokeMethod('updateScanWindow', {'rect': data}); | ||
| 299 | + } | ||
| 298 | } | 300 | } |
| @@ -61,6 +61,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -61,6 +61,8 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 61 | // switchAnalyzeMode(call, result) | 61 | // switchAnalyzeMode(call, result) |
| 62 | case "stop": | 62 | case "stop": |
| 63 | stop(result) | 63 | stop(result) |
| 64 | + case "updateScanWindow": | ||
| 65 | + updateScanWindow(call) | ||
| 64 | default: | 66 | default: |
| 65 | result(FlutterMethodNotImplemented) | 67 | result(FlutterMethodNotImplemented) |
| 66 | } | 68 | } |
| @@ -113,18 +115,17 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -113,18 +115,17 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 113 | try imageRequestHandler.perform([VNDetectBarcodesRequest { (request, error) in | 115 | try imageRequestHandler.perform([VNDetectBarcodesRequest { (request, error) in |
| 114 | if error == nil { | 116 | if error == nil { |
| 115 | if let results = request.results as? [VNBarcodeObservation] { | 117 | if let results = request.results as? [VNBarcodeObservation] { |
| 116 | - for barcode in results { | ||
| 117 | - if scanWindow != nil { | ||
| 118 | - let boundingBox = barcode.frame | ||
| 119 | - if !scanWindow!.contains(boundingBox) { | ||
| 120 | - continue | ||
| 121 | - } | 118 | + for barcode in results { |
| 119 | + if scanWindow != nil { | ||
| 120 | + let match = isbarCodeInScanWindow(scanWindow!, barcode, buffer!.image) | ||
| 121 | + if (!match) { | ||
| 122 | + continue | ||
| 122 | } | 123 | } |
| 124 | + } | ||
| 123 | 125 | ||
| 124 | - | ||
| 125 | - let barcodeType = String(barcode.symbology.rawValue).replacingOccurrences(of: "VNBarcodeSymbology", with: "") | ||
| 126 | - let event: [String: Any?] = ["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcodeType]] | ||
| 127 | - self.sink?(event) | 126 | + let barcodeType = String(barcode.symbology.rawValue).replacingOccurrences(of: "VNBarcodeSymbology", with: "") |
| 127 | + let event: [String: Any?] = ["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcodeType]] | ||
| 128 | + self.sink?(event) | ||
| 128 | 129 | ||
| 129 | // if barcodeType == "QR" { | 130 | // if barcodeType == "QR" { |
| 130 | // let image = CIImage(image: source) | 131 | // let image = CIImage(image: source) |
| @@ -170,6 +171,38 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -170,6 +171,38 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 170 | } | 171 | } |
| 171 | } | 172 | } |
| 172 | 173 | ||
| 174 | + func updateScanWindow(_ call: FlutterMethodCall) { | ||
| 175 | + let argReader = MapArgumentReader(call.arguments as? [String: Any]) | ||
| 176 | + let scanWindowData: Array? = argReader.floatArray(key: "rect") | ||
| 177 | + | ||
| 178 | + if (scanWindowData == nil) { | ||
| 179 | + return | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + let minX = scanWindowData![0] | ||
| 183 | + let minY = scanWindowData![1] | ||
| 184 | + | ||
| 185 | + let width = scanWindowData![2] - minX | ||
| 186 | + let height = scanWindowData![3] - minY | ||
| 187 | + | ||
| 188 | + scanWindow = CGRect(x: minX, y: minY, width: width, height: height) | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + func isbarCodeInScanWindow(_ scanWindow: CGRect, _ barcode: Barcode, _ inputImage: UIImage) -> Bool { | ||
| 192 | + let barcodeBoundingBox = barcode.frame | ||
| 193 | + | ||
| 194 | + let imageWidth = inputImage.size.width; | ||
| 195 | + let imageHeight = inputImage.size.height; | ||
| 196 | + | ||
| 197 | + let minX = scanWindow.minX * imageWidth | ||
| 198 | + let minY = scanWindow.minY * imageHeight | ||
| 199 | + let width = scanWindow.width * imageWidth | ||
| 200 | + let height = scanWindow.height * imageHeight | ||
| 201 | + | ||
| 202 | + let scaledScanWindow = CGRect(x: minX, y: minY, width: width, height: height) | ||
| 203 | + return scaledScanWindow.contains(barcodeBoundingBox) | ||
| 204 | + } | ||
| 205 | + | ||
| 173 | func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 206 | func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 174 | if (device != nil) { | 207 | if (device != nil) { |
| 175 | result(FlutterError(code: "MobileScanner", | 208 | result(FlutterError(code: "MobileScanner", |
| @@ -187,19 +220,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -187,19 +220,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 187 | let torch: Bool = argReader.bool(key: "torch") ?? false | 220 | let torch: Bool = argReader.bool(key: "torch") ?? false |
| 188 | let facing: Int = argReader.int(key: "facing") ?? 1 | 221 | let facing: Int = argReader.int(key: "facing") ?? 1 |
| 189 | 222 | ||
| 190 | - let scanWindowData: Array? = argReader.floatArray(key: "scanWindow") | ||
| 191 | - | ||
| 192 | - if(scanWindowData != nil) { | ||
| 193 | - | ||
| 194 | - let minX = scanWindowData![0] | ||
| 195 | - let minY = scanWindowData![1] | ||
| 196 | - | ||
| 197 | - let width = scanWindowData![2] - minX | ||
| 198 | - let height = scanWindowData![3] - minY | ||
| 199 | - | ||
| 200 | - scanWindow = CGRect(x: minX, y: minY, width: width, height: height) | ||
| 201 | - } | ||
| 202 | - | ||
| 203 | // Set the camera to use | 223 | // Set the camera to use |
| 204 | position = facing == 0 ? AVCaptureDevice.Position.front : .back | 224 | position = facing == 0 ? AVCaptureDevice.Position.front : .back |
| 205 | 225 |
-
Please register or login to post a comment