Navaron Bracke
Committed by GitHub

Merge pull request #1202 from navaronbracke/always_return_capture_size

feat: Always return capture size
@@ -5,11 +5,14 @@ Improvements: @@ -5,11 +5,14 @@ Improvements:
5 * [MacOS] Added support for `analyzeImage`. 5 * [MacOS] Added support for `analyzeImage`.
6 * [MacOS] Added a Privacy Manifest. 6 * [MacOS] Added a Privacy Manifest.
7 * [web] Added the size information to barcode results. 7 * [web] Added the size information to barcode results.
  8 +* [web] Added the video output size information to barcode capture.
8 * Added support for barcode formats to image analysis. 9 * Added support for barcode formats to image analysis.
9 * Updated the scanner to report any scanning errors that were encountered during processing. 10 * Updated the scanner to report any scanning errors that were encountered during processing.
10 * Introduced a new getter `hasCameraPermission` for the `MobileScannerState`. 11 * Introduced a new getter `hasCameraPermission` for the `MobileScannerState`.
11 * Fixed a bug in the lifecycle handling sample. Now instead of checking `isInitialized`, 12 * Fixed a bug in the lifecycle handling sample. Now instead of checking `isInitialized`,
12 the sample recommends using `hasCameraPermission`, which also guards against camera permission errors. 13 the sample recommends using `hasCameraPermission`, which also guards against camera permission errors.
  14 +* Updated the behavior of `returnImage` to only determine if the camera output bytes should be sent.
  15 +* Updated the behavior of `BarcodeCapture.size` to always be provided when available, regardless of `returnImage`.
13 16
14 Bugs fixed: 17 Bugs fixed:
15 * Fixed a bug that would cause the scanner to emit an error when it was already started. Now it ignores any calls to start while it is starting. 18 * Fixed a bug that would cause the scanner to emit an error when it was already started. Now it ignores any calls to start while it is starting.
@@ -120,7 +120,11 @@ class MobileScanner( @@ -120,7 +120,11 @@ class MobileScanner(
120 } 120 }
121 121
122 if (!returnImage) { 122 if (!returnImage) {
123 - mobileScannerCallback(barcodeMap, null, null, null) 123 + mobileScannerCallback(
  124 + barcodeMap,
  125 + null,
  126 + mediaImage.width,
  127 + mediaImage.height)
124 return@addOnSuccessListener 128 return@addOnSuccessListener
125 } 129 }
126 130
@@ -47,22 +47,17 @@ class MobileScannerHandler( @@ -47,22 +47,17 @@ class MobileScannerHandler(
47 private var analyzerResult: MethodChannel.Result? = null 47 private var analyzerResult: MethodChannel.Result? = null
48 48
49 private val callback: MobileScannerCallback = { barcodes: List<Map<String, Any?>>, image: ByteArray?, width: Int?, height: Int? -> 49 private val callback: MobileScannerCallback = { barcodes: List<Map<String, Any?>>, image: ByteArray?, width: Int?, height: Int? ->
50 - if (image != null) {  
51 - barcodeHandler.publishEvent(mapOf(  
52 - "name" to "barcode",  
53 - "data" to barcodes,  
54 - "image" to mapOf(  
55 - "bytes" to image,  
56 - "width" to width?.toDouble(),  
57 - "height" to height?.toDouble(),  
58 - )  
59 - ))  
60 - } else {  
61 - barcodeHandler.publishEvent(mapOf(  
62 - "name" to "barcode",  
63 - "data" to barcodes  
64 - ))  
65 - } 50 + barcodeHandler.publishEvent(mapOf(
  51 + "name" to "barcode",
  52 + "data" to barcodes,
  53 + // The image dimensions are always provided.
  54 + // The image bytes are only non-null when `returnImage` is true.
  55 + "image" to mapOf(
  56 + "bytes" to image,
  57 + "width" to width?.toDouble(),
  58 + "height" to height?.toDouble(),
  59 + )
  60 + ))
66 } 61 }
67 62
68 private val errorCallback: MobileScannerErrorCallback = {error: String -> 63 private val errorCallback: MobileScannerErrorCallback = {error: String ->
@@ -69,22 +69,18 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin { @@ -69,22 +69,18 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin {
69 return 69 return
70 } 70 }
71 71
72 - if (!MobileScannerPlugin.returnImage) {  
73 - barcodeHandler.publishEvent([  
74 - "name": "barcode",  
75 - "data": barcodesMap,  
76 - ])  
77 - return  
78 - } 72 + // The image dimensions are always provided.
  73 + // The image bytes are only non-null when `returnImage` is true.
  74 + let imageData: [String: Any?] = [
  75 + "bytes": MobileScannerPlugin.returnImage ? FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!) : nil,
  76 + "width": image.size.width,
  77 + "height": image.size.height,
  78 + ]
79 79
80 barcodeHandler.publishEvent([ 80 barcodeHandler.publishEvent([
81 "name": "barcode", 81 "name": "barcode",
82 "data": barcodesMap, 82 "data": barcodesMap,
83 - "image": [  
84 - "bytes": FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!),  
85 - "width": image.size.width,  
86 - "height": image.size.height,  
87 - ], 83 + "image": imageData,
88 ]) 84 ])
89 }, torchModeChangeCallback: { torchState in 85 }, torchModeChangeCallback: { torchState in
90 barcodeHandler.publishEvent(["name": "torchState", "data": torchState]) 86 barcodeHandler.publishEvent(["name": "torchState", "data": torchState])
@@ -75,8 +75,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { @@ -75,8 +75,7 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
75 /// If this is empty, all supported formats are detected. 75 /// If this is empty, all supported formats are detected.
76 final List<BarcodeFormat> formats; 76 final List<BarcodeFormat> formats;
77 77
78 - /// Whether scanned barcodes should contain the image  
79 - /// that is embedded into the barcode. 78 + /// Whether the [BarcodeCapture.image] bytes should be provided.
80 /// 79 ///
81 /// If this is false, [BarcodeCapture.image] will always be null. 80 /// If this is false, [BarcodeCapture.image] will always be null.
82 /// 81 ///
@@ -32,8 +32,6 @@ class BarcodeCapture { @@ -32,8 +32,6 @@ class BarcodeCapture {
32 /// This is the data that was used to detect the available [barcodes], the input [image] and the [size]. 32 /// This is the data that was used to detect the available [barcodes], the input [image] and the [size].
33 final Object? raw; 33 final Object? raw;
34 34
35 - /// The size of the input [image].  
36 - ///  
37 - /// If [image] is null, this will be [Size.zero]. 35 + /// The size of the camera input [image].
38 final Size size; 36 final Size size;
39 } 37 }
@@ -143,6 +143,7 @@ class MobileScannerWeb extends MobileScannerPlatform { @@ -143,6 +143,7 @@ class MobileScannerWeb extends MobileScannerPlatform {
143 final JSArray<JSString>? facingModes = capabilities.facingModeNullable; 143 final JSArray<JSString>? facingModes = capabilities.facingModeNullable;
144 144
145 // TODO: this is an empty array on MacOS Chrome, where there is no facing mode, but one, user facing camera. 145 // TODO: this is an empty array on MacOS Chrome, where there is no facing mode, but one, user facing camera.
  146 + // We might be able to add a workaround, using the label of the video track.
146 // Facing mode is not supported by this track, do nothing. 147 // Facing mode is not supported by this track, do nothing.
147 if (facingModes == null || facingModes.toDart.isEmpty) { 148 if (facingModes == null || facingModes.toDart.isEmpty) {
148 return; 149 return;
@@ -121,6 +121,7 @@ final class ZXingBarcodeReader extends BarcodeReader { @@ -121,6 +121,7 @@ final class ZXingBarcodeReader extends BarcodeReader {
121 controller.add( 121 controller.add(
122 BarcodeCapture( 122 BarcodeCapture(
123 barcodes: [result.toBarcode], 123 barcodes: [result.toBarcode],
  124 + size: videoSize,
124 ), 125 ),
125 ); 126 );
126 } 127 }
@@ -156,22 +156,26 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -156,22 +156,26 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
156 }) 156 })
157 157
158 DispatchQueue.main.async { 158 DispatchQueue.main.async {
159 - if (!MobileScannerPlugin.returnImage) { 159 + guard let image = cgImage else {
160 self?.sink?([ 160 self?.sink?([
161 "name": "barcode", 161 "name": "barcode",
162 "data": barcodes.map({ $0.toMap() }), 162 "data": barcodes.map({ $0.toMap() }),
163 ]) 163 ])
164 return 164 return
165 } 165 }
166 - 166 +
  167 + // The image dimensions are always provided.
  168 + // The image bytes are only non-null when `returnImage` is true.
  169 + let imageData: [String: Any?] = [
  170 + "bytes": MobileScannerPlugin.returnImage ? FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 0.8)!) : nil,
  171 + "width": Double(image.width),
  172 + "height": Double(image.height),
  173 + ]
  174 +
167 self?.sink?([ 175 self?.sink?([
168 "name": "barcode", 176 "name": "barcode",
169 "data": barcodes.map({ $0.toMap() }), 177 "data": barcodes.map({ $0.toMap() }),
170 - "image": cgImage == nil ? nil : [  
171 - "bytes": FlutterStandardTypedData(bytes: cgImage!.jpegData(compressionQuality: 0.8)!),  
172 - "width": Double(cgImage!.width),  
173 - "height": Double(cgImage!.height),  
174 - ], 178 + "image": imageData,
175 ]) 179 ])
176 } 180 }
177 }) 181 })