Committed by
GitHub
Merge pull request #1169 from navaronbracke/size_fix_android
fix: Fix discrepancy between barcode bounding box and barcode capture size
Showing
16 changed files
with
257 additions
and
131 deletions
| 1 | +## 5.2.2 | ||
| 2 | + | ||
| 3 | +Improvements: | ||
| 4 | +* [MacOS] Adds Swift Package Manager support. | ||
| 5 | +* [MacOS] Adds support for `returnImage`. | ||
| 6 | +* Added a new `size` property to `Barcode`, that denotes the bounding box of the barcode. | ||
| 7 | + | ||
| 8 | +Bugs fixed: | ||
| 9 | +* Fixed some documentation errors for the `size` and `image` of `BarcodeCapture`. | ||
| 10 | +* [iOS] Fixed a bug with `returnImage`. | ||
| 11 | +* [Android/iOS] Adjusted the raw barcode scan value to pass the raw event data, like on MacOS. | ||
| 12 | + | ||
| 1 | ## 5.2.1 | 13 | ## 5.2.1 |
| 2 | 14 | ||
| 3 | * Updates the `package:web` dependency to use a version range. | 15 | * Updates the `package:web` dependency to use a version range. |
| @@ -44,7 +56,7 @@ Improvements: | @@ -44,7 +56,7 @@ Improvements: | ||
| 44 | This major release contains all the changes from the 5.0.0 beta releases, along with the following changes: | 56 | This major release contains all the changes from the 5.0.0 beta releases, along with the following changes: |
| 45 | 57 | ||
| 46 | Improvements: | 58 | Improvements: |
| 47 | -- [Android] Remove the Kotlin Standard Library from the dependencies, as it is automatically included in Kotlin 1.4+ | 59 | +* [Android] Remove the Kotlin Standard Library from the dependencies, as it is automatically included in Kotlin 1.4+ |
| 48 | 60 | ||
| 49 | ## 5.0.0-beta.3 | 61 | ## 5.0.0-beta.3 |
| 50 | **BREAKING CHANGES:** | 62 | **BREAKING CHANGES:** |
| @@ -50,9 +50,11 @@ class MobileScannerHandler( | @@ -50,9 +50,11 @@ class MobileScannerHandler( | ||
| 50 | barcodeHandler.publishEvent(mapOf( | 50 | barcodeHandler.publishEvent(mapOf( |
| 51 | "name" to "barcode", | 51 | "name" to "barcode", |
| 52 | "data" to barcodes, | 52 | "data" to barcodes, |
| 53 | - "image" to image, | ||
| 54 | - "width" to width!!.toDouble(), | ||
| 55 | - "height" to height!!.toDouble() | 53 | + "image" to mapOf( |
| 54 | + "bytes" to image, | ||
| 55 | + "width" to width?.toDouble(), | ||
| 56 | + "height" to height?.toDouble(), | ||
| 57 | + ) | ||
| 56 | )) | 58 | )) |
| 57 | } else { | 59 | } else { |
| 58 | barcodeHandler.publishEvent(mapOf( | 60 | barcodeHandler.publishEvent(mapOf( |
| @@ -28,12 +28,22 @@ fun Image.toByteArray(): ByteArray { | @@ -28,12 +28,22 @@ fun Image.toByteArray(): ByteArray { | ||
| 28 | 28 | ||
| 29 | val Barcode.data: Map<String, Any?> | 29 | val Barcode.data: Map<String, Any?> |
| 30 | get() = mapOf( | 30 | get() = mapOf( |
| 31 | - "corners" to cornerPoints?.map { corner -> corner.data }, "format" to format, | ||
| 32 | - "rawBytes" to rawBytes, "rawValue" to rawValue, "type" to valueType, | ||
| 33 | - "calendarEvent" to calendarEvent?.data, "contactInfo" to contactInfo?.data, | ||
| 34 | - "driverLicense" to driverLicense?.data, "email" to email?.data, | ||
| 35 | - "geoPoint" to geoPoint?.data, "phone" to phone?.data, "sms" to sms?.data, | ||
| 36 | - "url" to url?.data, "wifi" to wifi?.data, "displayValue" to displayValue | 31 | + "calendarEvent" to calendarEvent?.data, |
| 32 | + "contactInfo" to contactInfo?.data, | ||
| 33 | + "corners" to cornerPoints?.map { corner -> corner.data }, | ||
| 34 | + "displayValue" to displayValue, | ||
| 35 | + "driverLicense" to driverLicense?.data, | ||
| 36 | + "email" to email?.data, | ||
| 37 | + "format" to format, | ||
| 38 | + "geoPoint" to geoPoint?.data, | ||
| 39 | + "phone" to phone?.data, | ||
| 40 | + "rawBytes" to rawBytes, | ||
| 41 | + "rawValue" to rawValue, | ||
| 42 | + "size" to boundingBox?.size, | ||
| 43 | + "sms" to sms?.data, | ||
| 44 | + "type" to valueType, | ||
| 45 | + "url" to url?.data, | ||
| 46 | + "wifi" to wifi?.data, | ||
| 37 | ) | 47 | ) |
| 38 | 48 | ||
| 39 | private val Point.data: Map<String, Double> | 49 | private val Point.data: Map<String, Double> |
| @@ -92,4 +102,14 @@ private val Barcode.UrlBookmark.data: Map<String, Any?> | @@ -92,4 +102,14 @@ private val Barcode.UrlBookmark.data: Map<String, Any?> | ||
| 92 | get() = mapOf("title" to title, "url" to url) | 102 | get() = mapOf("title" to title, "url" to url) |
| 93 | 103 | ||
| 94 | private val Barcode.WiFi.data: Map<String, Any?> | 104 | private val Barcode.WiFi.data: Map<String, Any?> |
| 95 | - get() = mapOf("encryptionType" to encryptionType, "password" to password, "ssid" to ssid) | ||
| 105 | + get() = mapOf("encryptionType" to encryptionType, "password" to password, "ssid" to ssid) | ||
| 106 | + | ||
| 107 | +private val Rect.size: Map<String, Any?> | ||
| 108 | + get() { | ||
| 109 | + // Rect.isValid can't be accessed for some reason, so just do the check manually. | ||
| 110 | + if (left <= right && top <= bottom) { | ||
| 111 | + return mapOf("width" to width().toDouble(), "height" to height().toDouble()) | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + return emptyMap() | ||
| 115 | + } |
| 1 | import UIKit | 1 | import UIKit |
| 2 | import Flutter | 2 | import Flutter |
| 3 | 3 | ||
| 4 | -@UIApplicationMain | 4 | +@main |
| 5 | @objc class AppDelegate: FlutterAppDelegate { | 5 | @objc class AppDelegate: FlutterAppDelegate { |
| 6 | override func application( | 6 | override func application( |
| 7 | _ application: UIApplication, | 7 | _ application: UIApplication, |
| 1 | import Cocoa | 1 | import Cocoa |
| 2 | import FlutterMacOS | 2 | import FlutterMacOS |
| 3 | 3 | ||
| 4 | -@NSApplicationMain | 4 | +@main |
| 5 | class AppDelegate: FlutterAppDelegate { | 5 | class AppDelegate: FlutterAppDelegate { |
| 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { | 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { |
| 7 | return true | 7 | return true |
| @@ -25,9 +25,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -25,9 +25,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 25 | /// Barcode scanner for results | 25 | /// Barcode scanner for results |
| 26 | var scanner = BarcodeScanner.barcodeScanner() | 26 | var scanner = BarcodeScanner.barcodeScanner() |
| 27 | 27 | ||
| 28 | - /// Return image buffer with the Barcode event | ||
| 29 | - var returnImage: Bool = false | ||
| 30 | - | ||
| 31 | /// Default position of camera | 28 | /// Default position of camera |
| 32 | var videoPosition: AVCaptureDevice.Position = AVCaptureDevice.Position.back | 29 | var videoPosition: AVCaptureDevice.Position = AVCaptureDevice.Position.back |
| 33 | 30 | ||
| @@ -127,7 +124,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -127,7 +124,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 127 | /// Gets called when a new image is added to the buffer | 124 | /// Gets called when a new image is added to the buffer |
| 128 | public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { | 125 | public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { |
| 129 | guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { | 126 | guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { |
| 130 | - print("Failed to get image buffer from sample buffer.") | ||
| 131 | return | 127 | return |
| 132 | } | 128 | } |
| 133 | latestBuffer = imageBuffer | 129 | latestBuffer = imageBuffer |
| @@ -160,7 +156,9 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -160,7 +156,9 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 160 | 156 | ||
| 161 | if (error == nil && barcodesString != nil && newScannedBarcodes != nil && barcodesString!.elementsEqual(newScannedBarcodes!)) { | 157 | if (error == nil && barcodesString != nil && newScannedBarcodes != nil && barcodesString!.elementsEqual(newScannedBarcodes!)) { |
| 162 | return | 158 | return |
| 163 | - } else if (newScannedBarcodes?.isEmpty == false) { | 159 | + } |
| 160 | + | ||
| 161 | + if (newScannedBarcodes?.isEmpty == false) { | ||
| 164 | barcodesString = newScannedBarcodes | 162 | barcodesString = newScannedBarcodes |
| 165 | } | 163 | } |
| 166 | } | 164 | } |
| @@ -171,7 +169,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -171,7 +169,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 171 | } | 169 | } |
| 172 | 170 | ||
| 173 | /// Start scanning for barcodes | 171 | /// Start scanning for barcodes |
| 174 | - func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws { | 172 | + func start(barcodeScannerOptions: BarcodeScannerOptions?, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws { |
| 175 | self.detectionSpeed = detectionSpeed | 173 | self.detectionSpeed = detectionSpeed |
| 176 | if (device != nil || captureSession != nil) { | 174 | if (device != nil || captureSession != nil) { |
| 177 | throw MobileScannerError.alreadyStarted | 175 | throw MobileScannerError.alreadyStarted |
| @@ -446,17 +444,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -446,17 +444,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 446 | 444 | ||
| 447 | var barcodesString: Array<String?>? | 445 | var barcodesString: Array<String?>? |
| 448 | 446 | ||
| 449 | - // /// Convert image buffer to jpeg | ||
| 450 | - // private func ciImageToJpeg(ciImage: CIImage) -> Data { | ||
| 451 | - // | ||
| 452 | - // // let ciImage = CIImage(cvPixelBuffer: latestBuffer) | ||
| 453 | - // let context:CIContext = CIContext.init(options: nil) | ||
| 454 | - // let cgImage:CGImage = context.createCGImage(ciImage, from: ciImage.extent)! | ||
| 455 | - // let uiImage:UIImage = UIImage(cgImage: cgImage, scale: 1, orientation: UIImage.Orientation.up) | ||
| 456 | - // | ||
| 457 | - // return uiImage.jpegData(compressionQuality: 0.8)! | ||
| 458 | - // } | ||
| 459 | - | ||
| 460 | /// Rotates images accordingly | 447 | /// Rotates images accordingly |
| 461 | func imageOrientation( | 448 | func imageOrientation( |
| 462 | deviceOrientation: UIDeviceOrientation, | 449 | deviceOrientation: UIDeviceOrientation, |
| @@ -11,6 +11,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -11,6 +11,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 11 | 11 | ||
| 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 | + | ||
| 15 | + /// Whether to return the input image with the barcode event. | ||
| 16 | + /// This is static to avoid accessing `self` in the callback in the constructor. | ||
| 17 | + private static var returnImage: Bool = false | ||
| 14 | 18 | ||
| 15 | /// The points for the scan window. | 19 | /// The points for the scan window. |
| 16 | static var scanWindow: [CGFloat]? | 20 | static var scanWindow: [CGFloat]? |
| @@ -37,24 +41,48 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -37,24 +41,48 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 37 | 41 | ||
| 38 | init(barcodeHandler: BarcodeHandler, registry: FlutterTextureRegistry) { | 42 | init(barcodeHandler: BarcodeHandler, registry: FlutterTextureRegistry) { |
| 39 | self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in | 43 | self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in |
| 40 | - if barcodes != nil { | ||
| 41 | - let barcodesMap: [Any?] = barcodes!.compactMap { barcode in | ||
| 42 | - if (MobileScannerPlugin.scanWindow != nil) { | ||
| 43 | - if (MobileScannerPlugin.isBarcodeInScanWindow(barcode: barcode, imageSize: image.size)) { | ||
| 44 | - return barcode.data | ||
| 45 | - } else { | ||
| 46 | - return nil | ||
| 47 | - } | ||
| 48 | - } else { | ||
| 49 | - return barcode.data | ||
| 50 | - } | 44 | + if error != nil { |
| 45 | + barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription]) | ||
| 46 | + return | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + if barcodes == nil { | ||
| 50 | + return | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + let barcodesMap: [Any?] = barcodes!.compactMap { barcode in | ||
| 54 | + if (MobileScannerPlugin.scanWindow == nil) { | ||
| 55 | + return barcode.data | ||
| 51 | } | 56 | } |
| 52 | - if (!barcodesMap.isEmpty) { | ||
| 53 | - barcodeHandler.publishEvent(["name": "barcode", "data": barcodesMap, "image": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!), "width": image.size.width, "height": image.size.height]) | 57 | + |
| 58 | + if (MobileScannerPlugin.isBarcodeInScanWindow(barcode: barcode, imageSize: image.size)) { | ||
| 59 | + return barcode.data | ||
| 54 | } | 60 | } |
| 55 | - } else if (error != nil){ | ||
| 56 | - barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription]) | 61 | + |
| 62 | + return nil | ||
| 57 | } | 63 | } |
| 64 | + | ||
| 65 | + if (barcodesMap.isEmpty) { | ||
| 66 | + return | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + if (!MobileScannerPlugin.returnImage) { | ||
| 70 | + barcodeHandler.publishEvent([ | ||
| 71 | + "name": "barcode", | ||
| 72 | + "data": barcodesMap, | ||
| 73 | + ]) | ||
| 74 | + return | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + barcodeHandler.publishEvent([ | ||
| 78 | + "name": "barcode", | ||
| 79 | + "data": barcodesMap, | ||
| 80 | + "image": [ | ||
| 81 | + "bytes": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!), | ||
| 82 | + "width": image.size.width, | ||
| 83 | + "height": image.size.height, | ||
| 84 | + ], | ||
| 85 | + ]) | ||
| 58 | }, torchModeChangeCallback: { torchState in | 86 | }, torchModeChangeCallback: { torchState in |
| 59 | barcodeHandler.publishEvent(["name": "torchState", "data": torchState]) | 87 | barcodeHandler.publishEvent(["name": "torchState", "data": torchState]) |
| 60 | }, zoomScaleChangeCallback: { zoomScale in | 88 | }, zoomScaleChangeCallback: { zoomScale in |
| @@ -104,6 +132,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -104,6 +132,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 104 | let speed: Int = (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0 | 132 | let speed: Int = (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0 |
| 105 | let timeoutMs: Int = (call.arguments as! Dictionary<String, Any?>)["timeout"] as? Int ?? 0 | 133 | let timeoutMs: Int = (call.arguments as! Dictionary<String, Any?>)["timeout"] as? Int ?? 0 |
| 106 | self.mobileScanner.timeoutSeconds = Double(timeoutMs) / Double(1000) | 134 | self.mobileScanner.timeoutSeconds = Double(timeoutMs) / Double(1000) |
| 135 | + MobileScannerPlugin.returnImage = returnImage | ||
| 107 | 136 | ||
| 108 | let formatList = formats.map { format in return BarcodeFormat(rawValue: format)} | 137 | let formatList = formats.map { format in return BarcodeFormat(rawValue: format)} |
| 109 | var barcodeOptions: BarcodeScannerOptions? = nil | 138 | var barcodeOptions: BarcodeScannerOptions? = nil |
| @@ -120,7 +149,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -120,7 +149,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 120 | let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)! | 149 | let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)! |
| 121 | 150 | ||
| 122 | do { | 151 | do { |
| 123 | - try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch, detectionSpeed: detectionSpeed) { parameters in | 152 | + try mobileScanner.start(barcodeScannerOptions: barcodeOptions, cameraPosition: position, torch: torch, detectionSpeed: detectionSpeed) { parameters in |
| 124 | DispatchQueue.main.async { | 153 | DispatchQueue.main.async { |
| 125 | result([ | 154 | result([ |
| 126 | "textureId": parameters.textureId, | 155 | "textureId": parameters.textureId, |
| @@ -257,12 +286,14 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | @@ -257,12 +286,14 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 257 | DispatchQueue.main.async { | 286 | DispatchQueue.main.async { |
| 258 | result(nil) | 287 | result(nil) |
| 259 | } | 288 | } |
| 260 | - } else { | ||
| 261 | - let barcodesMap: [Any?] = barcodes!.compactMap { barcode in barcode.data } | ||
| 262 | 289 | ||
| 263 | - DispatchQueue.main.async { | ||
| 264 | - result(["name": "barcode", "data": barcodesMap]) | ||
| 265 | - } | 290 | + return |
| 291 | + } | ||
| 292 | + | ||
| 293 | + let barcodesMap: [Any?] = barcodes!.compactMap { barcode in barcode.data } | ||
| 294 | + | ||
| 295 | + DispatchQueue.main.async { | ||
| 296 | + result(["name": "barcode", "data": barcodesMap]) | ||
| 266 | } | 297 | } |
| 267 | }) | 298 | }) |
| 268 | } | 299 | } |
| @@ -29,8 +29,27 @@ extension UIDeviceOrientation { | @@ -29,8 +29,27 @@ extension UIDeviceOrientation { | ||
| 29 | 29 | ||
| 30 | extension Barcode { | 30 | extension Barcode { |
| 31 | var data: [String: Any?] { | 31 | var data: [String: Any?] { |
| 32 | - let corners = cornerPoints?.map({$0.cgPointValue.data}) | ||
| 33 | - return ["corners": corners, "format": format.rawValue, "rawBytes": rawData, "rawValue": rawValue, "type": valueType.rawValue, "calendarEvent": calendarEvent?.data, "contactInfo": contactInfo?.data, "driverLicense": driverLicense?.data, "email": email?.data, "geoPoint": geoPoint?.data, "phone": phone?.data, "sms": sms?.data, "url": url?.data, "wifi": wifi?.data, "displayValue": displayValue] | 32 | + return [ |
| 33 | + "calendarEvent": calendarEvent?.data, | ||
| 34 | + "contactInfo": contactInfo?.data, | ||
| 35 | + "corners": cornerPoints?.map({$0.cgPointValue.data}), | ||
| 36 | + "displayValue": displayValue, | ||
| 37 | + "driverLicense": driverLicense?.data, | ||
| 38 | + "email": email?.data, | ||
| 39 | + "format": format.rawValue, | ||
| 40 | + "geoPoint": geoPoint?.data, | ||
| 41 | + "phone": phone?.data, | ||
| 42 | + "rawBytes": rawData, | ||
| 43 | + "rawValue": rawValue, | ||
| 44 | + "size": frame.isNull ? nil : [ | ||
| 45 | + "width": frame.width, | ||
| 46 | + "height": frame.height, | ||
| 47 | + ], | ||
| 48 | + "sms": sms?.data, | ||
| 49 | + "type": valueType.rawValue, | ||
| 50 | + "url": url?.data, | ||
| 51 | + "wifi": wifi?.data, | ||
| 52 | + ] | ||
| 34 | } | 53 | } |
| 35 | } | 54 | } |
| 36 | 55 |
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | # | 4 | # |
| 5 | Pod::Spec.new do |s| | 5 | Pod::Spec.new do |s| |
| 6 | s.name = 'mobile_scanner' | 6 | s.name = 'mobile_scanner' |
| 7 | - s.version = '5.2.1' | 7 | + s.version = '5.2.2' |
| 8 | s.summary = 'An universal scanner for Flutter based on MLKit.' | 8 | s.summary = 'An universal scanner for Flutter based on MLKit.' |
| 9 | s.description = <<-DESC | 9 | s.description = <<-DESC |
| 10 | An universal scanner for Flutter based on MLKit. | 10 | An universal scanner for Flutter based on MLKit. |
| @@ -3,7 +3,6 @@ import 'dart:async'; | @@ -3,7 +3,6 @@ import 'dart:async'; | ||
| 3 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; |
| 4 | import 'package:flutter/services.dart'; | 4 | import 'package:flutter/services.dart'; |
| 5 | import 'package:flutter/widgets.dart'; | 5 | import 'package:flutter/widgets.dart'; |
| 6 | -import 'package:mobile_scanner/src/enums/barcode_format.dart'; | ||
| 7 | import 'package:mobile_scanner/src/enums/mobile_scanner_authorization_state.dart'; | 6 | import 'package:mobile_scanner/src/enums/mobile_scanner_authorization_state.dart'; |
| 8 | import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart'; | 7 | import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart'; |
| 9 | import 'package:mobile_scanner/src/enums/torch_state.dart'; | 8 | import 'package:mobile_scanner/src/enums/torch_state.dart'; |
| @@ -54,31 +53,19 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | @@ -54,31 +53,19 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | ||
| 54 | final List<Map<Object?, Object?>> barcodes = | 53 | final List<Map<Object?, Object?>> barcodes = |
| 55 | data.cast<Map<Object?, Object?>>(); | 54 | data.cast<Map<Object?, Object?>>(); |
| 56 | 55 | ||
| 57 | - if (defaultTargetPlatform == TargetPlatform.macOS) { | ||
| 58 | - return BarcodeCapture( | ||
| 59 | - raw: event, | ||
| 60 | - barcodes: barcodes | ||
| 61 | - .map( | ||
| 62 | - (barcode) => Barcode( | ||
| 63 | - rawValue: barcode['payload'] as String?, | ||
| 64 | - format: BarcodeFormat.fromRawValue( | ||
| 65 | - barcode['symbology'] as int? ?? -1, | ||
| 66 | - ), | ||
| 67 | - ), | ||
| 68 | - ) | ||
| 69 | - .toList(), | ||
| 70 | - ); | ||
| 71 | - } | ||
| 72 | - | ||
| 73 | if (defaultTargetPlatform == TargetPlatform.android || | 56 | if (defaultTargetPlatform == TargetPlatform.android || |
| 74 | - defaultTargetPlatform == TargetPlatform.iOS) { | ||
| 75 | - final double? width = event['width'] as double?; | ||
| 76 | - final double? height = event['height'] as double?; | 57 | + defaultTargetPlatform == TargetPlatform.iOS || |
| 58 | + defaultTargetPlatform == TargetPlatform.macOS) { | ||
| 59 | + final Map<Object?, Object?>? imageData = | ||
| 60 | + event['image'] as Map<Object?, Object?>?; | ||
| 61 | + final Uint8List? image = imageData?['bytes'] as Uint8List?; | ||
| 62 | + final double? width = imageData?['width'] as double?; | ||
| 63 | + final double? height = imageData?['height'] as double?; | ||
| 77 | 64 | ||
| 78 | return BarcodeCapture( | 65 | return BarcodeCapture( |
| 79 | - raw: data, | 66 | + raw: event, |
| 80 | barcodes: barcodes.map(Barcode.fromNative).toList(), | 67 | barcodes: barcodes.map(Barcode.fromNative).toList(), |
| 81 | - image: event['image'] as Uint8List?, | 68 | + image: image, |
| 82 | size: width == null || height == null ? Size.zero : Size(width, height), | 69 | size: width == null || height == null ? Size.zero : Size(width, height), |
| 83 | ); | 70 | ); |
| 84 | } | 71 | } |
| @@ -154,8 +141,8 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | @@ -154,8 +141,8 @@ class MethodChannelMobileScanner extends MobileScannerPlatform { | ||
| 154 | 141 | ||
| 155 | @override | 142 | @override |
| 156 | Future<BarcodeCapture?> analyzeImage(String path) async { | 143 | Future<BarcodeCapture?> analyzeImage(String path) async { |
| 157 | - final Map<String, Object?>? result = | ||
| 158 | - await methodChannel.invokeMapMethod<String, Object?>( | 144 | + final Map<Object?, Object?>? result = |
| 145 | + await methodChannel.invokeMapMethod<Object?, Object?>( | ||
| 159 | 'analyzeImage', | 146 | 'analyzeImage', |
| 160 | path, | 147 | path, |
| 161 | ); | 148 | ); |
| @@ -80,7 +80,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | @@ -80,7 +80,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { | ||
| 80 | /// | 80 | /// |
| 81 | /// If this is false, [BarcodeCapture.image] will always be null. | 81 | /// If this is false, [BarcodeCapture.image] will always be null. |
| 82 | /// | 82 | /// |
| 83 | - /// Defaults to false, and is only supported on iOS and Android. | 83 | + /// Defaults to false, and is only supported on iOS, MacOS and Android. |
| 84 | final bool returnImage; | 84 | final bool returnImage; |
| 85 | 85 | ||
| 86 | /// Whether the flashlight should be turned on when the camera is started. | 86 | /// Whether the flashlight should be turned on when the camera is started. |
| @@ -28,6 +28,7 @@ class Barcode { | @@ -28,6 +28,7 @@ class Barcode { | ||
| 28 | this.phone, | 28 | this.phone, |
| 29 | this.rawBytes, | 29 | this.rawBytes, |
| 30 | this.rawValue, | 30 | this.rawValue, |
| 31 | + this.size = Size.zero, | ||
| 31 | this.sms, | 32 | this.sms, |
| 32 | this.type = BarcodeType.unknown, | 33 | this.type = BarcodeType.unknown, |
| 33 | this.url, | 34 | this.url, |
| @@ -38,9 +39,9 @@ class Barcode { | @@ -38,9 +39,9 @@ class Barcode { | ||
| 38 | factory Barcode.fromNative(Map<Object?, Object?> data) { | 39 | factory Barcode.fromNative(Map<Object?, Object?> data) { |
| 39 | final Map<Object?, Object?>? calendarEvent = | 40 | final Map<Object?, Object?>? calendarEvent = |
| 40 | data['calendarEvent'] as Map<Object?, Object?>?; | 41 | data['calendarEvent'] as Map<Object?, Object?>?; |
| 41 | - final List<Object?>? corners = data['corners'] as List<Object?>?; | ||
| 42 | final Map<Object?, Object?>? contactInfo = | 42 | final Map<Object?, Object?>? contactInfo = |
| 43 | data['contactInfo'] as Map<Object?, Object?>?; | 43 | data['contactInfo'] as Map<Object?, Object?>?; |
| 44 | + final List<Object?>? corners = data['corners'] as List<Object?>?; | ||
| 44 | final Map<Object?, Object?>? driverLicense = | 45 | final Map<Object?, Object?>? driverLicense = |
| 45 | data['driverLicense'] as Map<Object?, Object?>?; | 46 | data['driverLicense'] as Map<Object?, Object?>?; |
| 46 | final Map<Object?, Object?>? email = | 47 | final Map<Object?, Object?>? email = |
| @@ -50,9 +51,13 @@ class Barcode { | @@ -50,9 +51,13 @@ class Barcode { | ||
| 50 | final Map<Object?, Object?>? phone = | 51 | final Map<Object?, Object?>? phone = |
| 51 | data['phone'] as Map<Object?, Object?>?; | 52 | data['phone'] as Map<Object?, Object?>?; |
| 52 | final Map<Object?, Object?>? sms = data['sms'] as Map<Object?, Object?>?; | 53 | final Map<Object?, Object?>? sms = data['sms'] as Map<Object?, Object?>?; |
| 54 | + final Map<Object?, Object?>? size = data['size'] as Map<Object?, Object?>?; | ||
| 53 | final Map<Object?, Object?>? url = data['url'] as Map<Object?, Object?>?; | 55 | final Map<Object?, Object?>? url = data['url'] as Map<Object?, Object?>?; |
| 54 | final Map<Object?, Object?>? wifi = data['wifi'] as Map<Object?, Object?>?; | 56 | final Map<Object?, Object?>? wifi = data['wifi'] as Map<Object?, Object?>?; |
| 55 | 57 | ||
| 58 | + final double? barcodeWidth = size?['width'] as double?; | ||
| 59 | + final double? barcodeHeight = size?['height'] as double?; | ||
| 60 | + | ||
| 56 | return Barcode( | 61 | return Barcode( |
| 57 | calendarEvent: calendarEvent == null | 62 | calendarEvent: calendarEvent == null |
| 58 | ? null | 63 | ? null |
| @@ -81,6 +86,9 @@ class Barcode { | @@ -81,6 +86,9 @@ class Barcode { | ||
| 81 | phone: phone == null ? null : Phone.fromNative(phone), | 86 | phone: phone == null ? null : Phone.fromNative(phone), |
| 82 | rawBytes: data['rawBytes'] as Uint8List?, | 87 | rawBytes: data['rawBytes'] as Uint8List?, |
| 83 | rawValue: data['rawValue'] as String?, | 88 | rawValue: data['rawValue'] as String?, |
| 89 | + size: barcodeWidth == null || barcodeHeight == null | ||
| 90 | + ? Size.zero | ||
| 91 | + : Size(barcodeWidth, barcodeHeight), | ||
| 84 | sms: sms == null ? null : SMS.fromNative(sms), | 92 | sms: sms == null ? null : SMS.fromNative(sms), |
| 85 | type: BarcodeType.fromRawValue(data['type'] as int? ?? 0), | 93 | type: BarcodeType.fromRawValue(data['type'] as int? ?? 0), |
| 86 | url: url == null ? null : UrlBookmark.fromNative(url), | 94 | url: url == null ? null : UrlBookmark.fromNative(url), |
| @@ -144,6 +152,11 @@ class Barcode { | @@ -144,6 +152,11 @@ class Barcode { | ||
| 144 | /// This is null if the raw value is not available. | 152 | /// This is null if the raw value is not available. |
| 145 | final String? rawValue; | 153 | final String? rawValue; |
| 146 | 154 | ||
| 155 | + /// The size of the barcode bounding box. | ||
| 156 | + /// | ||
| 157 | + /// If the bounding box is unavailable, this will be [Size.zero]. | ||
| 158 | + final Size size; | ||
| 159 | + | ||
| 147 | /// The SMS message that is embedded in the barcode. | 160 | /// The SMS message that is embedded in the barcode. |
| 148 | final SMS? sms; | 161 | final SMS? sms; |
| 149 | 162 |
| 1 | +/// @docImport 'package:mobile_scanner/src/mobile_scanner_controller.dart'; | ||
| 2 | +library; | ||
| 3 | + | ||
| 1 | import 'dart:typed_data'; | 4 | import 'dart:typed_data'; |
| 2 | import 'dart:ui'; | 5 | import 'dart:ui'; |
| 3 | 6 | ||
| @@ -16,15 +19,21 @@ class BarcodeCapture { | @@ -16,15 +19,21 @@ class BarcodeCapture { | ||
| 16 | /// The list of scanned barcodes. | 19 | /// The list of scanned barcodes. |
| 17 | final List<Barcode> barcodes; | 20 | final List<Barcode> barcodes; |
| 18 | 21 | ||
| 19 | - /// The bytes of the image that is embedded in the barcode. | 22 | + /// The input image of the barcode capture. |
| 23 | + /// | ||
| 24 | + /// This is the image that was used to detect the available [barcodes], | ||
| 25 | + /// not the image from a specific barcode. | ||
| 20 | /// | 26 | /// |
| 21 | - /// This null if [MobileScannerController.returnImage] is false, | ||
| 22 | - /// or if there is no available image. | 27 | + /// This is always null if [MobileScannerController.returnImage] is false. |
| 23 | final Uint8List? image; | 28 | final Uint8List? image; |
| 24 | 29 | ||
| 25 | - /// The raw data of the scanned barcode. | 30 | + /// The raw data of the barcode scan. |
| 31 | + /// | ||
| 32 | + /// This is the data that was used to detect the available [barcodes], the input [image] and the [size]. | ||
| 26 | final Object? raw; | 33 | final Object? raw; |
| 27 | 34 | ||
| 28 | - /// The size of the scanned barcode. | 35 | + /// The size of the input [image]. |
| 36 | + /// | ||
| 37 | + /// If [image] is null, this will be [Size.zero]. | ||
| 29 | final Size size; | 38 | final Size size; |
| 30 | } | 39 | } |
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | # | 4 | # |
| 5 | Pod::Spec.new do |s| | 5 | Pod::Spec.new do |s| |
| 6 | s.name = 'mobile_scanner' | 6 | s.name = 'mobile_scanner' |
| 7 | - s.version = '5.2.1' | 7 | + s.version = '5.2.2' |
| 8 | s.summary = 'An universal scanner for Flutter based on MLKit.' | 8 | s.summary = 'An universal scanner for Flutter based on MLKit.' |
| 9 | s.description = <<-DESC | 9 | s.description = <<-DESC |
| 10 | An universal scanner for Flutter based on MLKit. | 10 | An universal scanner for Flutter based on MLKit. |
| @@ -25,6 +25,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -25,6 +25,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 25 | 25 | ||
| 26 | // optional window to limit scan search | 26 | // optional window to limit scan search |
| 27 | var scanWindow: CGRect? | 27 | var scanWindow: CGRect? |
| 28 | + | ||
| 29 | + /// Whether to return the input image with the barcode event. | ||
| 30 | + /// This is static to avoid accessing `self` in the `VNDetectBarcodesRequest` callback. | ||
| 31 | + private static var returnImage: Bool = false | ||
| 28 | 32 | ||
| 29 | var detectionSpeed: DetectionSpeed = DetectionSpeed.noDuplicates | 33 | var detectionSpeed: DetectionSpeed = DetectionSpeed.noDuplicates |
| 30 | 34 | ||
| @@ -32,8 +36,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -32,8 +36,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 32 | 36 | ||
| 33 | var symbologies:[VNBarcodeSymbology] = [] | 37 | var symbologies:[VNBarcodeSymbology] = [] |
| 34 | 38 | ||
| 35 | - // var analyzeMode: Int = 0 | ||
| 36 | - var analyzing: Bool = false | ||
| 37 | var position = AVCaptureDevice.Position.back | 39 | var position = AVCaptureDevice.Position.back |
| 38 | 40 | ||
| 39 | public static func register(with registrar: FlutterPluginRegistrar) { | 41 | public static func register(with registrar: FlutterPluginRegistrar) { |
| @@ -65,8 +67,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -65,8 +67,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 65 | setScale(call, result) | 67 | setScale(call, result) |
| 66 | case "resetScale": | 68 | case "resetScale": |
| 67 | resetScale(call, result) | 69 | resetScale(call, result) |
| 68 | - // case "analyze": | ||
| 69 | - // switchAnalyzeMode(call, result) | ||
| 70 | case "stop": | 70 | case "stop": |
| 71 | stop(result) | 71 | stop(result) |
| 72 | case "updateScanWindow": | 72 | case "updateScanWindow": |
| @@ -101,12 +101,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -101,12 +101,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 101 | 101 | ||
| 102 | // Gets called when a new image is added to the buffer | 102 | // Gets called when a new image is added to the buffer |
| 103 | public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { | 103 | public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { |
| 104 | - // Ignore invalid textureId | 104 | + // Ignore invalid texture id. |
| 105 | if textureId == nil { | 105 | if textureId == nil { |
| 106 | return | 106 | return |
| 107 | } | 107 | } |
| 108 | guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { | 108 | guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { |
| 109 | - print("Failed to get image buffer from sample buffer.") | ||
| 110 | return | 109 | return |
| 111 | } | 110 | } |
| 112 | latestBuffer = imageBuffer | 111 | latestBuffer = imageBuffer |
| @@ -118,7 +117,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -118,7 +117,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 118 | nextScanTime = currentTime + timeoutSeconds | 117 | nextScanTime = currentTime + timeoutSeconds |
| 119 | imagesCurrentlyBeingProcessed = true | 118 | imagesCurrentlyBeingProcessed = true |
| 120 | DispatchQueue.global(qos: .userInitiated).async { [weak self] in | 119 | DispatchQueue.global(qos: .userInitiated).async { [weak self] in |
| 121 | - if(self!.latestBuffer == nil){ | 120 | + if self!.latestBuffer == nil { |
| 122 | return | 121 | return |
| 123 | } | 122 | } |
| 124 | var cgImage: CGImage? | 123 | var cgImage: CGImage? |
| @@ -127,44 +126,57 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -127,44 +126,57 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 127 | do { | 126 | do { |
| 128 | let barcodeRequest:VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [weak self] (request, error) in | 127 | let barcodeRequest:VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [weak self] (request, error) in |
| 129 | self?.imagesCurrentlyBeingProcessed = false | 128 | self?.imagesCurrentlyBeingProcessed = false |
| 130 | - if error == nil { | ||
| 131 | - if let results = request.results as? [VNBarcodeObservation] { | ||
| 132 | - for barcode in results { | ||
| 133 | - if self?.scanWindow != nil && cgImage != nil { | ||
| 134 | - let match = self?.isBarCodeInScanWindow(self!.scanWindow!, barcode, cgImage!) ?? false | ||
| 135 | - if (!match) { | ||
| 136 | - continue | ||
| 137 | - } | ||
| 138 | - } | ||
| 139 | - | ||
| 140 | - DispatchQueue.main.async { | ||
| 141 | - self?.sink?([ | ||
| 142 | - "name": "barcode", | ||
| 143 | - "data": [ | ||
| 144 | - [ | ||
| 145 | - "payload": barcode.payloadStringValue ?? "", | ||
| 146 | - "symbology": barcode.symbology.toInt ?? -1, | ||
| 147 | - ], | ||
| 148 | - ], | ||
| 149 | - ]) | ||
| 150 | - } | ||
| 151 | - // if barcodeType == "QR" { | ||
| 152 | - // let image = CIImage(image: source) | ||
| 153 | - // image?.cropping(to: barcode.boundingBox) | ||
| 154 | - // self.qrCodeDescriptor(qrCode: barcode, qrCodeImage: image!) | ||
| 155 | - // } | ||
| 156 | - } | ||
| 157 | - } | ||
| 158 | - } else { | 129 | + |
| 130 | + if error != nil { | ||
| 159 | DispatchQueue.main.async { | 131 | DispatchQueue.main.async { |
| 160 | self?.sink?(FlutterError(code: "MobileScanner", message: error?.localizedDescription, details: nil)) | 132 | self?.sink?(FlutterError(code: "MobileScanner", message: error?.localizedDescription, details: nil)) |
| 161 | } | 133 | } |
| 134 | + return | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + guard let results: [VNBarcodeObservation] = request.results as? [VNBarcodeObservation] else { | ||
| 138 | + return | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + if results.isEmpty { | ||
| 142 | + return | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + let barcodes: [VNBarcodeObservation] = results.compactMap({ barcode in | ||
| 146 | + // If there is a scan window, check if the barcode is within said scan window. | ||
| 147 | + if self?.scanWindow != nil && cgImage != nil && !(self?.isBarCodeInScanWindow(self!.scanWindow!, barcode, cgImage!) ?? false) { | ||
| 148 | + return nil | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + return barcode | ||
| 152 | + }) | ||
| 153 | + | ||
| 154 | + DispatchQueue.main.async { | ||
| 155 | + if (!MobileScannerPlugin.returnImage) { | ||
| 156 | + self?.sink?([ | ||
| 157 | + "name": "barcode", | ||
| 158 | + "data": barcodes.map({ $0.toMap() }), | ||
| 159 | + ]) | ||
| 160 | + return | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + self?.sink?([ | ||
| 164 | + "name": "barcode", | ||
| 165 | + "data": barcodes.map({ $0.toMap() }), | ||
| 166 | + "image": cgImage == nil ? nil : [ | ||
| 167 | + "bytes": FlutterStandardTypedData(bytes: cgImage!.jpegData(compressionQuality: 0.8)!), | ||
| 168 | + "width": Double(cgImage!.width), | ||
| 169 | + "height": Double(cgImage!.height), | ||
| 170 | + ], | ||
| 171 | + ]) | ||
| 162 | } | 172 | } |
| 163 | }) | 173 | }) |
| 164 | - if(self?.symbologies.isEmpty == false){ | ||
| 165 | - // add the symbologies the user wishes to support | 174 | + |
| 175 | + if self?.symbologies.isEmpty == false { | ||
| 176 | + // Add the symbologies the user wishes to support. | ||
| 166 | barcodeRequest.symbologies = self!.symbologies | 177 | barcodeRequest.symbologies = self!.symbologies |
| 167 | } | 178 | } |
| 179 | + | ||
| 168 | try imageRequestHandler.perform([barcodeRequest]) | 180 | try imageRequestHandler.perform([barcodeRequest]) |
| 169 | } catch let e { | 181 | } catch let e { |
| 170 | DispatchQueue.main.async { | 182 | DispatchQueue.main.async { |
| @@ -265,6 +277,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -265,6 +277,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 265 | let speed:Int = argReader.int(key: "speed") ?? 0 | 277 | let speed:Int = argReader.int(key: "speed") ?? 0 |
| 266 | let timeoutMs:Int = argReader.int(key: "timeout") ?? 0 | 278 | let timeoutMs:Int = argReader.int(key: "timeout") ?? 0 |
| 267 | symbologies = argReader.toSymbology() | 279 | symbologies = argReader.toSymbology() |
| 280 | + MobileScannerPlugin.returnImage = argReader.bool(key: "returnImage") ?? false | ||
| 268 | 281 | ||
| 269 | timeoutSeconds = Double(timeoutMs) / 1000.0 | 282 | timeoutSeconds = Double(timeoutMs) / 1000.0 |
| 270 | detectionSpeed = DetectionSpeed(rawValue: speed)! | 283 | detectionSpeed = DetectionSpeed(rawValue: speed)! |
| @@ -415,11 +428,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -415,11 +428,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 415 | result(nil) | 428 | result(nil) |
| 416 | } | 429 | } |
| 417 | 430 | ||
| 418 | - // func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | ||
| 419 | - // analyzeMode = call.arguments as! Int | ||
| 420 | - // result(nil) | ||
| 421 | - // } | ||
| 422 | - | ||
| 423 | func stop(_ result: FlutterResult) { | 431 | func stop(_ result: FlutterResult) { |
| 424 | if (device == nil || captureSession == nil) { | 432 | if (device == nil || captureSession == nil) { |
| 425 | result(nil) | 433 | result(nil) |
| @@ -436,7 +444,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | @@ -436,7 +444,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, | ||
| 436 | device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) | 444 | device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) |
| 437 | registry.unregisterTexture(textureId) | 445 | registry.unregisterTexture(textureId) |
| 438 | 446 | ||
| 439 | - // analyzeMode = 0 | ||
| 440 | latestBuffer = nil | 447 | latestBuffer = nil |
| 441 | captureSession = nil | 448 | captureSession = nil |
| 442 | device = nil | 449 | device = nil |
| @@ -504,6 +511,45 @@ class MapArgumentReader { | @@ -504,6 +511,45 @@ class MapArgumentReader { | ||
| 504 | 511 | ||
| 505 | } | 512 | } |
| 506 | 513 | ||
| 514 | +extension CGImage { | ||
| 515 | + public func jpegData(compressionQuality: CGFloat) -> Data? { | ||
| 516 | + let mutableData = CFDataCreateMutable(nil, 0) | ||
| 517 | + | ||
| 518 | + let formatHint: CFString | ||
| 519 | + | ||
| 520 | + if #available(macOS 11.0, *) { | ||
| 521 | + formatHint = UTType.jpeg.identifier as CFString | ||
| 522 | + } else { | ||
| 523 | + formatHint = kUTTypeJPEG | ||
| 524 | + } | ||
| 525 | + | ||
| 526 | + guard let destination = CGImageDestinationCreateWithData(mutableData!, formatHint, 1, nil) else { | ||
| 527 | + return nil | ||
| 528 | + } | ||
| 529 | + | ||
| 530 | + let options: NSDictionary = [ | ||
| 531 | + kCGImageDestinationLossyCompressionQuality: compressionQuality, | ||
| 532 | + ] | ||
| 533 | + | ||
| 534 | + CGImageDestinationAddImage(destination, self, options) | ||
| 535 | + | ||
| 536 | + if !CGImageDestinationFinalize(destination) { | ||
| 537 | + return nil | ||
| 538 | + } | ||
| 539 | + | ||
| 540 | + return mutableData as Data? | ||
| 541 | + } | ||
| 542 | +} | ||
| 543 | + | ||
| 544 | +extension VNBarcodeObservation { | ||
| 545 | + public func toMap() -> [String: Any?] { | ||
| 546 | + return [ | ||
| 547 | + "rawValue": self.payloadStringValue ?? "", | ||
| 548 | + "format": self.symbology.toInt ?? -1, | ||
| 549 | + ] | ||
| 550 | + } | ||
| 551 | +} | ||
| 552 | + | ||
| 507 | extension VNBarcodeSymbology { | 553 | extension VNBarcodeSymbology { |
| 508 | static func fromInt(_ mapValue:Int) -> VNBarcodeSymbology? { | 554 | static func fromInt(_ mapValue:Int) -> VNBarcodeSymbology? { |
| 509 | if #available(macOS 12.0, *) { | 555 | if #available(macOS 12.0, *) { |
| 1 | name: mobile_scanner | 1 | name: mobile_scanner |
| 2 | description: A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS. | 2 | description: A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS. |
| 3 | -version: 5.2.1 | 3 | +version: 5.2.2 |
| 4 | repository: https://github.com/juliansteenbakker/mobile_scanner | 4 | repository: https://github.com/juliansteenbakker/mobile_scanner |
| 5 | 5 | ||
| 6 | screenshots: | 6 | screenshots: |
-
Please register or login to post a comment