casvanluijtelaar

updated scaling implementation

@@ -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,20 +218,6 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -184,20 +218,6 @@ 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 {
@@ -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 }
@@ -115,13 +117,12 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, @@ -115,13 +117,12 @@ public class MobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler,
115 if let results = request.results as? [VNBarcodeObservation] { 117 if let results = request.results as? [VNBarcodeObservation] {
116 for barcode in results { 118 for barcode in results {
117 if scanWindow != nil { 119 if scanWindow != nil {
118 - let boundingBox = barcode.frame  
119 - if !scanWindow!.contains(boundingBox) { 120 + let match = isbarCodeInScanWindow(scanWindow!, barcode, buffer!.image)
  121 + if (!match) {
120 continue 122 continue
121 } 123 }
122 } 124 }
123 125
124 -  
125 let barcodeType = String(barcode.symbology.rawValue).replacingOccurrences(of: "VNBarcodeSymbology", with: "") 126 let barcodeType = String(barcode.symbology.rawValue).replacingOccurrences(of: "VNBarcodeSymbology", with: "")
126 let event: [String: Any?] = ["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcodeType]] 127 let event: [String: Any?] = ["name": "barcodeMac", "data" : ["payload": barcode.payloadStringValue, "symbology": barcodeType]]
127 self.sink?(event) 128 self.sink?(event)
@@ -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