Khoren Markosyan
2 years ago
21 changed files with 314 additions and 277 deletions
@ -1,242 +1,7 @@
|
||||
import 'dart:async'; |
||||
import 'dart:ffi'; |
||||
import 'dart:io'; |
||||
import 'dart:isolate'; |
||||
import 'dart:typed_data'; |
||||
|
||||
import 'package:camera/camera.dart'; |
||||
import 'package:ffi/ffi.dart'; |
||||
import 'package:flutter/services.dart'; |
||||
import 'package:image/image.dart' as imglib; |
||||
|
||||
import 'generated_bindings.dart'; |
||||
import 'isolate_utils.dart'; |
||||
|
||||
export 'generated_bindings.dart'; |
||||
export 'image_converter.dart'; |
||||
export 'reader_widget.dart'; |
||||
export 'scanner_overlay.dart'; |
||||
export 'writer_widget.dart'; |
||||
|
||||
// TODO: fix below warning from lint |
||||
// ignore: avoid_classes_with_only_static_members |
||||
/// The main class for reading barcodes from images or camera. |
||||
class FlutterZxing { |
||||
static const MethodChannel _channel = MethodChannel('flutter_zxing'); |
||||
|
||||
static Future<String?> get platformVersion async { |
||||
final String? version = await _channel.invokeMethod('getPlatformVersion'); |
||||
return version; |
||||
} |
||||
|
||||
static final GeneratedBindings bindings = GeneratedBindings(dylib); |
||||
|
||||
static IsolateUtils? isolateUtils; |
||||
|
||||
/// Enables or disables the logging of the library |
||||
static void setLogEnabled(bool enabled) => |
||||
bindings.setLogEnabled(enabled ? 1 : 0); |
||||
|
||||
/// Returns a version of the zxing library |
||||
static String version() => bindings.version().cast<Utf8>().toDartString(); |
||||
|
||||
/// Starts reading barcode from the camera |
||||
static Future<void> startCameraProcessing() async { |
||||
// Spawn a new isolate |
||||
isolateUtils = IsolateUtils(); |
||||
await isolateUtils?.startReadingBarcode(); |
||||
} |
||||
|
||||
/// Stops reading barcode from the camera |
||||
static void stopCameraProcessing() => isolateUtils?.stopReadingBarcode(); |
||||
|
||||
/// Reads barcode from String image path |
||||
static Future<CodeResult?> readImagePathString( |
||||
String path, { |
||||
int format = Format.Any, |
||||
int cropWidth = 0, |
||||
int cropHeight = 0, |
||||
}) => |
||||
readImagePath(XFile(path), |
||||
format: format, cropWidth: cropWidth, cropHeight: cropHeight); |
||||
|
||||
/// Reads barcode from XFile image path |
||||
static Future<CodeResult?> readImagePath( |
||||
XFile path, { |
||||
int format = Format.Any, |
||||
int cropWidth = 0, |
||||
int cropHeight = 0, |
||||
}) async { |
||||
final Uint8List imageBytes = await path.readAsBytes(); |
||||
final imglib.Image? image = imglib.decodeImage(imageBytes); |
||||
if (image == null) { |
||||
return null; |
||||
} |
||||
return readBarcode( |
||||
image.getBytes(format: imglib.Format.luminance), |
||||
format, |
||||
image.width, |
||||
image.height, |
||||
cropWidth, |
||||
cropHeight, |
||||
); |
||||
} |
||||
|
||||
/// Reads barcode from image url |
||||
static Future<CodeResult?> readImageUrl( |
||||
String url, { |
||||
int format = Format.Any, |
||||
int cropWidth = 0, |
||||
int cropHeight = 0, |
||||
}) async { |
||||
final Uint8List imageBytes = |
||||
(await NetworkAssetBundle(Uri.parse(url)).load(url)) |
||||
.buffer |
||||
.asUint8List(); |
||||
final imglib.Image? image = imglib.decodeImage(imageBytes); |
||||
if (image == null) { |
||||
return null; |
||||
} |
||||
return readBarcode( |
||||
image.getBytes(format: imglib.Format.luminance), |
||||
format, |
||||
image.width, |
||||
image.height, |
||||
cropWidth, |
||||
cropHeight, |
||||
); |
||||
} |
||||
|
||||
static CodeResult readBarcode(Uint8List bytes, int format, int width, |
||||
int height, int cropWidth, int cropHeight) { |
||||
return bindings.readBarcode( |
||||
bytes.allocatePointer(), format, width, height, cropWidth, cropHeight); |
||||
} |
||||
|
||||
static List<CodeResult> readBarcodes(Uint8List bytes, int format, int width, |
||||
int height, int cropWidth, int cropHeight) { |
||||
final CodeResults result = bindings.readBarcodes( |
||||
bytes.allocatePointer(), format, width, height, cropWidth, cropHeight); |
||||
final List<CodeResult> results = <CodeResult>[]; |
||||
for (int i = 0; i < result.count; i++) { |
||||
results.add(result.results.elementAt(i).ref); |
||||
} |
||||
return results; |
||||
} |
||||
|
||||
static EncodeResult encodeBarcode(String contents, int width, int height, |
||||
int format, int margin, int eccLevel) { |
||||
final EncodeResult result = bindings.encodeBarcode( |
||||
contents.toNativeUtf8().cast<Char>(), |
||||
width, |
||||
height, |
||||
format, |
||||
margin, |
||||
eccLevel); |
||||
return result; |
||||
} |
||||
|
||||
static Future<CodeResult> processCameraImage(CameraImage image, |
||||
{int format = Format.Any, double cropPercent = 0.5}) async { |
||||
final IsolateData isolateData = IsolateData(image, format, cropPercent); |
||||
final CodeResult result = await _inference(isolateData); |
||||
return result; |
||||
} |
||||
|
||||
/// Runs inference in another isolate |
||||
static Future<CodeResult> _inference(IsolateData isolateData) async { |
||||
final ReceivePort responsePort = ReceivePort(); |
||||
isolateUtils?.sendPort |
||||
?.send(isolateData..responsePort = responsePort.sendPort); |
||||
// TODO: fix below warning from lint |
||||
// ignore: always_specify_types |
||||
final results = await responsePort.first; |
||||
return results; |
||||
} |
||||
|
||||
static String formatName(int format) => _formatNames[format] ?? 'Unknown'; |
||||
} |
||||
|
||||
// Getting a library that holds needed symbols |
||||
DynamicLibrary _openDynamicLibrary() { |
||||
if (Platform.isAndroid) { |
||||
return DynamicLibrary.open('libflutter_zxing.so'); |
||||
} else if (Platform.isWindows) { |
||||
return DynamicLibrary.open('flutter_zxing_windows_plugin.dll'); |
||||
} |
||||
return DynamicLibrary.process(); |
||||
} |
||||
|
||||
DynamicLibrary dylib = _openDynamicLibrary(); |
||||
|
||||
extension Uint8ListBlobConversion on Uint8List { |
||||
/// Allocates a pointer filled with the Uint8List data. |
||||
Pointer<Char> allocatePointer() { |
||||
final Pointer<Int8> blob = calloc<Int8>(length); |
||||
final Int8List blobBytes = blob.asTypedList(length); |
||||
blobBytes.setAll(0, this); |
||||
return blob.cast<Char>(); |
||||
} |
||||
} |
||||
|
||||
extension CodeExt on CodeResult { |
||||
bool get isValidBool => isValid == 1; |
||||
String? get textString => |
||||
text == nullptr ? null : text.cast<Utf8>().toDartString(); |
||||
String get formatString => FlutterZxing.formatName(format); |
||||
} |
||||
|
||||
extension EncodeExt on EncodeResult { |
||||
bool get isValidBool => isValid == 1; |
||||
String? get textString => |
||||
text == nullptr ? null : text.cast<Utf8>().toDartString(); |
||||
String get formatString => FlutterZxing.formatName(format); |
||||
Uint32List get bytes => data.cast<Uint32>().asTypedList(length); |
||||
String get errorMessage => error.cast<Utf8>().toDartString(); |
||||
} |
||||
|
||||
extension CodeFormat on Format { |
||||
String get name => _formatNames[this] ?? 'Unknown'; |
||||
|
||||
static final List<int> writerFormats = <int>[ |
||||
Format.QRCode, |
||||
Format.DataMatrix, |
||||
Format.Aztec, |
||||
Format.PDF417, |
||||
Format.Codabar, |
||||
Format.Code39, |
||||
Format.Code93, |
||||
Format.Code128, |
||||
Format.EAN8, |
||||
Format.EAN13, |
||||
Format.ITF, |
||||
Format.UPCA, |
||||
Format.UPCE, |
||||
// Format.DataBar, |
||||
// Format.DataBarExpanded, |
||||
// Format.MaxiCode, |
||||
]; |
||||
} |
||||
|
||||
final Map<int, String> _formatNames = <int, String>{ |
||||
Format.None: 'None', |
||||
Format.Aztec: 'Aztec', |
||||
Format.Codabar: 'CodaBar', |
||||
Format.Code39: 'Code39', |
||||
Format.Code93: 'Code93', |
||||
Format.Code128: 'Code128', |
||||
Format.DataBar: 'DataBar', |
||||
Format.DataBarExpanded: 'DataBarExpanded', |
||||
Format.DataMatrix: 'DataMatrix', |
||||
Format.EAN8: 'EAN8', |
||||
Format.EAN13: 'EAN13', |
||||
Format.ITF: 'ITF', |
||||
Format.MaxiCode: 'MaxiCode', |
||||
Format.PDF417: 'PDF417', |
||||
Format.QRCode: 'QR Code', |
||||
Format.UPCA: 'UPCA', |
||||
Format.UPCE: 'UPCE', |
||||
Format.OneDCodes: 'OneD', |
||||
Format.TwoDCodes: 'TwoD', |
||||
Format.Any: 'Any', |
||||
}; |
||||
export 'src/logic/zxing.dart'; |
||||
export 'src/ui/reader_widget.dart'; |
||||
export 'src/ui/scanner_overlay.dart'; |
||||
export 'src/ui/writer_widget.dart'; |
||||
export 'src/utils/extentions.dart'; |
||||
export 'src/utils/image_converter.dart'; |
||||
|
@ -0,0 +1,13 @@
|
||||
part of 'zxing.dart'; |
||||
|
||||
EncodeResult encodeBarcode(String contents, int width, int height, int format, |
||||
int margin, int eccLevel) { |
||||
return bindings.encodeBarcode( |
||||
contents.toNativeUtf8().cast<Char>(), |
||||
width, |
||||
height, |
||||
format, |
||||
margin, |
||||
eccLevel, |
||||
); |
||||
} |
@ -0,0 +1,74 @@
|
||||
part of 'zxing.dart'; |
||||
|
||||
/// Reads barcode from String image path |
||||
Future<CodeResult?> readImagePathString( |
||||
String path, { |
||||
int format = Format.Any, |
||||
int cropWidth = 0, |
||||
int cropHeight = 0, |
||||
}) => |
||||
readImagePath(XFile(path), |
||||
format: format, cropWidth: cropWidth, cropHeight: cropHeight); |
||||
|
||||
/// Reads barcode from XFile image path |
||||
Future<CodeResult?> readImagePath( |
||||
XFile path, { |
||||
int format = Format.Any, |
||||
int cropWidth = 0, |
||||
int cropHeight = 0, |
||||
}) async { |
||||
final Uint8List imageBytes = await path.readAsBytes(); |
||||
final imglib.Image? image = imglib.decodeImage(imageBytes); |
||||
if (image == null) { |
||||
return null; |
||||
} |
||||
return readBarcode( |
||||
image.getBytes(format: imglib.Format.luminance), |
||||
format, |
||||
image.width, |
||||
image.height, |
||||
cropWidth, |
||||
cropHeight, |
||||
); |
||||
} |
||||
|
||||
/// Reads barcode from image url |
||||
Future<CodeResult?> readImageUrl( |
||||
String url, { |
||||
int format = Format.Any, |
||||
int cropWidth = 0, |
||||
int cropHeight = 0, |
||||
}) async { |
||||
final Uint8List imageBytes = |
||||
(await NetworkAssetBundle(Uri.parse(url)).load(url)).buffer.asUint8List(); |
||||
final imglib.Image? image = imglib.decodeImage(imageBytes); |
||||
if (image == null) { |
||||
return null; |
||||
} |
||||
return readBarcode( |
||||
image.getBytes(format: imglib.Format.luminance), |
||||
format, |
||||
image.width, |
||||
image.height, |
||||
cropWidth, |
||||
cropHeight, |
||||
); |
||||
} |
||||
|
||||
// Reads barcode from Uint8List image bytes |
||||
CodeResult readBarcode( |
||||
Uint8List bytes, |
||||
int format, |
||||
int width, |
||||
int height, |
||||
int cropWidth, |
||||
int cropHeight, |
||||
) => |
||||
bindings.readBarcode( |
||||
bytes.allocatePointer(), |
||||
format, |
||||
width, |
||||
height, |
||||
cropWidth, |
||||
cropHeight, |
||||
); |
@ -0,0 +1,13 @@
|
||||
part of 'zxing.dart'; |
||||
|
||||
// Reads barcodes from Uint8List image bytes |
||||
List<CodeResult> readBarcodes(Uint8List bytes, int format, int width, |
||||
int height, int cropWidth, int cropHeight) { |
||||
final CodeResults result = bindings.readBarcodes( |
||||
bytes.allocatePointer(), format, width, height, cropWidth, cropHeight); |
||||
final List<CodeResult> results = <CodeResult>[]; |
||||
for (int i = 0; i < result.count; i++) { |
||||
results.add(result.results.elementAt(i).ref); |
||||
} |
||||
return results; |
||||
} |
@ -0,0 +1,15 @@
|
||||
part of 'zxing.dart'; |
||||
|
||||
final GeneratedBindings bindings = GeneratedBindings(dylib); |
||||
|
||||
// Getting a library that holds needed symbols |
||||
DynamicLibrary _openDynamicLibrary() { |
||||
if (Platform.isAndroid) { |
||||
return DynamicLibrary.open('libflutter_zxing.so'); |
||||
} else if (Platform.isWindows) { |
||||
return DynamicLibrary.open('flutter_zxing_windows_plugin.dll'); |
||||
} |
||||
return DynamicLibrary.process(); |
||||
} |
||||
|
||||
DynamicLibrary dylib = _openDynamicLibrary(); |
@ -0,0 +1,28 @@
|
||||
part of 'zxing.dart'; |
||||
|
||||
IsolateUtils? isolateUtils; |
||||
|
||||
/// Starts reading barcode from the camera |
||||
Future<void> startCameraProcessing() async { |
||||
isolateUtils = IsolateUtils(); |
||||
await isolateUtils?.startReadingBarcode(); |
||||
} |
||||
|
||||
/// Stops reading barcode from the camera |
||||
void stopCameraProcessing() => isolateUtils?.stopReadingBarcode(); |
||||
|
||||
Future<CodeResult> processCameraImage(CameraImage image, |
||||
{int format = Format.Any, double cropPercent = 0.5}) async { |
||||
final IsolateData isolateData = IsolateData(image, format, cropPercent); |
||||
final CodeResult result = await _inference(isolateData); |
||||
return result; |
||||
} |
||||
|
||||
/// Runs inference in another isolate |
||||
Future<CodeResult> _inference(IsolateData isolateData) async { |
||||
final ReceivePort responsePort = ReceivePort(); |
||||
isolateUtils?.sendPort |
||||
?.send(isolateData..responsePort = responsePort.sendPort); |
||||
final dynamic results = await responsePort.first; |
||||
return results; |
||||
} |
@ -0,0 +1,29 @@
|
||||
import 'dart:ffi'; |
||||
import 'dart:io'; |
||||
import 'dart:isolate'; |
||||
import 'dart:typed_data'; |
||||
|
||||
import 'package:camera/camera.dart'; |
||||
import 'package:ffi/ffi.dart'; |
||||
import 'package:flutter/services.dart'; |
||||
import 'package:image/image.dart' as imglib; |
||||
|
||||
import '../../generated_bindings.dart'; |
||||
import '../utils/extentions.dart'; |
||||
import '../utils/isolate_utils.dart'; |
||||
|
||||
part 'barcode_encoder.dart'; |
||||
part 'barcode_reader.dart'; |
||||
part 'barcodes_reader.dart'; |
||||
part 'bindings.dart'; |
||||
part 'camera_stream.dart'; |
||||
|
||||
/// Returns a version of the zxing library |
||||
String zxingVersion() => bindings.version().cast<Utf8>().toDartString(); |
||||
|
||||
/// Enables or disables the logging of the library |
||||
void setZxingLogEnabled(bool enabled) => |
||||
bindings.setLogEnabled(enabled ? 1 : 0); |
||||
|
||||
/// Returns a readable barcode format name |
||||
String barcodeFormatName(int format) => formatNames[format] ?? 'Unknown'; |
@ -0,0 +1,78 @@
|
||||
import 'dart:ffi'; |
||||
import 'dart:typed_data'; |
||||
|
||||
import 'package:ffi/ffi.dart'; |
||||
|
||||
import '../../flutter_zxing.dart'; |
||||
|
||||
extension Uint8ListBlobConversion on Uint8List { |
||||
/// Allocates a pointer filled with the Uint8List data. |
||||
Pointer<Char> allocatePointer() { |
||||
final Pointer<Int8> blob = calloc<Int8>(length); |
||||
final Int8List blobBytes = blob.asTypedList(length); |
||||
blobBytes.setAll(0, this); |
||||
return blob.cast<Char>(); |
||||
} |
||||
} |
||||
|
||||
extension CodeExt on CodeResult { |
||||
bool get isValidBool => isValid == 1; |
||||
String? get textString => |
||||
text == nullptr ? null : text.cast<Utf8>().toDartString(); |
||||
String get formatString => barcodeFormatName(format); |
||||
} |
||||
|
||||
extension EncodeExt on EncodeResult { |
||||
bool get isValidBool => isValid == 1; |
||||
String? get textString => |
||||
text == nullptr ? null : text.cast<Utf8>().toDartString(); |
||||
String get formatString => barcodeFormatName(format); |
||||
Uint32List get bytes => data.cast<Uint32>().asTypedList(length); |
||||
String get errorMessage => error.cast<Utf8>().toDartString(); |
||||
} |
||||
|
||||
extension CodeFormat on Format { |
||||
String get name => formatNames[this] ?? 'Unknown'; |
||||
|
||||
static final List<int> writerFormats = <int>[ |
||||
Format.QRCode, |
||||
Format.DataMatrix, |
||||
Format.Aztec, |
||||
Format.PDF417, |
||||
Format.Codabar, |
||||
Format.Code39, |
||||
Format.Code93, |
||||
Format.Code128, |
||||
Format.EAN8, |
||||
Format.EAN13, |
||||
Format.ITF, |
||||
Format.UPCA, |
||||
Format.UPCE, |
||||
// Format.DataBar, |
||||
// Format.DataBarExpanded, |
||||
// Format.MaxiCode, |
||||
]; |
||||
} |
||||
|
||||
final Map<int, String> formatNames = <int, String>{ |
||||
Format.None: 'None', |
||||
Format.Aztec: 'Aztec', |
||||
Format.Codabar: 'CodaBar', |
||||
Format.Code39: 'Code39', |
||||
Format.Code93: 'Code93', |
||||
Format.Code128: 'Code128', |
||||
Format.DataBar: 'DataBar', |
||||
Format.DataBarExpanded: 'DataBarExpanded', |
||||
Format.DataMatrix: 'DataMatrix', |
||||
Format.EAN8: 'EAN8', |
||||
Format.EAN13: 'EAN13', |
||||
Format.ITF: 'ITF', |
||||
Format.MaxiCode: 'MaxiCode', |
||||
Format.PDF417: 'PDF417', |
||||
Format.QRCode: 'QR Code', |
||||
Format.UPCA: 'UPCA', |
||||
Format.UPCE: 'UPCE', |
||||
Format.OneDCodes: 'OneD', |
||||
Format.TwoDCodes: 'TwoD', |
||||
Format.Any: 'Any', |
||||
}; |
Loading…
Reference in new issue