Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
mobile_scanner
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
casvanluijtelaar
2022-06-03 14:21:58 +0200
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
de533d51b75f833bf852d5c5fef1e1c21fb359ba
de533d51
1 parent
54239631
android implementation
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
220 additions
and
1 deletions
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScanner.kt
example/lib/barcode_scanner_window.dart
example/lib/main.dart
lib/src/mobile_scanner.dart
lib/src/mobile_scanner_controller.dart
android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScanner.kt
View file @
de533d5
...
...
@@ -4,9 +4,11 @@ import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.graphics.Point
import android.graphics.Rect
import android.net.Uri
import android.util.Log
import android.util.Size
import android.media.Image
import android.view.Surface
import androidx.annotation.NonNull
import androidx.camera.core.*
...
...
@@ -18,6 +20,7 @@ import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.common.InputImage.IMAGE_FORMAT_NV21
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
...
...
@@ -40,6 +43,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
private var camera: Camera? = null
private var preview: Preview? = null
private var textureEntry: TextureRegistry.SurfaceTextureEntry? = null
private var scanWindow: Rect? = null;
// @AnalyzeMode
// private var analyzeMode: Int = AnalyzeMode.NONE
...
...
@@ -99,7 +103,20 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
// when (analyzeMode) {
// AnalyzeMode.BARCODE -> {
val mediaImage = imageProxy.image ?: return@Analyzer
val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
var inputImage: InputImage?;
if(scanWindow != null) {
val croppedImage = croppedNV21(mediaImage, scanWindow!!)
inputImage = InputImage.fromByteArray(
croppedImage,
scanWindow!!.width(),
scanWindow!!.height(),
imageProxy.imageInfo.rotationDegrees,
IMAGE_FORMAT_NV21,
)
} else {
inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
}
scanner.process(inputImage)
.addOnSuccessListener { barcodes ->
...
...
@@ -115,6 +132,50 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
// }
}
private fun croppedNV21(mediaImage: Image, cropRect: Rect): ByteArray {
val yBuffer = mediaImage.planes[0].buffer // Y
val vuBuffer = mediaImage.planes[2].buffer // VU
val ySize = yBuffer.remaining()
val vuSize = vuBuffer.remaining()
val nv21 = ByteArray(ySize + vuSize)
yBuffer.get(nv21, 0, ySize)
vuBuffer.get(nv21, ySize, vuSize)
return cropByteArray(nv21, mediaImage.width, mediaImage.height, cropRect)
}
private fun cropByteArray(src: ByteArray, width: Int, height: Int, cropRect: Rect, ): ByteArray {
val x = cropRect.left * 2 / 2
val y = cropRect.top * 2 / 2
val w = cropRect.width() * 2 / 2
val h = cropRect.height() * 2 / 2
val yUnit = w * h
val uv = yUnit / 2
val nData = ByteArray(yUnit + uv)
val uvIndexDst = w * h - y / 2 * w
val uvIndexSrc = width * height + x
var srcPos0 = y * width
var destPos0 = 0
var uvSrcPos0 = uvIndexSrc
var uvDestPos0 = uvIndexDst
for (i in y until y + h) {
System.arraycopy(src, srcPos0 + x, nData, destPos0, w) //y memory block copy
srcPos0 += width
destPos0 += w
if (i and 1 == 0) {
System.arraycopy(src, uvSrcPos0, nData, uvDestPos0, w) //uv memory block copy
uvSrcPos0 += width
uvDestPos0 += w
}
}
return nData
}
private var scanner = BarcodeScanning.getClient()
...
...
@@ -134,6 +195,9 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
val torch: Boolean = call.argument<Boolean>("torch") ?: false
val formats: List<Int>? = call.argument<List<Int>>("formats")
val scanWindowData: List<Int>? = call.argument("scanWindow")
if(scanWindowData != null) scanWindow = Rect(scanWindowData!![0], scanWindowData!![1], scanWindowData!![2], scanWindowData!![3])
if (formats != null) {
val formatsList: MutableList<Int> = mutableListOf()
for (index in formats) {
...
...
example/lib/barcode_scanner_window.dart
0 → 100644
View file @
de533d5
import
'dart:ui'
;
import
'package:flutter/material.dart'
;
import
'package:mobile_scanner/mobile_scanner.dart'
;
class
BarcodeScannerWithScanWindow
extends
StatefulWidget
{
const
BarcodeScannerWithScanWindow
({
Key
?
key
})
:
super
(
key:
key
);
@override
_BarcodeScannerWithScanWindowState
createState
()
=>
_BarcodeScannerWithScanWindowState
();
}
class
_BarcodeScannerWithScanWindowState
extends
State
<
BarcodeScannerWithScanWindow
>
with
SingleTickerProviderStateMixin
{
String
?
barcode
;
late
Rect
scanWindow
;
late
MobileScannerController
controller
;
@override
void
initState
()
{
super
.
initState
();
final
screenWidth
=
window
.
physicalSize
.
shortestSide
/
window
.
devicePixelRatio
;
final
screenHeight
=
window
.
physicalSize
.
longestSide
/
window
.
devicePixelRatio
;
scanWindow
=
Rect
.
fromCenter
(
center:
Offset
(
screenWidth
/
2
,
screenHeight
/
2
),
width:
200
,
height:
200
,
);
controller
=
MobileScannerController
(
torchEnabled:
true
,
scanWindow:
scanWindow
,
);
}
bool
isStarted
=
true
;
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
backgroundColor:
Colors
.
black
,
body:
Builder
(
builder:
(
context
)
{
return
Stack
(
children:
[
MobileScanner
(
fit:
BoxFit
.
contain
,
controller:
controller
,
onDetect:
(
barcode
,
args
)
{
setState
(()
{
this
.
barcode
=
barcode
.
rawValue
;
});
},
),
CustomPaint
(
painter:
ScannerOverlay
(
scanWindow
),
),
Align
(
alignment:
Alignment
.
bottomCenter
,
child:
Container
(
alignment:
Alignment
.
bottomCenter
,
height:
100
,
color:
Colors
.
black
.
withOpacity
(
0.4
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceEvenly
,
children:
[
Center
(
child:
SizedBox
(
width:
MediaQuery
.
of
(
context
).
size
.
width
-
120
,
height:
50
,
child:
FittedBox
(
child:
Text
(
barcode
??
'Scan something!'
,
overflow:
TextOverflow
.
fade
,
style:
Theme
.
of
(
context
)
.
textTheme
.
headline4
!
.
copyWith
(
color:
Colors
.
white
),
),
),
),
),
],
),
),
),
],
);
},
),
);
}
}
class
ScannerOverlay
extends
CustomPainter
{
ScannerOverlay
(
this
.
scanWindow
);
final
Rect
scanWindow
;
@override
void
paint
(
Canvas
canvas
,
Size
size
)
{
final
backgroundPath
=
Path
()..
addRect
(
Rect
.
largest
);
final
cutoutPath
=
Path
()..
addRect
(
scanWindow
);
final
backgroundPaint
=
Paint
()
..
color
=
Colors
.
black
.
withOpacity
(
0.5
)
..
style
=
PaintingStyle
.
fill
..
blendMode
=
BlendMode
.
dstOut
;
final
backgroundWithCutout
=
Path
.
combine
(
PathOperation
.
difference
,
backgroundPath
,
cutoutPath
,
);
canvas
.
drawPath
(
backgroundWithCutout
,
backgroundPaint
);
}
@override
bool
shouldRepaint
(
covariant
CustomPainter
oldDelegate
)
{
return
false
;
}
}
...
...
example/lib/main.dart
View file @
de533d5
import
'package:flutter/material.dart'
;
import
'package:mobile_scanner_example/barcode_scanner_controller.dart'
;
import
'package:mobile_scanner_example/barcode_scanner_window.dart'
;
import
'package:mobile_scanner_example/barcode_scanner_without_controller.dart'
;
void
main
(
)
=>
runApp
(
const
MaterialApp
(
home:
MyHome
()));
class
MyHome
extends
StatelessWidget
{
...
...
@@ -27,6 +29,17 @@ class MyHome extends StatelessWidget {
},
child:
const
Text
(
'MobileScanner with Controller'
),
),
ElevatedButton
(
onPressed:
()
{
Navigator
.
of
(
context
).
push
(
MaterialPageRoute
(
builder:
(
context
)
=>
const
BarcodeScannerWithScanWindow
(),
),
);
},
child:
const
Text
(
'MobileScanner with ScanWindow'
),
),
ElevatedButton
(
onPressed:
()
{
Navigator
.
of
(
context
).
push
(
...
...
lib/src/mobile_scanner.dart
View file @
de533d5
...
...
@@ -27,6 +27,7 @@ class MobileScanner extends StatefulWidget {
/// Set to false if you don't want duplicate scans.
final
bool
allowDuplicates
;
/// Create a [MobileScanner] with a [controller], the [controller] must has been initialized.
const
MobileScanner
({
Key
?
key
,
...
...
lib/src/mobile_scanner_controller.dart
View file @
de533d5
...
...
@@ -49,6 +49,9 @@ class MobileScannerController {
/// WARNING: On iOS, only 1 format is supported.
final
List
<
BarcodeFormat
>?
formats
;
/// can be used to limit the scan area to a portion of the screen
final
Rect
?
scanWindow
;
CameraFacing
facing
;
bool
hasTorch
=
false
;
late
StreamController
<
Barcode
>
barcodesController
;
...
...
@@ -60,6 +63,7 @@ class MobileScannerController {
this
.
ratio
,
this
.
torchEnabled
,
this
.
formats
,
this
.
scanWindow
,
})
{
// In case a new instance is created before calling dispose()
if
(
_controllerHashcode
!=
null
)
{
...
...
@@ -156,6 +160,14 @@ class MobileScannerController {
// Set the starting arguments for the camera
final
Map
arguments
=
{};
arguments
[
'facing'
]
=
facing
.
index
;
if
(
scanWindow
!=
null
)
{
arguments
[
'scanWindow'
]
=
[
scanWindow
!.
left
,
scanWindow
!.
top
,
scanWindow
!.
right
,
scanWindow
!.
bottom
,
];
}
if
(
ratio
!=
null
)
arguments
[
'ratio'
]
=
ratio
;
if
(
torchEnabled
!=
null
)
arguments
[
'torch'
]
=
torchEnabled
;
...
...
Please
register
or
login
to post a comment