Navaron Bracke
Committed by GitHub

Merge pull request #1169 from navaronbracke/size_fix_android

fix: Fix discrepancy between barcode bounding box and barcode capture size
## 5.2.2
Improvements:
* [MacOS] Adds Swift Package Manager support.
* [MacOS] Adds support for `returnImage`.
* Added a new `size` property to `Barcode`, that denotes the bounding box of the barcode.
Bugs fixed:
* Fixed some documentation errors for the `size` and `image` of `BarcodeCapture`.
* [iOS] Fixed a bug with `returnImage`.
* [Android/iOS] Adjusted the raw barcode scan value to pass the raw event data, like on MacOS.
## 5.2.1
* Updates the `package:web` dependency to use a version range.
... ... @@ -44,7 +56,7 @@ Improvements:
This major release contains all the changes from the 5.0.0 beta releases, along with the following changes:
Improvements:
- [Android] Remove the Kotlin Standard Library from the dependencies, as it is automatically included in Kotlin 1.4+
* [Android] Remove the Kotlin Standard Library from the dependencies, as it is automatically included in Kotlin 1.4+
## 5.0.0-beta.3
**BREAKING CHANGES:**
... ...
... ... @@ -50,9 +50,11 @@ class MobileScannerHandler(
barcodeHandler.publishEvent(mapOf(
"name" to "barcode",
"data" to barcodes,
"image" to image,
"width" to width!!.toDouble(),
"height" to height!!.toDouble()
"image" to mapOf(
"bytes" to image,
"width" to width?.toDouble(),
"height" to height?.toDouble(),
)
))
} else {
barcodeHandler.publishEvent(mapOf(
... ...
... ... @@ -28,12 +28,22 @@ fun Image.toByteArray(): ByteArray {
val Barcode.data: Map<String, Any?>
get() = mapOf(
"corners" to cornerPoints?.map { corner -> corner.data }, "format" to format,
"rawBytes" to rawBytes, "rawValue" to rawValue, "type" to valueType,
"calendarEvent" to calendarEvent?.data, "contactInfo" to contactInfo?.data,
"driverLicense" to driverLicense?.data, "email" to email?.data,
"geoPoint" to geoPoint?.data, "phone" to phone?.data, "sms" to sms?.data,
"url" to url?.data, "wifi" to wifi?.data, "displayValue" to displayValue
"calendarEvent" to calendarEvent?.data,
"contactInfo" to contactInfo?.data,
"corners" to cornerPoints?.map { corner -> corner.data },
"displayValue" to displayValue,
"driverLicense" to driverLicense?.data,
"email" to email?.data,
"format" to format,
"geoPoint" to geoPoint?.data,
"phone" to phone?.data,
"rawBytes" to rawBytes,
"rawValue" to rawValue,
"size" to boundingBox?.size,
"sms" to sms?.data,
"type" to valueType,
"url" to url?.data,
"wifi" to wifi?.data,
)
private val Point.data: Map<String, Double>
... ... @@ -92,4 +102,14 @@ private val Barcode.UrlBookmark.data: Map<String, Any?>
get() = mapOf("title" to title, "url" to url)
private val Barcode.WiFi.data: Map<String, Any?>
get() = mapOf("encryptionType" to encryptionType, "password" to password, "ssid" to ssid)
\ No newline at end of file
get() = mapOf("encryptionType" to encryptionType, "password" to password, "ssid" to ssid)
private val Rect.size: Map<String, Any?>
get() {
// Rect.isValid can't be accessed for some reason, so just do the check manually.
if (left <= right && top <= bottom) {
return mapOf("width" to width().toDouble(), "height" to height().toDouble())
}
return emptyMap()
}
\ No newline at end of file
... ...
import UIKit
import Flutter
@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
... ...
import Cocoa
import FlutterMacOS
@NSApplicationMain
@main
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
... ...
... ... @@ -25,9 +25,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
/// Barcode scanner for results
var scanner = BarcodeScanner.barcodeScanner()
/// Return image buffer with the Barcode event
var returnImage: Bool = false
/// Default position of camera
var videoPosition: AVCaptureDevice.Position = AVCaptureDevice.Position.back
... ... @@ -127,7 +124,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
/// Gets called when a new image is added to the buffer
public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
print("Failed to get image buffer from sample buffer.")
return
}
latestBuffer = imageBuffer
... ... @@ -160,7 +156,9 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
if (error == nil && barcodesString != nil && newScannedBarcodes != nil && barcodesString!.elementsEqual(newScannedBarcodes!)) {
return
} else if (newScannedBarcodes?.isEmpty == false) {
}
if (newScannedBarcodes?.isEmpty == false) {
barcodesString = newScannedBarcodes
}
}
... ... @@ -171,7 +169,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
}
/// Start scanning for barcodes
func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws {
func start(barcodeScannerOptions: BarcodeScannerOptions?, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws {
self.detectionSpeed = detectionSpeed
if (device != nil || captureSession != nil) {
throw MobileScannerError.alreadyStarted
... ... @@ -446,17 +444,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega
var barcodesString: Array<String?>?
// /// Convert image buffer to jpeg
// private func ciImageToJpeg(ciImage: CIImage) -> Data {
//
// // let ciImage = CIImage(cvPixelBuffer: latestBuffer)
// let context:CIContext = CIContext.init(options: nil)
// let cgImage:CGImage = context.createCGImage(ciImage, from: ciImage.extent)!
// let uiImage:UIImage = UIImage(cgImage: cgImage, scale: 1, orientation: UIImage.Orientation.up)
//
// return uiImage.jpegData(compressionQuality: 0.8)!
// }
/// Rotates images accordingly
func imageOrientation(
deviceOrientation: UIDeviceOrientation,
... ...
... ... @@ -11,6 +11,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
/// The handler sends all information via an event channel back to Flutter
private let barcodeHandler: BarcodeHandler
/// Whether to return the input image with the barcode event.
/// This is static to avoid accessing `self` in the callback in the constructor.
private static var returnImage: Bool = false
/// The points for the scan window.
static var scanWindow: [CGFloat]?
... ... @@ -37,24 +41,48 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
init(barcodeHandler: BarcodeHandler, registry: FlutterTextureRegistry) {
self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in
if barcodes != nil {
let barcodesMap: [Any?] = barcodes!.compactMap { barcode in
if (MobileScannerPlugin.scanWindow != nil) {
if (MobileScannerPlugin.isBarcodeInScanWindow(barcode: barcode, imageSize: image.size)) {
return barcode.data
} else {
return nil
}
} else {
return barcode.data
}
if error != nil {
barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription])
return
}
if barcodes == nil {
return
}
let barcodesMap: [Any?] = barcodes!.compactMap { barcode in
if (MobileScannerPlugin.scanWindow == nil) {
return barcode.data
}
if (!barcodesMap.isEmpty) {
barcodeHandler.publishEvent(["name": "barcode", "data": barcodesMap, "image": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!), "width": image.size.width, "height": image.size.height])
if (MobileScannerPlugin.isBarcodeInScanWindow(barcode: barcode, imageSize: image.size)) {
return barcode.data
}
} else if (error != nil){
barcodeHandler.publishEvent(["name": "error", "data": error!.localizedDescription])
return nil
}
if (barcodesMap.isEmpty) {
return
}
if (!MobileScannerPlugin.returnImage) {
barcodeHandler.publishEvent([
"name": "barcode",
"data": barcodesMap,
])
return
}
barcodeHandler.publishEvent([
"name": "barcode",
"data": barcodesMap,
"image": [
"bytes": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!),
"width": image.size.width,
"height": image.size.height,
],
])
}, torchModeChangeCallback: { torchState in
barcodeHandler.publishEvent(["name": "torchState", "data": torchState])
}, zoomScaleChangeCallback: { zoomScale in
... ... @@ -104,6 +132,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
let speed: Int = (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0
let timeoutMs: Int = (call.arguments as! Dictionary<String, Any?>)["timeout"] as? Int ?? 0
self.mobileScanner.timeoutSeconds = Double(timeoutMs) / Double(1000)
MobileScannerPlugin.returnImage = returnImage
let formatList = formats.map { format in return BarcodeFormat(rawValue: format)}
var barcodeOptions: BarcodeScannerOptions? = nil
... ... @@ -120,7 +149,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)!
do {
try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch, detectionSpeed: detectionSpeed) { parameters in
try mobileScanner.start(barcodeScannerOptions: barcodeOptions, cameraPosition: position, torch: torch, detectionSpeed: detectionSpeed) { parameters in
DispatchQueue.main.async {
result([
"textureId": parameters.textureId,
... ... @@ -257,12 +286,14 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
DispatchQueue.main.async {
result(nil)
}
} else {
let barcodesMap: [Any?] = barcodes!.compactMap { barcode in barcode.data }
DispatchQueue.main.async {
result(["name": "barcode", "data": barcodesMap])
}
return
}
let barcodesMap: [Any?] = barcodes!.compactMap { barcode in barcode.data }
DispatchQueue.main.async {
result(["name": "barcode", "data": barcodesMap])
}
})
}
... ...
... ... @@ -29,8 +29,27 @@ extension UIDeviceOrientation {
extension Barcode {
var data: [String: Any?] {
let corners = cornerPoints?.map({$0.cgPointValue.data})
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]
return [
"calendarEvent": calendarEvent?.data,
"contactInfo": contactInfo?.data,
"corners": cornerPoints?.map({$0.cgPointValue.data}),
"displayValue": displayValue,
"driverLicense": driverLicense?.data,
"email": email?.data,
"format": format.rawValue,
"geoPoint": geoPoint?.data,
"phone": phone?.data,
"rawBytes": rawData,
"rawValue": rawValue,
"size": frame.isNull ? nil : [
"width": frame.width,
"height": frame.height,
],
"sms": sms?.data,
"type": valueType.rawValue,
"url": url?.data,
"wifi": wifi?.data,
]
}
}
... ...
... ... @@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'mobile_scanner'
s.version = '5.2.1'
s.version = '5.2.2'
s.summary = 'An universal scanner for Flutter based on MLKit.'
s.description = <<-DESC
An universal scanner for Flutter based on MLKit.
... ...
... ... @@ -3,7 +3,6 @@ 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';
... ... @@ -54,31 +53,19 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
final List<Map<Object?, Object?>> barcodes =
data.cast<Map<Object?, Object?>>();
if (defaultTargetPlatform == TargetPlatform.macOS) {
return BarcodeCapture(
raw: event,
barcodes: barcodes
.map(
(barcode) => Barcode(
rawValue: barcode['payload'] as String?,
format: BarcodeFormat.fromRawValue(
barcode['symbology'] as int? ?? -1,
),
),
)
.toList(),
);
}
if (defaultTargetPlatform == TargetPlatform.android ||
defaultTargetPlatform == TargetPlatform.iOS) {
final double? width = event['width'] as double?;
final double? height = event['height'] as double?;
defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS) {
final Map<Object?, Object?>? imageData =
event['image'] as Map<Object?, Object?>?;
final Uint8List? image = imageData?['bytes'] as Uint8List?;
final double? width = imageData?['width'] as double?;
final double? height = imageData?['height'] as double?;
return BarcodeCapture(
raw: data,
raw: event,
barcodes: barcodes.map(Barcode.fromNative).toList(),
image: event['image'] as Uint8List?,
image: image,
size: width == null || height == null ? Size.zero : Size(width, height),
);
}
... ... @@ -154,8 +141,8 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
@override
Future<BarcodeCapture?> analyzeImage(String path) async {
final Map<String, Object?>? result =
await methodChannel.invokeMapMethod<String, Object?>(
final Map<Object?, Object?>? result =
await methodChannel.invokeMapMethod<Object?, Object?>(
'analyzeImage',
path,
);
... ...
... ... @@ -80,7 +80,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
///
/// If this is false, [BarcodeCapture.image] will always be null.
///
/// Defaults to false, and is only supported on iOS and Android.
/// Defaults to false, and is only supported on iOS, MacOS and Android.
final bool returnImage;
/// Whether the flashlight should be turned on when the camera is started.
... ...
... ... @@ -28,6 +28,7 @@ class Barcode {
this.phone,
this.rawBytes,
this.rawValue,
this.size = Size.zero,
this.sms,
this.type = BarcodeType.unknown,
this.url,
... ... @@ -38,9 +39,9 @@ class Barcode {
factory Barcode.fromNative(Map<Object?, Object?> data) {
final Map<Object?, Object?>? calendarEvent =
data['calendarEvent'] as Map<Object?, Object?>?;
final List<Object?>? corners = data['corners'] as List<Object?>?;
final Map<Object?, Object?>? contactInfo =
data['contactInfo'] as Map<Object?, Object?>?;
final List<Object?>? corners = data['corners'] as List<Object?>?;
final Map<Object?, Object?>? driverLicense =
data['driverLicense'] as Map<Object?, Object?>?;
final Map<Object?, Object?>? email =
... ... @@ -50,9 +51,13 @@ class Barcode {
final Map<Object?, Object?>? phone =
data['phone'] as Map<Object?, Object?>?;
final Map<Object?, Object?>? sms = data['sms'] as Map<Object?, Object?>?;
final Map<Object?, Object?>? size = data['size'] as Map<Object?, Object?>?;
final Map<Object?, Object?>? url = data['url'] as Map<Object?, Object?>?;
final Map<Object?, Object?>? wifi = data['wifi'] as Map<Object?, Object?>?;
final double? barcodeWidth = size?['width'] as double?;
final double? barcodeHeight = size?['height'] as double?;
return Barcode(
calendarEvent: calendarEvent == null
? null
... ... @@ -81,6 +86,9 @@ class Barcode {
phone: phone == null ? null : Phone.fromNative(phone),
rawBytes: data['rawBytes'] as Uint8List?,
rawValue: data['rawValue'] as String?,
size: barcodeWidth == null || barcodeHeight == null
? Size.zero
: Size(barcodeWidth, barcodeHeight),
sms: sms == null ? null : SMS.fromNative(sms),
type: BarcodeType.fromRawValue(data['type'] as int? ?? 0),
url: url == null ? null : UrlBookmark.fromNative(url),
... ... @@ -144,6 +152,11 @@ class Barcode {
/// This is null if the raw value is not available.
final String? rawValue;
/// The size of the barcode bounding box.
///
/// If the bounding box is unavailable, this will be [Size.zero].
final Size size;
/// The SMS message that is embedded in the barcode.
final SMS? sms;
... ...
/// @docImport 'package:mobile_scanner/src/mobile_scanner_controller.dart';
library;
import 'dart:typed_data';
import 'dart:ui';
... ... @@ -16,15 +19,21 @@ class BarcodeCapture {
/// The list of scanned barcodes.
final List<Barcode> barcodes;
/// The bytes of the image that is embedded in the barcode.
/// The input image of the barcode capture.
///
/// This is the image that was used to detect the available [barcodes],
/// not the image from a specific barcode.
///
/// This null if [MobileScannerController.returnImage] is false,
/// or if there is no available image.
/// This is always null if [MobileScannerController.returnImage] is false.
final Uint8List? image;
/// The raw data of the scanned barcode.
/// The raw data of the barcode scan.
///
/// This is the data that was used to detect the available [barcodes], the input [image] and the [size].
final Object? raw;
/// The size of the scanned barcode.
/// The size of the input [image].
///
/// If [image] is null, this will be [Size.zero].
final Size size;
}
... ...
... ... @@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'mobile_scanner'
s.version = '5.2.1'
s.version = '5.2.2'
s.summary = 'An universal scanner for Flutter based on MLKit.'
s.description = <<-DESC
An universal scanner for Flutter based on MLKit.
... ...
... ... @@ -25,6 +25,10 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
// optional window to limit scan search
var scanWindow: CGRect?
/// Whether to return the input image with the barcode event.
/// This is static to avoid accessing `self` in the `VNDetectBarcodesRequest` callback.
private static var returnImage: Bool = false
var detectionSpeed: DetectionSpeed = DetectionSpeed.noDuplicates
... ... @@ -32,8 +36,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
var symbologies:[VNBarcodeSymbology] = []
// var analyzeMode: Int = 0
var analyzing: Bool = false
var position = AVCaptureDevice.Position.back
public static func register(with registrar: FlutterPluginRegistrar) {
... ... @@ -65,8 +67,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
setScale(call, result)
case "resetScale":
resetScale(call, result)
// case "analyze":
// switchAnalyzeMode(call, result)
case "stop":
stop(result)
case "updateScanWindow":
... ... @@ -101,12 +101,11 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
// Gets called when a new image is added to the buffer
public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// Ignore invalid textureId
// Ignore invalid texture id.
if textureId == nil {
return
}
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
print("Failed to get image buffer from sample buffer.")
return
}
latestBuffer = imageBuffer
... ... @@ -118,7 +117,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
nextScanTime = currentTime + timeoutSeconds
imagesCurrentlyBeingProcessed = true
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
if(self!.latestBuffer == nil){
if self!.latestBuffer == nil {
return
}
var cgImage: CGImage?
... ... @@ -127,44 +126,57 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
do {
let barcodeRequest:VNDetectBarcodesRequest = VNDetectBarcodesRequest(completionHandler: { [weak self] (request, error) in
self?.imagesCurrentlyBeingProcessed = false
if error == nil {
if let results = request.results as? [VNBarcodeObservation] {
for barcode in results {
if self?.scanWindow != nil && cgImage != nil {
let match = self?.isBarCodeInScanWindow(self!.scanWindow!, barcode, cgImage!) ?? false
if (!match) {
continue
}
}
DispatchQueue.main.async {
self?.sink?([
"name": "barcode",
"data": [
[
"payload": barcode.payloadStringValue ?? "",
"symbology": barcode.symbology.toInt ?? -1,
],
],
])
}
// if barcodeType == "QR" {
// let image = CIImage(image: source)
// image?.cropping(to: barcode.boundingBox)
// self.qrCodeDescriptor(qrCode: barcode, qrCodeImage: image!)
// }
}
}
} else {
if error != nil {
DispatchQueue.main.async {
self?.sink?(FlutterError(code: "MobileScanner", message: error?.localizedDescription, details: nil))
}
return
}
guard let results: [VNBarcodeObservation] = request.results as? [VNBarcodeObservation] else {
return
}
if results.isEmpty {
return
}
let barcodes: [VNBarcodeObservation] = results.compactMap({ barcode in
// If there is a scan window, check if the barcode is within said scan window.
if self?.scanWindow != nil && cgImage != nil && !(self?.isBarCodeInScanWindow(self!.scanWindow!, barcode, cgImage!) ?? false) {
return nil
}
return barcode
})
DispatchQueue.main.async {
if (!MobileScannerPlugin.returnImage) {
self?.sink?([
"name": "barcode",
"data": barcodes.map({ $0.toMap() }),
])
return
}
self?.sink?([
"name": "barcode",
"data": barcodes.map({ $0.toMap() }),
"image": cgImage == nil ? nil : [
"bytes": FlutterStandardTypedData(bytes: cgImage!.jpegData(compressionQuality: 0.8)!),
"width": Double(cgImage!.width),
"height": Double(cgImage!.height),
],
])
}
})
if(self?.symbologies.isEmpty == false){
// add the symbologies the user wishes to support
if self?.symbologies.isEmpty == false {
// Add the symbologies the user wishes to support.
barcodeRequest.symbologies = self!.symbologies
}
try imageRequestHandler.perform([barcodeRequest])
} catch let e {
DispatchQueue.main.async {
... ... @@ -265,6 +277,7 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
let speed:Int = argReader.int(key: "speed") ?? 0
let timeoutMs:Int = argReader.int(key: "timeout") ?? 0
symbologies = argReader.toSymbology()
MobileScannerPlugin.returnImage = argReader.bool(key: "returnImage") ?? false
timeoutSeconds = Double(timeoutMs) / 1000.0
detectionSpeed = DetectionSpeed(rawValue: speed)!
... ... @@ -415,11 +428,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
result(nil)
}
// func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
// analyzeMode = call.arguments as! Int
// result(nil)
// }
func stop(_ result: FlutterResult) {
if (device == nil || captureSession == nil) {
result(nil)
... ... @@ -436,7 +444,6 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode))
registry.unregisterTexture(textureId)
// analyzeMode = 0
latestBuffer = nil
captureSession = nil
device = nil
... ... @@ -504,6 +511,45 @@ class MapArgumentReader {
}
extension CGImage {
public func jpegData(compressionQuality: CGFloat) -> Data? {
let mutableData = CFDataCreateMutable(nil, 0)
let formatHint: CFString
if #available(macOS 11.0, *) {
formatHint = UTType.jpeg.identifier as CFString
} else {
formatHint = kUTTypeJPEG
}
guard let destination = CGImageDestinationCreateWithData(mutableData!, formatHint, 1, nil) else {
return nil
}
let options: NSDictionary = [
kCGImageDestinationLossyCompressionQuality: compressionQuality,
]
CGImageDestinationAddImage(destination, self, options)
if !CGImageDestinationFinalize(destination) {
return nil
}
return mutableData as Data?
}
}
extension VNBarcodeObservation {
public func toMap() -> [String: Any?] {
return [
"rawValue": self.payloadStringValue ?? "",
"format": self.symbology.toInt ?? -1,
]
}
}
extension VNBarcodeSymbology {
static func fromInt(_ mapValue:Int) -> VNBarcodeSymbology? {
if #available(macOS 12.0, *) {
... ...
name: mobile_scanner
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.
version: 5.2.1
version: 5.2.2
repository: https://github.com/juliansteenbakker/mobile_scanner
screenshots:
... ...