Skip to content

Commit

Permalink
replaced mobile_scanner
Browse files Browse the repository at this point in the history
  • Loading branch information
frankmer committed Mar 13, 2024
1 parent a07ada1 commit bd11d93
Show file tree
Hide file tree
Showing 7 changed files with 531 additions and 320 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_launcher_icons/custom_exceptions.dart';

import '../../../utils/home_widget_utils.dart';
import '../../../utils/logger.dart';
Expand All @@ -19,8 +18,11 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor {
@override
Future<void> processUri(Uri uri, {BuildContext? context, bool fromInit = false}) async {
if (context == null) {
Logger.error('HomeWidgetNavigateProcessor: Cannot Navigate without context',
error: const InvalidConfigException('context is null'), stackTrace: StackTrace.current);
Logger.error(
'HomeWidgetNavigateProcessor: Cannot Navigate without context',
error: Exception('context is null'),
stackTrace: StackTrace.current,
);
return;
}
Logger.warning('HomeWidgetNavigateProcessor: Processing uri: $uri');
Expand Down
133 changes: 133 additions & 0 deletions lib/utils/image_converter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui';
import 'package:camera/camera.dart';
import 'package:image/image.dart' as imglib;

class ImageConverter {
final imglib.Image image;
final Size size;

ImageConverter({
required this.image,
}) : size = Size(image.width.toDouble(), image.height.toDouble());

factory ImageConverter.fromCameraImage(CameraImage image, int rotation, {bool isFrontCamera = false}) {
if (image.format.group != ImageFormatGroup.yuv420 || image.planes.length != 3) {
throw ArgumentError('Only support YUV_420 format');
}
return ImageConverter._rotatedCameraImage(image, rotation: rotation, mirror: isFrontCamera);
}

factory ImageConverter._rotatedCameraImage(CameraImage image, {required int rotation, required bool mirror}) {
rotation = 360 - rotation; // if the rotation is 90, we need to rotate by 270 to get the correct rotation
const alpha = 0xFF;
final height = image.height;
final width = image.width;
final yPlane = image.planes[0];
final uPlane = image.planes[1];
final vPlane = image.planes[2];
final int outputWidth = rotation == 90 || rotation == 270 ? height : width;
final int outputHeight = rotation == 90 || rotation == 270 ? width : height;
final int uvRowStride = uPlane.bytesPerRow;
final int uvPixelStride = uPlane.bytesPerPixel!;
Function(int x, int y) getNewX;
Function(int x, int y) getNewY;

switch (rotation) {
case 90:
if (mirror) {
// rotate by 90 and flip horizontally
getNewX = (x, y) => height - y - 1;
getNewY = (x, y) => width - x - 1;
} else {
getNewX = (x, y) => y;
getNewY = (x, y) => width - x - 1;
}
break;
case 180:
if (mirror) {
// rotate by 180 and flip horizontally
getNewX = (x, y) => x;
getNewY = (x, y) => height - y - 1;
} else {
getNewX = (x, y) => width - x - 1;
getNewY = (x, y) => height - y - 1;
}
break;
case 270:
if (mirror) {
// rotate by 270 and flip horizontally
getNewX = (x, y) => y;
getNewY = (x, y) => outputHeight - x;
} else {
getNewX = (x, y) => height - y - 1;
getNewY = (x, y) => x;
}
break;

default:
if (mirror) {
// flip horizontally
getNewX = (x, y) => x;
getNewY = (x, y) => height - y - 1;
} else {
getNewX = (x, y) => x;
getNewY = (x, y) => y;
}
break;
}

try {
// imgLib -> Image package from https://pub.dartlang.org/packages/image
var img = imglib.Image(width: outputWidth, height: outputHeight); // Create Image buffer

// Fill image buffer with plane[0] from YUV420_888
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
final int uvIndex = uvPixelStride * (x / 2).floor() + uvRowStride * (y / 2).floor();
final int index = (y * width + x);

final yp = yPlane.bytes[index];
final up = uPlane.bytes[uvIndex];
final vp = vPlane.bytes[uvIndex];
// Calculate pixel color

final int r = (yp + vp * 1436 / 1024 - 179).round().clamp(0, 255);
final int g = (yp - up * 46549 / 131072 + 44 - vp * 93604 / 131072 + 91).round().clamp(0, 255);
final int b = (yp + up * 1814 / 1024 - 227).round().clamp(0, 255);
// color: 0x FF FF FF FF
// A B G R
final newX = getNewX(x, y);
final newY = getNewY(x, y);

if ((img.isBoundsSafe(newX, newY))) {
img.setPixelRgba(newX, newY, r, g, b, alpha);
}
}
}
return ImageConverter(image: img);
} catch (e) {
print(">>>>>>>>>>>> ERROR:" + e.toString());
throw e;
}
}

factory ImageConverter.fromFile(String path) {
final img = imglib.decodeImage(File(path).readAsBytesSync())!;
return ImageConverter(image: img);
}

factory ImageConverter.fromBytes(Uint8List bytes) {
final img = imglib.decodeImage(bytes)!;
return ImageConverter(image: img);
}

Uint8List toBytes() {
return Uint8List.fromList(imglib.encodePng(image));
}

imglib.Image toImage() {
return image;
}
}
98 changes: 49 additions & 49 deletions lib/views/qr_scanner_view/qr_scanner_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,56 +37,56 @@ class QRScannerView extends StatelessView {

@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.black,
leading: IconButton(
icon: const Icon(
Icons.arrow_back,
color: Colors.white,
size: 32,
),
onPressed: () {
Navigator.pop(context, null);
}),
),
extendBodyBehindAppBar: true,
body: FutureBuilder(
future: Permission.camera.request(),
builder: (context, isGranted) {
if (isGranted.connectionState != ConnectionState.done) return const SizedBox();
if (isGranted.data == PermissionStatus.permanentlyDenied) {
return DefaultDialog(
title: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogTitle),
content: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogPermanentlyDenied),
);
}
if (isGranted.data != PermissionStatus.granted) {
return DefaultDialog(
title: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogTitle),
content: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogContent),
actions: [
DefaultDialogButton(
child: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogButton),
onPressed: () {
//Trigger the permission to request it
Permission.camera.request();
},
return FutureBuilder(
future: Permission.camera.request(),
builder: (context, isGranted) {
if (isGranted.connectionState != ConnectionState.done) return const SizedBox();
if (isGranted.data == PermissionStatus.permanentlyDenied) {
return DefaultDialog(
title: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogTitle),
content: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogPermanentlyDenied),
);
}
if (isGranted.data != PermissionStatus.granted) {
return DefaultDialog(
title: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogTitle),
content: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogContent),
actions: [
DefaultDialogButton(
child: Text(AppLocalizations.of(context)!.grantCameraPermissionDialogButton),
onPressed: () {
//Trigger the permission to request it
Permission.camera.request();
},
),
DefaultDialogButton(
child: Text(AppLocalizations.of(context)!.cancel),
onPressed: () {
Navigator.pop(context, null);
},
),
],
);
}
return SafeArea(
child: Stack(
children: [
const QRScannerWidget(),
Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
elevation: 0,
),
DefaultDialogButton(
child: Text(AppLocalizations.of(context)!.cancel),
onPressed: () {
Navigator.pop(context, null);
},
),
],
);
}
return const QRScannerWidget();
},
),
extendBodyBehindAppBar: true,
body: const SizedBox(),
),
],
),
);
},
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,37 @@ class ScannerOverlayShape extends ShapeBorder {
EdgeInsetsGeometry get dimensions => const EdgeInsets.all(10.0);

@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return Path()
..fillType = PathFillType.evenOdd
..addPath(getOuterPath(rect), Offset.zero);
}
Path getInnerPath(Rect rect, {TextDirection? textDirection}) => Path()
..fillType = PathFillType.evenOdd
..addPath(getOuterPath(rect), Offset.zero);

@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
{
Path getLeftTopPath(Rect rect) {
return Path()
..moveTo(rect.left, rect.bottom)
..lineTo(rect.left, rect.top)
..lineTo(rect.right, rect.top);
}

return getLeftTopPath(rect)
..lineTo(
rect.right,
rect.bottom,
)
..lineTo(
rect.left,
rect.bottom,
)
..lineTo(
rect.left,
rect.top,
);
}
}
Path getOuterPath(Rect rect, {TextDirection? textDirection}) => Path()
..moveTo(rect.left, rect.bottom)
..lineTo(rect.left, rect.top)
..lineTo(rect.right, rect.top)
..lineTo(rect.right, rect.bottom)
..lineTo(rect.left, rect.bottom);

@override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
const lineSize = 30;

final isPortrait = rect.height > rect.width;

final width = rect.width;
final borderWidthSize = width * 10 / 100;
final double borderWidthSize;
final height = rect.height;
final borderHeightSize = height - (width - borderWidthSize);
final double borderHeightSize;

if (isPortrait) {
borderWidthSize = width * 10 / 100;
borderHeightSize = height - (width - borderWidthSize);
} else {
borderHeightSize = height * 10 / 100;
borderWidthSize = width - (height - borderHeightSize);
}

final borderSize = Size(borderWidthSize / 2, borderHeightSize / 2);

var paint = Paint()
Expand Down
Loading

0 comments on commit bd11d93

Please sign in to comment.