casvanluijtelaar

fixed Boxfit.cover scanwindow alignment

@@ -166,7 +166,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -166,7 +166,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
166 result.success(answer) 166 result.success(answer)
167 } else { 167 } else {
168 val facing: Int = call.argument<Int>("facing") ?: 0 168 val facing: Int = call.argument<Int>("facing") ?: 0
169 - val ratio: Int? = call.argument<Int>("ratio") 169 + val ratio: Int = call.argument<Int>("ratio") ?: 1
170 val torch: Boolean = call.argument<Boolean>("torch") ?: false 170 val torch: Boolean = call.argument<Boolean>("torch") ?: false
171 val formats: List<Int>? = call.argument<List<Int>>("formats") 171 val formats: List<Int>? = call.argument<List<Int>>("formats")
172 172
@@ -197,6 +197,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -197,6 +197,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
197 result.error("textureEntry", "textureEntry is null", null) 197 result.error("textureEntry", "textureEntry is null", null)
198 return@addListener 198 return@addListener
199 } 199 }
  200 +
200 // Preview 201 // Preview
201 val surfaceProvider = Preview.SurfaceProvider { request -> 202 val surfaceProvider = Preview.SurfaceProvider { request ->
202 val texture = textureEntry!!.surfaceTexture() 203 val texture = textureEntry!!.surfaceTexture()
@@ -207,17 +208,15 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -207,17 +208,15 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
207 208
208 // Build the preview to be shown on the Flutter texture 209 // Build the preview to be shown on the Flutter texture
209 val previewBuilder = Preview.Builder() 210 val previewBuilder = Preview.Builder()
210 - if (ratio != null) {  
211 - previewBuilder.setTargetAspectRatio(ratio)  
212 - } 211 + .setTargetAspectRatio(ratio)
  212 +
213 preview = previewBuilder.build().apply { setSurfaceProvider(surfaceProvider) } 213 preview = previewBuilder.build().apply { setSurfaceProvider(surfaceProvider) }
214 214
215 // Build the analyzer to be passed on to MLKit 215 // Build the analyzer to be passed on to MLKit
216 val analysisBuilder = ImageAnalysis.Builder() 216 val analysisBuilder = ImageAnalysis.Builder()
217 .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) 217 .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
218 - .setTargetResolution(preview!!.resolutionInfo?.resolution ?: Size(640, 480)) 218 + .setTargetAspectRatio(ratio)
219 219
220 - if (ratio != null) { analysisBuilder.setTargetAspectRatio(ratio) }  
221 val analysis = analysisBuilder.build().apply { setAnalyzer(executor, analyzer) } 220 val analysis = analysisBuilder.build().apply { setAnalyzer(executor, analyzer) }
222 221
223 // Select the correct camera 222 // Select the correct camera
@@ -227,7 +226,6 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -227,7 +226,6 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
227 226
228 val analysisSize = analysis.resolutionInfo?.resolution ?: Size(0, 0) 227 val analysisSize = analysis.resolutionInfo?.resolution ?: Size(0, 0)
229 val previewSize = preview!!.resolutionInfo?.resolution ?: Size(0, 0) 228 val previewSize = preview!!.resolutionInfo?.resolution ?: Size(0, 0)
230 -  
231 229
232 Log.i("LOG", "Analyzer: $analysisSize") 230 Log.i("LOG", "Analyzer: $analysisSize")
233 Log.i("LOG", "Preview: $previewSize") 231 Log.i("LOG", "Preview: $previewSize")
@@ -22,10 +22,16 @@ class _BarcodeScannerWithScanWindowState @@ -22,10 +22,16 @@ class _BarcodeScannerWithScanWindowState
22 } 22 }
23 23
24 Future<void> restart() async { 24 Future<void> restart() async {
25 - await controller.stop(); 25 + // await controller.stop();
26 await controller.start(); 26 await controller.start();
27 } 27 }
28 28
  29 + Future<void> onDetect(Barcode barcode, MobileScannerArguments? _) async {
  30 + setState(() => this.barcode = barcode.rawValue);
  31 + await Future.delayed(const Duration(seconds: 1));
  32 + setState(() => this.barcode = '');
  33 + }
  34 +
29 @override 35 @override
30 Widget build(BuildContext context) { 36 Widget build(BuildContext context) {
31 final scanWindow = Rect.fromCenter( 37 final scanWindow = Rect.fromCenter(
@@ -41,12 +47,11 @@ class _BarcodeScannerWithScanWindowState @@ -41,12 +47,11 @@ class _BarcodeScannerWithScanWindowState
41 return Stack( 47 return Stack(
42 children: [ 48 children: [
43 MobileScanner( 49 MobileScanner(
44 - fit: BoxFit.contain, 50 + fit: BoxFit.cover,
45 scanWindow: scanWindow, 51 scanWindow: scanWindow,
46 controller: controller, 52 controller: controller,
47 - onDetect: (barcode, _) => setState(() {  
48 - this.barcode = barcode.rawValue;  
49 - }), 53 + onDetect: onDetect,
  54 + allowDuplicates: true,
50 ), 55 ),
51 CustomPaint( 56 CustomPaint(
52 painter: ScannerOverlay(scanWindow), 57 painter: ScannerOverlay(scanWindow),
1 import 'package:flutter/foundation.dart'; 1 import 'package:flutter/foundation.dart';
2 -import 'package:flutter/material.dart'; 2 +import 'package:flutter/material.dart' hide applyBoxFit;
3 import 'package:mobile_scanner/mobile_scanner.dart'; 3 import 'package:mobile_scanner/mobile_scanner.dart';
  4 +import 'package:mobile_scanner/src/objects/barcode_utility.dart';
4 5
5 enum Ratio { ratio_4_3, ratio_16_9 } 6 enum Ratio { ratio_4_3, ratio_16_9 }
6 7
@@ -89,22 +90,17 @@ class _MobileScannerState extends State<MobileScanner> @@ -89,22 +90,17 @@ class _MobileScannerState extends State<MobileScanner>
89 Size textureSize, 90 Size textureSize,
90 Size widgetSize, 91 Size widgetSize,
91 ) { 92 ) {
92 -  
93 /// map the texture size to get its new size after fitted to screen 93 /// map the texture size to get its new size after fitted to screen
94 - final fittedSizes = applyBoxFit(fit, textureSize, widgetSize);  
95 - final fittedTextureSize = fittedSizes.destination; 94 + final fittedTextureSize = applyBoxFit(fit, textureSize, widgetSize);
96 95
97 /// create a new rectangle that represents the texture on the screen 96 /// create a new rectangle that represents the texture on the screen
98 final minX = widgetSize.width / 2 - fittedTextureSize.width / 2; 97 final minX = widgetSize.width / 2 - fittedTextureSize.width / 2;
99 final minY = widgetSize.height / 2 - fittedTextureSize.height / 2; 98 final minY = widgetSize.height / 2 - fittedTextureSize.height / 2;
100 - final width = fittedTextureSize.width;  
101 - final height = fittedTextureSize.height;  
102 - final textureWindow = Rect.fromLTWH(minX, minY, width, height); 99 + final textureWindow = Offset(minX, minY) & fittedTextureSize;
103 100
104 /// create a new scan window and with only the area of the rect intersecting the texture window 101 /// create a new scan window and with only the area of the rect intersecting the texture window
105 final scanWindowInTexture = scanWindow.intersect(textureWindow); 102 final scanWindowInTexture = scanWindow.intersect(textureWindow);
106 103
107 -  
108 /// update the scanWindow left and top to be relative to the texture not the widget 104 /// update the scanWindow left and top to be relative to the texture not the widget
109 final newLeft = scanWindowInTexture.left - textureWindow.left; 105 final newLeft = scanWindowInTexture.left - textureWindow.left;
110 final newTop = scanWindowInTexture.top - textureWindow.top; 106 final newTop = scanWindowInTexture.top - textureWindow.top;
@@ -114,21 +110,12 @@ class _MobileScannerState extends State<MobileScanner> @@ -114,21 +110,12 @@ class _MobileScannerState extends State<MobileScanner>
114 /// new scanWindow that is adapted to the boxfit and relative to the texture 110 /// new scanWindow that is adapted to the boxfit and relative to the texture
115 final windowInTexture = Rect.fromLTWH(newLeft, newTop, newWidth, newHeight); 111 final windowInTexture = Rect.fromLTWH(newLeft, newTop, newWidth, newHeight);
116 112
117 - print(windowInTexture);  
118 -  
119 /// get the scanWindow as a percentage of the texture 113 /// get the scanWindow as a percentage of the texture
120 final percentageLeft = windowInTexture.left / fittedTextureSize.width; 114 final percentageLeft = windowInTexture.left / fittedTextureSize.width;
121 final percentageTop = windowInTexture.top / fittedTextureSize.height; 115 final percentageTop = windowInTexture.top / fittedTextureSize.height;
122 final percentageRight = windowInTexture.right / fittedTextureSize.width; 116 final percentageRight = windowInTexture.right / fittedTextureSize.width;
123 final percentagebottom = windowInTexture.bottom / fittedTextureSize.height; 117 final percentagebottom = windowInTexture.bottom / fittedTextureSize.height;
124 118
125 - print(Rect.fromLTRB(  
126 - percentageLeft,  
127 - percentageTop,  
128 - percentageRight,  
129 - percentagebottom,  
130 - ));  
131 -  
132 /// this rectangle can be send to native code and used to cut out a rectangle of the scan image 119 /// this rectangle can be send to native code and used to cut out a rectangle of the scan image
133 return Rect.fromLTRB( 120 return Rect.fromLTRB(
134 percentageLeft, 121 percentageLeft,
  1 +import 'dart:math' as math;
  2 +
1 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
2 import 'package:mobile_scanner/mobile_scanner.dart'; 4 import 'package:mobile_scanner/mobile_scanner.dart';
3 5
@@ -147,3 +149,79 @@ WiFi? toWiFi(Map? data) { @@ -147,3 +149,79 @@ WiFi? toWiFi(Map? data) {
147 return null; 149 return null;
148 } 150 }
149 } 151 }
  152 +
  153 +Size applyBoxFit(BoxFit fit, Size input, Size output) {
  154 + if (input.height <= 0.0 ||
  155 + input.width <= 0.0 ||
  156 + output.height <= 0.0 ||
  157 + output.width <= 0.0) {
  158 + return Size.zero;
  159 + }
  160 +
  161 + Size destination;
  162 +
  163 + final inputAspectRatio = input.width / input.height;
  164 + final outputAspectRatio = output.width / output.height;
  165 +
  166 + switch (fit) {
  167 + case BoxFit.fill:
  168 + destination = output;
  169 + break;
  170 + case BoxFit.contain:
  171 + if (outputAspectRatio > inputAspectRatio) {
  172 + destination = Size(
  173 + input.width * output.height / input.height,
  174 + output.height,
  175 + );
  176 + } else {
  177 + destination = Size(
  178 + output.width,
  179 + input.height * output.width / input.width,
  180 + );
  181 + }
  182 + break;
  183 +
  184 + case BoxFit.cover:
  185 + if (outputAspectRatio > inputAspectRatio) {
  186 + destination = Size(
  187 + output.width,
  188 + input.height * (output.width / input.width),
  189 + );
  190 + } else {
  191 + destination = Size(
  192 + input.width * (output.height / input.height),
  193 + output.height,
  194 + );
  195 + }
  196 + break;
  197 + case BoxFit.fitWidth:
  198 + destination = Size(
  199 + output.width,
  200 + input.height * (output.width / input.width),
  201 + );
  202 + break;
  203 + case BoxFit.fitHeight:
  204 + destination = Size(
  205 + input.width * (output.height / input.height),
  206 + output.height,
  207 + );
  208 + break;
  209 + case BoxFit.none:
  210 + destination = Size(
  211 + math.min(input.width, output.width),
  212 + math.min(input.height, output.height),
  213 + );
  214 + break;
  215 + case BoxFit.scaleDown:
  216 + destination = input;
  217 + if (destination.height > output.height) {
  218 + destination = Size(output.height * inputAspectRatio, output.height);
  219 + }
  220 + if (destination.width > output.width) {
  221 + destination = Size(output.width, output.width / inputAspectRatio);
  222 + }
  223 + break;
  224 + }
  225 +
  226 + return destination;
  227 +}