From 8eba0fcfff2a2498ac38ca2aeff61599c9383cf6 Mon Sep 17 00:00:00 2001 From: Dominik Przywara Date: Thu, 9 Jul 2020 23:16:39 +0200 Subject: [PATCH 1/6] Added ScanningArea to MobileBarcodeScanningOptions.shared.cs --- .../MobileBarcodeScanningOptions.shared.cs | 9 +- ZXing.Net.Mobile/ScanningArea.cs | 106 ++++++++++++++++++ ZXing.Net.Mobile/ZXing.Net.Mobile.csproj | 1 + 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 ZXing.Net.Mobile/ScanningArea.cs diff --git a/ZXing.Net.Mobile/MobileBarcodeScanningOptions.shared.cs b/ZXing.Net.Mobile/MobileBarcodeScanningOptions.shared.cs index 789c8aa6a..9d819fb31 100644 --- a/ZXing.Net.Mobile/MobileBarcodeScanningOptions.shared.cs +++ b/ZXing.Net.Mobile/MobileBarcodeScanningOptions.shared.cs @@ -21,12 +21,19 @@ public MobileBarcodeScanningOptions() InitialDelayBeforeAnalyzingFrames = 300; DelayBetweenContinuousScans = 1000; UseNativeScanning = false; - } + ScanningArea = ScanningArea.Default; + } public CameraResolutionSelectorDelegate CameraResolutionSelector { get; set; } public IEnumerable PossibleFormats { get; set; } + /// + /// Narrow chosen scanning area.
+ /// Works only on iOS and Android + ///
+ public ScanningArea ScanningArea { get; set; } + public bool? TryHarder { get; set; } public bool? PureBarcode { get; set; } diff --git a/ZXing.Net.Mobile/ScanningArea.cs b/ZXing.Net.Mobile/ScanningArea.cs new file mode 100644 index 000000000..1ac9486f3 --- /dev/null +++ b/ZXing.Net.Mobile/ScanningArea.cs @@ -0,0 +1,106 @@ +using System; + +namespace ZXing.Mobile +{ + /// + /// Representation of restricted scanning area in PERCENTAGE. + /// Allowed values: 0 <= value <= 1 AND startY != endY + /// Values of startY and endY are ABSOLUTE to image that means if use values of + /// startY:0.49 and endY:0.51 we will scan only 2% of the whole image + /// starting at 49% and finishing at 51% of the image height. + /// + public class ScanningArea + { + public float StartX { get; } + public float StartY { get; } + public float EndX { get; } + public float EndY { get; } + + ScanningArea(float startX, float startY, float endX, float endY) + { + //if difference between parameters is less than 1% we assume those are equal + if (Math.Abs(startY - endY) < 0.01f) + { + throw new ArgumentException($"Values of {nameof(startY)} and {nameof(endY)} cannot be the same"); + } + + //if difference between parameters is less than 1% we assume those are equal + if (Math.Abs(startX - endX) < 0.01f) + { + throw new ArgumentException($"Values of {nameof(startX)} and {nameof(endX)} cannot be the same"); + } + + //Reverse values instead of throwing argument exception + if (startY > endY) + { + var temp = endY; + endY = startY; + startY = temp; + } + + if (startX > endX) + { + var temp = endX; + endX = startX; + startX = temp; + } + + if (startY < 0) + { + startY = 0; + } + + if (endY > 1) + { + endY = 1; + } + + if (startX < 0) + { + startX = 0; + } + + if (endX > 1) + { + endX = 1; + } + + StartY = startY; + EndY = endY; + StartX = startX; + EndX = endX; + } + + public ScanningArea RotateCounterClockwise() + { + var startX = StartY; + var startY = EndX; + var endX = EndY; + var endY = StartX; + + if (startY > endY) + { + startY = 1f - startY; + endY = 1 - endY; + } + + if (startX > endX) + { + startX = 1 - startX; + endX = 1 - endX; + } + + return new ScanningArea(startX, startY, endX, endY); + } + + + static ScanningArea _default = new ScanningArea(0f, 0f, 1f, 1f); + + /// + /// Returns value that represents whole image. + /// + public static ScanningArea Default => _default; + + public static ScanningArea From(float startX, float startY, float endX, float endY) => new ScanningArea(startX, startY, endX, endY); + } +} \ No newline at end of file diff --git a/ZXing.Net.Mobile/ZXing.Net.Mobile.csproj b/ZXing.Net.Mobile/ZXing.Net.Mobile.csproj index 1eaa83f3d..da00f48fd 100644 --- a/ZXing.Net.Mobile/ZXing.Net.Mobile.csproj +++ b/ZXing.Net.Mobile/ZXing.Net.Mobile.csproj @@ -49,6 +49,7 @@ + From 4485620d15b2904e0f6c756111943b3dce26747c Mon Sep 17 00:00:00 2001 From: Dominik Przywara Date: Thu, 9 Jul 2020 23:17:22 +0200 Subject: [PATCH 2/6] Added narrowing in Android with sample --- Samples/Sample.Android/MainActivity.cs | 5 +- .../CameraAccess/CameraAnalyzer.android.cs | 319 ++++++++++-------- 2 files changed, 175 insertions(+), 149 deletions(-) diff --git a/Samples/Sample.Android/MainActivity.cs b/Samples/Sample.Android/MainActivity.cs index 0d5f17364..a6da23515 100644 --- a/Samples/Sample.Android/MainActivity.cs +++ b/Samples/Sample.Android/MainActivity.cs @@ -45,7 +45,10 @@ protected override void OnCreate(Bundle bundle) scanner.BottomText = "Wait for the barcode to automatically scan!"; //Start scanning - var result = await scanner.Scan(); + var result = await scanner.Scan( new MobileBarcodeScanningOptions + { + ScanningArea = ScanningArea.From(0f, 0.49f, 1f, 0.51f) + }); HandleScanResult(result); }; diff --git a/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs b/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs index 6d4f5c2be..70ef53495 100644 --- a/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs +++ b/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs @@ -1,157 +1,180 @@ using System; using System.Collections.Generic; +using System.Security.AccessControl; using System.Threading.Tasks; using Android.Views; using ApxLabs.FastAndroidCamera; namespace ZXing.Mobile.CameraAccess { - public class CameraAnalyzer - { - readonly CameraController cameraController; - readonly CameraEventsListener cameraEventListener; - Task processingTask; - DateTime lastPreviewAnalysis = DateTime.UtcNow; - bool wasScanned; - IScannerSessionHost scannerHost; - - public CameraAnalyzer(SurfaceView surfaceView, IScannerSessionHost scannerHost) - { - this.scannerHost = scannerHost; - cameraEventListener = new CameraEventsListener(); - cameraController = new CameraController(surfaceView, cameraEventListener, scannerHost); - Torch = new Torch(cameraController, surfaceView.Context); - } - - public Action BarcodeFound; - - public Torch Torch { get; } - - public bool IsAnalyzing { get; private set; } - - public void PauseAnalysis() - => IsAnalyzing = false; - - public void ResumeAnalysis() - => IsAnalyzing = true; - - public void ShutdownCamera() - { - IsAnalyzing = false; - cameraEventListener.OnPreviewFrameReady -= HandleOnPreviewFrameReady; - cameraController.ShutdownCamera(); - } - - public void SetupCamera() - { - cameraEventListener.OnPreviewFrameReady += HandleOnPreviewFrameReady; - cameraController.SetupCamera(); - } - - public void AutoFocus() - => cameraController.AutoFocus(); - - public void AutoFocus(int x, int y) - => cameraController.AutoFocus(x, y); - - public void RefreshCamera() - => cameraController.RefreshCamera(); - - bool CanAnalyzeFrame - { - get - { - if (!IsAnalyzing) - return false; - - //Check and see if we're still processing a previous frame - // todo: check if we can run as many as possible or mby run two analyzers at once (Vision + ZXing) - if (processingTask != null && !processingTask.IsCompleted) - return false; - - var elapsedTimeMs = (DateTime.UtcNow - lastPreviewAnalysis).TotalMilliseconds; - if (elapsedTimeMs < scannerHost.ScanningOptions.DelayBetweenAnalyzingFrames) - return false; - - // Delay a minimum between scans - if (wasScanned && elapsedTimeMs < scannerHost.ScanningOptions.DelayBetweenContinuousScans) - return false; - - return true; - } - } - - void HandleOnPreviewFrameReady(object sender, FastJavaByteArray fastArray) - { - if (!CanAnalyzeFrame) - return; - - wasScanned = false; - lastPreviewAnalysis = DateTime.UtcNow; - - processingTask = Task.Run(() => - { - try - { - DecodeFrame(fastArray); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - }).ContinueWith(task => - { - if (task.IsFaulted) - Android.Util.Log.Debug(MobileBarcodeScanner.TAG, "DecodeFrame exception occurs"); - }, TaskContinuationOptions.OnlyOnFaulted); - } - - void DecodeFrame(FastJavaByteArray fastArray) - { - var cameraParameters = cameraController.Camera.GetParameters(); - var width = cameraParameters.PreviewSize.Width; - var height = cameraParameters.PreviewSize.Height; - - var barcodeReader = scannerHost.ScanningOptions.BuildBarcodeReader(); - - var rotate = false; - var newWidth = width; - var newHeight = height; - - // use last value for performance gain - var cDegrees = cameraController.LastCameraDisplayOrientationDegree; - - if (cDegrees == 90 || cDegrees == 270) - { - rotate = true; - newWidth = height; - newHeight = width; - } - - ZXing.Result result = null; - var start = PerformanceCounter.Start(); - - LuminanceSource fast = new FastJavaByteArrayYUVLuminanceSource(fastArray, width, height, 0, 0, width, height); // _area.Left, _area.Top, _area.Width, _area.Height); - if (rotate) - fast = fast.rotateCounterClockwise(); - - result = barcodeReader.Decode(fast); - - fastArray.Dispose(); - fastArray = null; - - PerformanceCounter.Stop(start, - "Decode Time: {0} ms (width: " + width + ", height: " + height + ", degrees: " + cDegrees + ", rotate: " + - rotate + ")"); - - if (result != null) - { - Android.Util.Log.Debug(MobileBarcodeScanner.TAG, "Barcode Found"); - - wasScanned = true; - BarcodeFound?.Invoke(result); - return; - } - } - } + public class CameraAnalyzer + { + readonly CameraController cameraController; + readonly CameraEventsListener cameraEventListener; + Task processingTask; + DateTime lastPreviewAnalysis = DateTime.UtcNow; + bool wasScanned; + IScannerSessionHost scannerHost; + + public CameraAnalyzer(SurfaceView surfaceView, IScannerSessionHost scannerHost) + { + this.scannerHost = scannerHost; + cameraEventListener = new CameraEventsListener(); + cameraController = new CameraController(surfaceView, cameraEventListener, scannerHost); + Torch = new Torch(cameraController, surfaceView.Context); + } + + public Action BarcodeFound; + + public Torch Torch { get; } + + public bool IsAnalyzing { get; private set; } + + public void PauseAnalysis() + => IsAnalyzing = false; + + public void ResumeAnalysis() + => IsAnalyzing = true; + + public void ShutdownCamera() + { + IsAnalyzing = false; + cameraEventListener.OnPreviewFrameReady -= HandleOnPreviewFrameReady; + cameraController.ShutdownCamera(); + } + + public void SetupCamera() + { + cameraEventListener.OnPreviewFrameReady += HandleOnPreviewFrameReady; + cameraController.SetupCamera(); + } + + public void AutoFocus() + => cameraController.AutoFocus(); + + public void AutoFocus(int x, int y) + => cameraController.AutoFocus(x, y); + + public void RefreshCamera() + => cameraController.RefreshCamera(); + + bool CanAnalyzeFrame + { + get + { + if (!IsAnalyzing) + return false; + + //Check and see if we're still processing a previous frame + // todo: check if we can run as many as possible or mby run two analyzers at once (Vision + ZXing) + if (processingTask != null && !processingTask.IsCompleted) + return false; + + var elapsedTimeMs = (DateTime.UtcNow - lastPreviewAnalysis).TotalMilliseconds; + if (elapsedTimeMs < scannerHost.ScanningOptions.DelayBetweenAnalyzingFrames) + return false; + + // Delay a minimum between scans + if (wasScanned && elapsedTimeMs < scannerHost.ScanningOptions.DelayBetweenContinuousScans) + return false; + + return true; + } + } + + void HandleOnPreviewFrameReady(object sender, FastJavaByteArray fastArray) + { + if (!CanAnalyzeFrame) + return; + + wasScanned = false; + lastPreviewAnalysis = DateTime.UtcNow; + + processingTask = Task.Run(() => + { + try + { + DecodeFrame(fastArray); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + }).ContinueWith(task => + { + if (task.IsFaulted) + Android.Util.Log.Debug(MobileBarcodeScanner.TAG, "DecodeFrame exception occurs"); + }, TaskContinuationOptions.OnlyOnFaulted); + } + + void DecodeFrame(FastJavaByteArray fastArray) + { + var cameraParameters = cameraController.Camera.GetParameters(); + var width = cameraParameters.PreviewSize.Width; + var height = cameraParameters.PreviewSize.Height; + + var rotate = false; + var newWidth = width; + var newHeight = height; + + // use last value for performance gain + var cDegrees = cameraController.LastCameraDisplayOrientationDegree; + + if (cDegrees == 90 || cDegrees == 270) + { + rotate = true; + newWidth = height; + newHeight = width; + } + + ZXing.Result result = null; + var start = PerformanceCounter.Start(); + + var scanningRect = scannerHost.ScanningOptions.ScanningArea; + if (rotate) + { + scanningRect = scanningRect.RotateCounterClockwise(); + } + + var left = (int)(width * scanningRect.StartX); + var top = (int)(height * scanningRect.StartY); + var endHeight = (int)(scanningRect.EndY * height) - top; + var endWidth = (int)(scanningRect.EndX * width) - left; + + LuminanceSource fast = + new FastJavaByteArrayYUVLuminanceSource( + fastArray, + width, + height, + left, + top, + endWidth, + endHeight); + + if (rotate) + { + fast = fast.rotateCounterClockwise(); + } + + var barcodeReader = scannerHost.ScanningOptions.BuildBarcodeReader(); + result = barcodeReader.Decode(fast); + + fastArray.Dispose(); + fastArray = null; + + PerformanceCounter.Stop(start, + "Decode Time: {0} ms (width: " + width + ", height: " + height + ", degrees: " + cDegrees + + ", rotate: " + + rotate + ")"); + + if (result != null) + { + Android.Util.Log.Debug(MobileBarcodeScanner.TAG, "Barcode Found"); + + wasScanned = true; + BarcodeFound?.Invoke(result); + return; + } + } + } } \ No newline at end of file From f5a8e11f375242fe60021aa5589ab9373dedd3b1 Mon Sep 17 00:00:00 2001 From: Dominik Przywara Date: Thu, 9 Jul 2020 23:17:41 +0200 Subject: [PATCH 3/6] Added narrowing in iOS with sample --- Samples/Sample.iOS/HomeViewController.cs | 32 ++- ZXing.Net.Mobile/ZXing.Net.Mobile.csproj | 6 + .../CVPixelBufferBGRA32LuminanceSource.ios.cs | 200 ++++++++++++++++-- ZXing.Net.Mobile/iOS/Helpers/Rect.cs | 29 +++ ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs | 32 ++- 5 files changed, 258 insertions(+), 41 deletions(-) create mode 100644 ZXing.Net.Mobile/iOS/Helpers/Rect.cs diff --git a/Samples/Sample.iOS/HomeViewController.cs b/Samples/Sample.iOS/HomeViewController.cs index 7287c6950..80d45d715 100644 --- a/Samples/Sample.iOS/HomeViewController.cs +++ b/Samples/Sample.iOS/HomeViewController.cs @@ -1,10 +1,6 @@ using System; using MonoTouch.Dialog; - -using Foundation; -using CoreGraphics; using UIKit; - using ZXing; using ZXing.Mobile; using System.Collections.Generic; @@ -25,10 +21,27 @@ public override void ViewDidLoad() //Create a new instance of our scanner scanner = new MobileBarcodeScanner(this.NavigationController); - Root = new RootElement("ZXing.Net.Mobile") { + Root = new RootElement("ZXing.Net.Mobile") + { new Section { + new StyledStringElement("Scan with Default View", async () => + { + //Tell our scanner to use the default overlay + scanner.UseCustomOverlay = false; + //We can customize the top and bottom text of the default overlay + scanner.TopText = "Hold camera up to barcode to scan"; + scanner.BottomText = "Barcode will automatically scan"; + + //Start scanning + var result = await scanner.Scan(new MobileBarcodeScanningOptions + { + ScanningArea = ScanningArea.From(0f, 0.3f, 1f, 0.7f) + }); - new StyledStringElement ("Scan with Default View", async () => { + HandleScanResult(result); + }), + new StyledStringElement("Scan with Default View using laser point", async () => + { //Tell our scanner to use the default overlay scanner.UseCustomOverlay = false; //We can customize the top and bottom text of the default overlay @@ -36,7 +49,10 @@ public override void ViewDidLoad() scanner.BottomText = "Barcode will automatically scan"; //Start scanning - var result = await scanner.Scan (); + var result = await scanner.Scan(new MobileBarcodeScanningOptions + { + ScanningArea = ScanningArea.From(0f, 0.49f, 1f, 0.51f) + }); HandleScanResult(result); }), @@ -145,4 +161,4 @@ public void UITestBackdoorScan(string param) }); } } -} +} \ No newline at end of file diff --git a/ZXing.Net.Mobile/ZXing.Net.Mobile.csproj b/ZXing.Net.Mobile/ZXing.Net.Mobile.csproj index da00f48fd..d8f27b0e3 100644 --- a/ZXing.Net.Mobile/ZXing.Net.Mobile.csproj +++ b/ZXing.Net.Mobile/ZXing.Net.Mobile.csproj @@ -53,6 +53,9 @@ + + + @@ -127,4 +130,7 @@ + + + \ No newline at end of file diff --git a/ZXing.Net.Mobile/iOS/CVPixelBufferBGRA32LuminanceSource.ios.cs b/ZXing.Net.Mobile/iOS/CVPixelBufferBGRA32LuminanceSource.ios.cs index 23867d963..9206a136b 100644 --- a/ZXing.Net.Mobile/iOS/CVPixelBufferBGRA32LuminanceSource.ios.cs +++ b/ZXing.Net.Mobile/iOS/CVPixelBufferBGRA32LuminanceSource.ios.cs @@ -1,34 +1,202 @@ using System; -using System.Runtime.InteropServices; -using CoreVideo; -using ZXing; +using ZXing.Mobile.iOS.Helpers; namespace ZXing.Mobile { public class CVPixelBufferBGRA32LuminanceSource : BaseLuminanceSource { - public unsafe CVPixelBufferBGRA32LuminanceSource(byte* cvPixelByteArray, int cvPixelByteArrayLength, int width, int height) - : base(width, height) => CalculateLuminance(cvPixelByteArray, cvPixelByteArrayLength); + private readonly bool shouldRotate; - public CVPixelBufferBGRA32LuminanceSource(byte[] luminances, int width, int height) : base(luminances, width, height) + public CVPixelBufferBGRA32LuminanceSource( + Span cvPixelByteArray, + bool shouldRotate, + int originalImageWidth, + int originalImageHeight, + ScanningArea scanningArea) + : base(0, 0) // this is not an mistake. We calculate those values later on { + this.shouldRotate = shouldRotate; + + var destinationRect = PrepareDestinationImageRect(scanningArea, originalImageWidth, originalImageHeight); + SetupLuminanceArray(destinationRect); + + CalculateLuminance(cvPixelByteArray, originalImageWidth, destinationRect); } - unsafe void CalculateLuminance(byte* rgbRawBytes, int bytesLen) + protected CVPixelBufferBGRA32LuminanceSource(byte[] luminances, int width, int height) : base(luminances, width, + height) { - for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < bytesLen && luminanceIndex < luminances.Length; luminanceIndex++) + } + + void CalculateLuminance(Span rgbRawBytes, int originalImageWidth, Rect destinationRect) + { + if (shouldRotate) + { + CalculateLuminanceWithCroppingAndRotation(rgbRawBytes, originalImageWidth, destinationRect); + } + else { - // Calculate luminance cheaply, favoring green. - var b = rgbRawBytes[rgbIndex++]; - var g = rgbRawBytes[rgbIndex++]; - var r = rgbRawBytes[rgbIndex++]; - var alpha = rgbRawBytes[rgbIndex++]; - var luminance = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight); - luminances[luminanceIndex] = (byte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8)); + CalculateLuminanceWithCropping(rgbRawBytes, originalImageWidth, destinationRect); } } protected override LuminanceSource CreateLuminanceSource(byte[] newLuminances, int width, int height) => new CVPixelBufferBGRA32LuminanceSource(newLuminances, width, height); + + + void CalculateLuminanceWithCroppingAndRotation( + Span rgbRawBytes, + int originalImageWidth, + Rect destinationRect) + { + for (int x = 0, y = 0, destinationX = 0, destinationY = 0, rgbIndex = 0; rgbIndex < rgbRawBytes.Length; x++) + { + //Follow the current Y. Because image in memory is represented row after row + //we increment Y each time we've read whole line + if (x == originalImageWidth) + { + x = 0; + ++y; + } + + //Check if the current coordinates are outside of destination ScanningArea. + //We flip the values because of rotation + if (destinationRect.Outside(y, x)) + { + //Pixel in memory is represented by 4 bytes (BGRA) therefore we skip whole pixel. + rgbIndex += 4; + continue; + } + + //Because of the rotation and consecutive reading row by row of original image + //we fulfill destination image column by column. + if (destinationY == destinationRect.Height) + { + destinationY = 0; + destinationX++; + } + + var index = destinationX + (destinationY * destinationRect.Width); + luminances[index] = CalculateLuminance(rgbRawBytes, ref rgbIndex); + + //Because of the rotation and consecutive reading row by row of original image + //we fulfill destination image column by column. + destinationY++; + } + } + + void CalculateLuminanceWithCropping( + Span rgbRawBytes, + int originalImageWidth, + Rect destinationRect) + { + for (int x = 0, y = 0, destinationX = 0, destinationY = 0, rgbIndex = 0; rgbIndex < rgbRawBytes.Length; x++) + { + if (x == originalImageWidth) + { + x = 0; + ++y; + } + + //Check if the current coordinates are outside of destination ScanningArea. + if (destinationRect.Outside(x, y)) + { + //Pixel in memory is represented by 4 bytes (BGRA) therefore we skip whole pixel. + rgbIndex += 4; + continue; + } + + //Fill data row by row + if (destinationX == destinationRect.Width) + { + destinationX = 0; + destinationY++; + } + + var index = destinationX + (destinationY * destinationRect.Width); + luminances[index] = CalculateLuminance(rgbRawBytes, ref rgbIndex); + + destinationX++; + } + } + + byte CalculateLuminance(Span rgbRawBytes, ref int rgbIndex) + { + // Calculate luminance cheaply, favoring green. + var b = rgbRawBytes[rgbIndex++]; + var g = rgbRawBytes[rgbIndex++]; + var r = rgbRawBytes[rgbIndex++]; + var alpha = rgbRawBytes[rgbIndex++]; + var luminance = (byte) ((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> + ChannelWeight); + + return (byte) (((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8)); + } + + Rect PrepareDestinationImageRect(ScanningArea area, int width, int height) + { + int left, top, right, bottom = 0; + + if (shouldRotate) + { + //this ones are flipped because we are rotating destination image + left = (int) (height * area.StartX); + right = (int) (height * area.EndX); + + top = (int) (width * area.StartY); + bottom = (int) (width * area.EndY); + + //Flip values because we are rotating destination image + var temp = height; + height = width; + width = temp; + } + else + { + left = (int) (width * area.StartX); + right = (int) (width * area.EndX); + + top = (int) (height * area.StartY); + bottom = (int) (height * area.EndY); + } + + //Internally ZXing.Net uses minimum 15 rows, so we want to match that + const int minimalAmountOfRows = 16; + if (bottom - top < minimalAmountOfRows) + { + var croppedHeight = bottom - top; + if (croppedHeight % 2 != 0) + { + ++croppedHeight; + } + + var diff = croppedHeight >> 1; + + //Compensate the difference both from bottom and top + if (bottom + diff < height) + { + bottom += diff; + top -= diff; + } + else + { + //to prevent bottom coordinate to go outside of the scope + //we move the additional pixels above. + var rest = Math.Abs(height - bottom - diff); + top -= diff + rest; + bottom += rest; + } + } + + + return new Rect(left, top, right, bottom); + } + + void SetupLuminanceArray(Rect rect) + { + Width = rect.Width; + Height = rect.Height; + luminances = new byte[Width * Height]; + } } -} +} \ No newline at end of file diff --git a/ZXing.Net.Mobile/iOS/Helpers/Rect.cs b/ZXing.Net.Mobile/iOS/Helpers/Rect.cs new file mode 100644 index 000000000..b8ce0c6f4 --- /dev/null +++ b/ZXing.Net.Mobile/iOS/Helpers/Rect.cs @@ -0,0 +1,29 @@ +namespace ZXing.Mobile.iOS.Helpers +{ + class Rect + { + public readonly int Left; + public readonly int Right; + public readonly int Top; + public readonly int Bottom; + + public readonly int Width; + public readonly int Height; + + public Rect(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + + Width = Right - Left; + Height = Bottom - Top; + } + + public bool Outside(int x, int y) + { + return Left > x || Right <= x || Top > y || Bottom <= y; + } + } +} \ No newline at end of file diff --git a/ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs b/ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs index 406cd0b4c..a18c22c7f 100644 --- a/ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs +++ b/ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs @@ -14,9 +14,6 @@ using ObjCRuntime; using UIKit; -using ZXing.Common; -using ZXing.Mobile; - namespace ZXing.Mobile { public class ZXingScannerView : UIView, IZXingScanner, IScannerSessionHost @@ -232,8 +229,8 @@ bool SetupCaptureSession() var barcodeReader = ScanningOptions.BuildBarcodeReader(); - outputRecorder = new OutputRecorder(this, img => - { + outputRecorder = new OutputRecorder(shouldRotatePreviewBuffer, this, (img ) => + { var ls = img; if (!IsAnalyzing) @@ -242,10 +239,6 @@ bool SetupCaptureSession() try { var perfDecode = PerformanceCounter.Start(); - - if (shouldRotatePreviewBuffer) - ls = ls.rotateCounterClockwise(); - var result = barcodeReader.Decode(ls); PerformanceCounter.Stop(perfDecode, "Decode Time: {0} ms"); @@ -402,14 +395,16 @@ public void Focus(PointF pointOfInterest) public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate { - public OutputRecorder(IScannerSessionHost scannerHost, Func handleImage) : base() + public OutputRecorder(bool shouldRotateCounterClockwise, IScannerSessionHost scannerHost, Func handleImage) : base() { - this.handleImage = handleImage; + this.shouldRotateCounterClockwise = shouldRotateCounterClockwise; + this.handleImage = handleImage; this.scannerHost = scannerHost; } IScannerSessionHost scannerHost; - Func handleImage; + Func handleImage; + readonly bool shouldRotateCounterClockwise; DateTime lastAnalysis = DateTime.MinValue; volatile bool working = false; @@ -463,11 +458,14 @@ public override void DidOutputSampleBuffer(AVCaptureOutput captureOutput, CMSamp // Let's access the raw underlying data and create a luminance source from it unsafe { - var rawData = (byte*)pixelBuffer.BaseAddress.ToPointer(); - var rawDatalen = (int)(pixelBuffer.Height * pixelBuffer.Width * 4); //This drops 8 bytes from the original length to give us the expected length - - luminanceSource = new CVPixelBufferBGRA32LuminanceSource(rawData, rawDatalen, (int)pixelBuffer.Width, (int)pixelBuffer.Height); - } + var rawData = new Span(pixelBuffer.BaseAddress.ToPointer(), (int)(pixelBuffer.Width * pixelBuffer.Height * 4)); + luminanceSource = new CVPixelBufferBGRA32LuminanceSource( + rawData, + shouldRotateCounterClockwise, + (int)pixelBuffer.Width, + (int)pixelBuffer.Height, + scannerHost.ScanningOptions.ScanningArea); + } if (handleImage(luminanceSource)) wasScanned = true; From 33718be6144bada5102afaa8f663ac2ffe2634a4 Mon Sep 17 00:00:00 2001 From: Dominik Przywara Date: Thu, 9 Jul 2020 23:17:59 +0200 Subject: [PATCH 4/6] Added samples to Forms project --- Samples/Sample.Forms/Sample.Forms/HomePage.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Samples/Sample.Forms/Sample.Forms/HomePage.cs b/Samples/Sample.Forms/Sample.Forms/HomePage.cs index 90f4fbd85..0ddb8ded8 100644 --- a/Samples/Sample.Forms/Sample.Forms/HomePage.cs +++ b/Samples/Sample.Forms/Sample.Forms/HomePage.cs @@ -4,6 +4,8 @@ using System.Text; using System.Threading.Tasks; using Xamarin.Forms; +using ZXing; +using ZXing.Mobile; using ZXing.Net.Mobile.Forms; namespace Sample.Forms @@ -17,6 +19,7 @@ public class HomePage : ContentPage Button buttonScanContinuousCustomPage; Button buttonScanCustomPage; Button buttonGenerateBarcode; + Button buttonScanMiddle1D; public HomePage() : base() @@ -130,8 +133,36 @@ public HomePage() : base() await Navigation.PushAsync(new BarcodePage()); }; + buttonScanMiddle1D = new Button + { + Text = "Scan 1D only in the middle (Android and iOS only)", + AutomationId = "barcodeMiddleScan1D" + }; + buttonScanMiddle1D.Clicked += async delegate + { + scanPage = new ZXingScannerPage(new MobileBarcodeScanningOptions + { + ScanningArea = ScanningArea.From(0f, 0.49f, 1f, 0.51f), + PossibleFormats = new List { BarcodeFormat.All_1D } + }); + + scanPage.OnScanResult += (result) => + { + scanPage.IsScanning = false; + + Device.BeginInvokeOnMainThread(async () => + { + await Navigation.PopAsync(); + await DisplayAlert("Scanned Barcode", result.Text, "OK"); + }); + }; + + await Navigation.PushAsync(scanPage); + }; + var stack = new StackLayout(); stack.Children.Add(buttonScanDefaultOverlay); + stack.Children.Add(buttonScanMiddle1D); stack.Children.Add(buttonScanCustomOverlay); stack.Children.Add(buttonScanContinuously); stack.Children.Add(buttonScanCustomPage); From 27649f0a704c9f7f6ffad402efba3074f7395a36 Mon Sep 17 00:00:00 2001 From: Dominik Przywara Date: Thu, 9 Jul 2020 23:20:45 +0200 Subject: [PATCH 5/6] Revert to tabs --- .../CameraAccess/CameraAnalyzer.android.cs | 340 +++++++++--------- 1 file changed, 170 insertions(+), 170 deletions(-) diff --git a/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs b/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs index 70ef53495..17461944d 100644 --- a/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs +++ b/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs @@ -7,174 +7,174 @@ namespace ZXing.Mobile.CameraAccess { - public class CameraAnalyzer - { - readonly CameraController cameraController; - readonly CameraEventsListener cameraEventListener; - Task processingTask; - DateTime lastPreviewAnalysis = DateTime.UtcNow; - bool wasScanned; - IScannerSessionHost scannerHost; - - public CameraAnalyzer(SurfaceView surfaceView, IScannerSessionHost scannerHost) - { - this.scannerHost = scannerHost; - cameraEventListener = new CameraEventsListener(); - cameraController = new CameraController(surfaceView, cameraEventListener, scannerHost); - Torch = new Torch(cameraController, surfaceView.Context); - } - - public Action BarcodeFound; - - public Torch Torch { get; } - - public bool IsAnalyzing { get; private set; } - - public void PauseAnalysis() - => IsAnalyzing = false; - - public void ResumeAnalysis() - => IsAnalyzing = true; - - public void ShutdownCamera() - { - IsAnalyzing = false; - cameraEventListener.OnPreviewFrameReady -= HandleOnPreviewFrameReady; - cameraController.ShutdownCamera(); - } - - public void SetupCamera() - { - cameraEventListener.OnPreviewFrameReady += HandleOnPreviewFrameReady; - cameraController.SetupCamera(); - } - - public void AutoFocus() - => cameraController.AutoFocus(); - - public void AutoFocus(int x, int y) - => cameraController.AutoFocus(x, y); - - public void RefreshCamera() - => cameraController.RefreshCamera(); - - bool CanAnalyzeFrame - { - get - { - if (!IsAnalyzing) - return false; - - //Check and see if we're still processing a previous frame - // todo: check if we can run as many as possible or mby run two analyzers at once (Vision + ZXing) - if (processingTask != null && !processingTask.IsCompleted) - return false; - - var elapsedTimeMs = (DateTime.UtcNow - lastPreviewAnalysis).TotalMilliseconds; - if (elapsedTimeMs < scannerHost.ScanningOptions.DelayBetweenAnalyzingFrames) - return false; - - // Delay a minimum between scans - if (wasScanned && elapsedTimeMs < scannerHost.ScanningOptions.DelayBetweenContinuousScans) - return false; - - return true; - } - } - - void HandleOnPreviewFrameReady(object sender, FastJavaByteArray fastArray) - { - if (!CanAnalyzeFrame) - return; - - wasScanned = false; - lastPreviewAnalysis = DateTime.UtcNow; - - processingTask = Task.Run(() => - { - try - { - DecodeFrame(fastArray); - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - }).ContinueWith(task => - { - if (task.IsFaulted) - Android.Util.Log.Debug(MobileBarcodeScanner.TAG, "DecodeFrame exception occurs"); - }, TaskContinuationOptions.OnlyOnFaulted); - } - - void DecodeFrame(FastJavaByteArray fastArray) - { - var cameraParameters = cameraController.Camera.GetParameters(); - var width = cameraParameters.PreviewSize.Width; - var height = cameraParameters.PreviewSize.Height; - - var rotate = false; - var newWidth = width; - var newHeight = height; - - // use last value for performance gain - var cDegrees = cameraController.LastCameraDisplayOrientationDegree; - - if (cDegrees == 90 || cDegrees == 270) - { - rotate = true; - newWidth = height; - newHeight = width; - } - - ZXing.Result result = null; - var start = PerformanceCounter.Start(); - - var scanningRect = scannerHost.ScanningOptions.ScanningArea; - if (rotate) - { - scanningRect = scanningRect.RotateCounterClockwise(); - } - - var left = (int)(width * scanningRect.StartX); - var top = (int)(height * scanningRect.StartY); - var endHeight = (int)(scanningRect.EndY * height) - top; - var endWidth = (int)(scanningRect.EndX * width) - left; - - LuminanceSource fast = - new FastJavaByteArrayYUVLuminanceSource( - fastArray, - width, - height, - left, - top, - endWidth, - endHeight); - - if (rotate) - { - fast = fast.rotateCounterClockwise(); - } - - var barcodeReader = scannerHost.ScanningOptions.BuildBarcodeReader(); - result = barcodeReader.Decode(fast); - - fastArray.Dispose(); - fastArray = null; - - PerformanceCounter.Stop(start, - "Decode Time: {0} ms (width: " + width + ", height: " + height + ", degrees: " + cDegrees + - ", rotate: " + - rotate + ")"); - - if (result != null) - { - Android.Util.Log.Debug(MobileBarcodeScanner.TAG, "Barcode Found"); - - wasScanned = true; - BarcodeFound?.Invoke(result); - return; - } - } - } + public class CameraAnalyzer + { + readonly CameraController cameraController; + readonly CameraEventsListener cameraEventListener; + Task processingTask; + DateTime lastPreviewAnalysis = DateTime.UtcNow; + bool wasScanned; + IScannerSessionHost scannerHost; + + public CameraAnalyzer(SurfaceView surfaceView, IScannerSessionHost scannerHost) + { + this.scannerHost = scannerHost; + cameraEventListener = new CameraEventsListener(); + cameraController = new CameraController(surfaceView, cameraEventListener, scannerHost); + Torch = new Torch(cameraController, surfaceView.Context); + } + + public Action BarcodeFound; + + public Torch Torch { get; } + + public bool IsAnalyzing { get; private set; } + + public void PauseAnalysis() + => IsAnalyzing = false; + + public void ResumeAnalysis() + => IsAnalyzing = true; + + public void ShutdownCamera() + { + IsAnalyzing = false; + cameraEventListener.OnPreviewFrameReady -= HandleOnPreviewFrameReady; + cameraController.ShutdownCamera(); + } + + public void SetupCamera() + { + cameraEventListener.OnPreviewFrameReady += HandleOnPreviewFrameReady; + cameraController.SetupCamera(); + } + + public void AutoFocus() + => cameraController.AutoFocus(); + + public void AutoFocus(int x, int y) + => cameraController.AutoFocus(x, y); + + public void RefreshCamera() + => cameraController.RefreshCamera(); + + bool CanAnalyzeFrame + { + get + { + if (!IsAnalyzing) + return false; + + //Check and see if we're still processing a previous frame + // todo: check if we can run as many as possible or mby run two analyzers at once (Vision + ZXing) + if (processingTask != null && !processingTask.IsCompleted) + return false; + + var elapsedTimeMs = (DateTime.UtcNow - lastPreviewAnalysis).TotalMilliseconds; + if (elapsedTimeMs < scannerHost.ScanningOptions.DelayBetweenAnalyzingFrames) + return false; + + // Delay a minimum between scans + if (wasScanned && elapsedTimeMs < scannerHost.ScanningOptions.DelayBetweenContinuousScans) + return false; + + return true; + } + } + + void HandleOnPreviewFrameReady(object sender, FastJavaByteArray fastArray) + { + if (!CanAnalyzeFrame) + return; + + wasScanned = false; + lastPreviewAnalysis = DateTime.UtcNow; + + processingTask = Task.Run(() => + { + try + { + DecodeFrame(fastArray); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + }).ContinueWith(task => + { + if (task.IsFaulted) + Android.Util.Log.Debug(MobileBarcodeScanner.TAG, "DecodeFrame exception occurs"); + }, TaskContinuationOptions.OnlyOnFaulted); + } + + void DecodeFrame(FastJavaByteArray fastArray) + { + var cameraParameters = cameraController.Camera.GetParameters(); + var width = cameraParameters.PreviewSize.Width; + var height = cameraParameters.PreviewSize.Height; + + var rotate = false; + var newWidth = width; + var newHeight = height; + + // use last value for performance gain + var cDegrees = cameraController.LastCameraDisplayOrientationDegree; + + if (cDegrees == 90 || cDegrees == 270) + { + rotate = true; + newWidth = height; + newHeight = width; + } + + ZXing.Result result = null; + var start = PerformanceCounter.Start(); + + var scanningRect = scannerHost.ScanningOptions.ScanningArea; + if (rotate) + { + scanningRect = scanningRect.RotateCounterClockwise(); + } + + var left = (int) (width * scanningRect.StartX); + var top = (int) (height * scanningRect.StartY); + var endHeight = (int) (scanningRect.EndY * height) - top; + var endWidth = (int) (scanningRect.EndX * width) - left; + + LuminanceSource fast = + new FastJavaByteArrayYUVLuminanceSource( + fastArray, + width, + height, + left, + top, + endWidth, + endHeight); + + if (rotate) + { + fast = fast.rotateCounterClockwise(); + } + + var barcodeReader = scannerHost.ScanningOptions.BuildBarcodeReader(); + result = barcodeReader.Decode(fast); + + fastArray.Dispose(); + fastArray = null; + + PerformanceCounter.Stop(start, + "Decode Time: {0} ms (width: " + width + ", height: " + height + ", degrees: " + cDegrees + + ", rotate: " + + rotate + ")"); + + if (result != null) + { + Android.Util.Log.Debug(MobileBarcodeScanner.TAG, "Barcode Found"); + + wasScanned = true; + BarcodeFound?.Invoke(result); + return; + } + } + } } \ No newline at end of file From a8b846e0edab032afb4aa170f27ce6bbca4e3ad9 Mon Sep 17 00:00:00 2001 From: Dominik Przywara Date: Thu, 9 Jul 2020 23:26:33 +0200 Subject: [PATCH 6/6] Revert to tabs --- .../CameraAccess/CameraAnalyzer.android.cs | 5 +- .../MobileBarcodeScanningOptions.shared.cs | 14 +- ZXing.Net.Mobile/ScanningArea.cs | 203 +++++++++--------- ZXing.Net.Mobile/iOS/Helpers/Rect.cs | 44 ++-- ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs | 14 +- 5 files changed, 139 insertions(+), 141 deletions(-) diff --git a/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs b/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs index 17461944d..cd073b8ae 100644 --- a/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs +++ b/ZXing.Net.Mobile/Android/CameraAccess/CameraAnalyzer.android.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Security.AccessControl; using System.Threading.Tasks; using Android.Views; using ApxLabs.FastAndroidCamera; @@ -163,8 +161,7 @@ void DecodeFrame(FastJavaByteArray fastArray) fastArray = null; PerformanceCounter.Stop(start, - "Decode Time: {0} ms (width: " + width + ", height: " + height + ", degrees: " + cDegrees + - ", rotate: " + + "Decode Time: {0} ms (width: " + width + ", height: " + height + ", degrees: " + cDegrees + ", rotate: " + rotate + ")"); if (result != null) diff --git a/ZXing.Net.Mobile/MobileBarcodeScanningOptions.shared.cs b/ZXing.Net.Mobile/MobileBarcodeScanningOptions.shared.cs index 9d819fb31..580b0f019 100644 --- a/ZXing.Net.Mobile/MobileBarcodeScanningOptions.shared.cs +++ b/ZXing.Net.Mobile/MobileBarcodeScanningOptions.shared.cs @@ -21,19 +21,19 @@ public MobileBarcodeScanningOptions() InitialDelayBeforeAnalyzingFrames = 300; DelayBetweenContinuousScans = 1000; UseNativeScanning = false; - ScanningArea = ScanningArea.Default; - } + ScanningArea = ScanningArea.Default; + } public CameraResolutionSelectorDelegate CameraResolutionSelector { get; set; } public IEnumerable PossibleFormats { get; set; } /// - /// Narrow chosen scanning area.
+ /// Narrow chosen scanning area.
/// Works only on iOS and Android - ///
- public ScanningArea ScanningArea { get; set; } - + /// + public ScanningArea ScanningArea { get; set; } + public bool? TryHarder { get; set; } public bool? PureBarcode { get; set; } @@ -125,4 +125,4 @@ public CameraResolution GetResolution(List availableResolution return r; } } -} +} \ No newline at end of file diff --git a/ZXing.Net.Mobile/ScanningArea.cs b/ZXing.Net.Mobile/ScanningArea.cs index 1ac9486f3..7710edf2a 100644 --- a/ZXing.Net.Mobile/ScanningArea.cs +++ b/ZXing.Net.Mobile/ScanningArea.cs @@ -2,105 +2,106 @@ namespace ZXing.Mobile { - /// - /// Representation of restricted scanning area in PERCENTAGE. - /// Allowed values: 0 <= value <= 1 AND startY != endY - /// Values of startY and endY are ABSOLUTE to image that means if use values of - /// startY:0.49 and endY:0.51 we will scan only 2% of the whole image - /// starting at 49% and finishing at 51% of the image height. - /// - public class ScanningArea - { - public float StartX { get; } - public float StartY { get; } - public float EndX { get; } - public float EndY { get; } - - ScanningArea(float startX, float startY, float endX, float endY) - { - //if difference between parameters is less than 1% we assume those are equal - if (Math.Abs(startY - endY) < 0.01f) - { - throw new ArgumentException($"Values of {nameof(startY)} and {nameof(endY)} cannot be the same"); - } - - //if difference between parameters is less than 1% we assume those are equal - if (Math.Abs(startX - endX) < 0.01f) - { - throw new ArgumentException($"Values of {nameof(startX)} and {nameof(endX)} cannot be the same"); - } - - //Reverse values instead of throwing argument exception - if (startY > endY) - { - var temp = endY; - endY = startY; - startY = temp; - } - - if (startX > endX) - { - var temp = endX; - endX = startX; - startX = temp; - } - - if (startY < 0) - { - startY = 0; - } - - if (endY > 1) - { - endY = 1; - } - - if (startX < 0) - { - startX = 0; - } - - if (endX > 1) - { - endX = 1; - } - - StartY = startY; - EndY = endY; - StartX = startX; - EndX = endX; - } - - public ScanningArea RotateCounterClockwise() - { - var startX = StartY; - var startY = EndX; - var endX = EndY; - var endY = StartX; - - if (startY > endY) - { - startY = 1f - startY; - endY = 1 - endY; - } - - if (startX > endX) - { - startX = 1 - startX; - endX = 1 - endX; - } - - return new ScanningArea(startX, startY, endX, endY); - } - - - static ScanningArea _default = new ScanningArea(0f, 0f, 1f, 1f); - - /// - /// Returns value that represents whole image. - /// - public static ScanningArea Default => _default; - - public static ScanningArea From(float startX, float startY, float endX, float endY) => new ScanningArea(startX, startY, endX, endY); - } + /// + /// Representation of restricted scanning area in PERCENTAGE. + /// Allowed values: 0 <= value <= 1 AND startY != endY + /// Values of startY and endY are ABSOLUTE to image that means if use values of + /// startY:0.49 and endY:0.51 we will scan only 2% of the whole image + /// starting at 49% and finishing at 51% of the image height. + /// + public class ScanningArea + { + public float StartX { get; } + public float StartY { get; } + public float EndX { get; } + public float EndY { get; } + + ScanningArea(float startX, float startY, float endX, float endY) + { + //if difference between parameters is less than 1% we assume those are equal + if (Math.Abs(startY - endY) < 0.01f) + { + throw new ArgumentException($"Values of {nameof(startY)} and {nameof(endY)} cannot be the same"); + } + + //if difference between parameters is less than 1% we assume those are equal + if (Math.Abs(startX - endX) < 0.01f) + { + throw new ArgumentException($"Values of {nameof(startX)} and {nameof(endX)} cannot be the same"); + } + + //Reverse values instead of throwing argument exception + if (startY > endY) + { + var temp = endY; + endY = startY; + startY = temp; + } + + if (startX > endX) + { + var temp = endX; + endX = startX; + startX = temp; + } + + if (startY < 0) + { + startY = 0; + } + + if (endY > 1) + { + endY = 1; + } + + if (startX < 0) + { + startX = 0; + } + + if (endX > 1) + { + endX = 1; + } + + StartY = startY; + EndY = endY; + StartX = startX; + EndX = endX; + } + + public ScanningArea RotateCounterClockwise() + { + var startX = StartY; + var startY = EndX; + var endX = EndY; + var endY = StartX; + + if (startY > endY) + { + startY = 1f - startY; + endY = 1 - endY; + } + + if (startX > endX) + { + startX = 1 - startX; + endX = 1 - endX; + } + + return new ScanningArea(startX, startY, endX, endY); + } + + + static ScanningArea _default = new ScanningArea(0f, 0f, 1f, 1f); + + /// + /// Returns value that represents whole image. + /// + public static ScanningArea Default => _default; + + public static ScanningArea From(float startX, float startY, float endX, float endY) => + new ScanningArea(startX, startY, endX, endY); + } } \ No newline at end of file diff --git a/ZXing.Net.Mobile/iOS/Helpers/Rect.cs b/ZXing.Net.Mobile/iOS/Helpers/Rect.cs index b8ce0c6f4..35be25ed4 100644 --- a/ZXing.Net.Mobile/iOS/Helpers/Rect.cs +++ b/ZXing.Net.Mobile/iOS/Helpers/Rect.cs @@ -1,29 +1,29 @@ namespace ZXing.Mobile.iOS.Helpers { - class Rect - { - public readonly int Left; - public readonly int Right; - public readonly int Top; - public readonly int Bottom; + class Rect + { + public readonly int Left; + public readonly int Right; + public readonly int Top; + public readonly int Bottom; - public readonly int Width; - public readonly int Height; + public readonly int Width; + public readonly int Height; - public Rect(int left, int top, int right, int bottom) - { - Left = left; - Top = top; - Right = right; - Bottom = bottom; + public Rect(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; - Width = Right - Left; - Height = Bottom - Top; - } + Width = Right - Left; + Height = Bottom - Top; + } - public bool Outside(int x, int y) - { - return Left > x || Right <= x || Top > y || Bottom <= y; - } - } + public bool Outside(int x, int y) + { + return Left > x || Right <= x || Top > y || Bottom <= y; + } + } } \ No newline at end of file diff --git a/ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs b/ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs index a18c22c7f..96dc7a6f2 100644 --- a/ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs +++ b/ZXing.Net.Mobile/iOS/ZXingScannerView.ios.cs @@ -230,7 +230,7 @@ bool SetupCaptureSession() var barcodeReader = ScanningOptions.BuildBarcodeReader(); outputRecorder = new OutputRecorder(shouldRotatePreviewBuffer, this, (img ) => - { + { var ls = img; if (!IsAnalyzing) @@ -397,14 +397,14 @@ public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate { public OutputRecorder(bool shouldRotateCounterClockwise, IScannerSessionHost scannerHost, Func handleImage) : base() { - this.shouldRotateCounterClockwise = shouldRotateCounterClockwise; - this.handleImage = handleImage; + this.shouldRotateCounterClockwise = shouldRotateCounterClockwise; + this.handleImage = handleImage; this.scannerHost = scannerHost; } IScannerSessionHost scannerHost; - Func handleImage; - readonly bool shouldRotateCounterClockwise; + Func handleImage; + readonly bool shouldRotateCounterClockwise; DateTime lastAnalysis = DateTime.MinValue; volatile bool working = false; @@ -458,9 +458,9 @@ public override void DidOutputSampleBuffer(AVCaptureOutput captureOutput, CMSamp // Let's access the raw underlying data and create a luminance source from it unsafe { - var rawData = new Span(pixelBuffer.BaseAddress.ToPointer(), (int)(pixelBuffer.Width * pixelBuffer.Height * 4)); + var rawData = new Span(pixelBuffer.BaseAddress.ToPointer(), (int)(pixelBuffer.Width * pixelBuffer.Height * 4)); luminanceSource = new CVPixelBufferBGRA32LuminanceSource( - rawData, + rawData, shouldRotateCounterClockwise, (int)pixelBuffer.Width, (int)pixelBuffer.Height,