Skip to content

Commit

Permalink
1.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
afriscic committed Mar 13, 2024
1 parent 79a1495 commit b8d099b
Show file tree
Hide file tree
Showing 17 changed files with 92 additions and 40 deletions.
18 changes: 15 additions & 3 deletions BarcodeScanning.Native.Maui/BarcodeScanning.Native.Maui.csproj
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net8.0-android;net8.0-ios</TargetFrameworks>
<TargetFrameworks>net8.0;net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<Version>1.2.5</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.3.0</Version>
<Authors>Alen Friščić</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Description>Barcode scanning library for .NET MAUI</Description>
<PackageProjectUrl>https://github.com/afriscic/BarcodeScanning.Native.Maui</PackageProjectUrl>
<PackageTags>.NET MAUI android ios barcode-scanner</PackageTags>
<PackageTags>.NET MAUI android ios macos barcode-scanner</PackageTags>

<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.1</SupportedOSPlatformVersion>
</PropertyGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-android')) != true">
<Compile Remove="**\Android\**\*.cs" />
<None Include="**\Android\**\*.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-ios')) != true AND $(TargetFramework.StartsWith('net8.0-maccatalyst')) != true">
<Compile Remove="**\MaciOS\**\*.cs" />
<None Include="**\MaciOS\**\*.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<ItemGroup Condition="!($(TargetFramework.StartsWith('net')) == true AND $(TargetFramework.EndsWith('.0')) == true AND $(TargetFramework.Contains('-')) != true)">
<Compile Remove="**\NET\**\*.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ namespace BarcodeScanning;

public static partial class Methods
{
private static readonly ParallelOptions parallelOptions = new()
{
MaxDegreeOfParallelism = Environment.ProcessorCount * 2
};

public static async Task<HashSet<BarcodeResult>> ScanFromImage(byte[] imageArray)
=> await ProcessBitmap(await BitmapFactory.DecodeByteArrayAsync(imageArray, 0, imageArray.Length));
public static async Task<HashSet<BarcodeResult>> ScanFromImage(FileResult file)
Expand All @@ -38,11 +43,22 @@ private static async Task<HashSet<BarcodeResult>> ProcessBitmap(Bitmap bitmap)
internal static void InvertLuminance(Image image)
{
var yBuffer = image.GetPlanes()[0].Buffer;
using (var bits = BitSet.ValueOf(yBuffer))
if (yBuffer.IsDirect)
{
bits.Flip(0, bits.Length());
yBuffer.Rewind();
yBuffer.Put(bits.ToByteArray());
unsafe
{
ulong* data = (ulong*)yBuffer.GetDirectBufferAddress();
Parallel.For(0, yBuffer.Capacity() / 8, parallelOptions, (i) => data[i] = ~data[i]);
}
}
else
{
using (var bits = BitSet.ValueOf(yBuffer))
{
bits.Flip(0, bits.Length());
yBuffer.Rewind();
yBuffer.Put(bits.ToByteArray());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public partial class CameraViewHandler
private BarcodeAnalyzer _barcodeAnalyzer;
private BarcodeView _barcodeView;
private UITapGestureRecognizer _uITapGestureRecognizer;
private NSObject _subjectAreaChangedNotificaion;

private readonly object _syncLock = new();
private readonly object _configLock = new();
Expand All @@ -34,7 +35,12 @@ protected override BarcodeView CreatePlatformView()
};
_barcodeView = new BarcodeView(_videoPreviewLayer);
_barcodeView.AddGestureRecognizer(_uITapGestureRecognizer);

_subjectAreaChangedNotificaion = NSNotificationCenter.DefaultCenter.AddObserver(AVCaptureDevice.SubjectAreaDidChangeNotification, (n) =>
{
if (n.Name == AVCaptureDevice.SubjectAreaDidChangeNotification)
ResetFocus();
});

return _barcodeView;
}

Expand Down Expand Up @@ -168,11 +174,7 @@ private void UpdateCamera()

if (_captureDevice is not null)
{
if (_captureDevice.IsFocusModeSupported(AVCaptureFocusMode.ContinuousAutoFocus))
CaptureDeviceLock(() => _captureDevice.FocusMode = AVCaptureFocusMode.ContinuousAutoFocus);
else
CaptureDeviceLock(() => _captureDevice.FocusMode = AVCaptureFocusMode.AutoFocus);

ResetFocus();
_captureInput = new AVCaptureDeviceInput(_captureDevice, out _);

if (_captureSession.CanAddInput(_captureInput))
Expand All @@ -185,7 +187,7 @@ private void UpdateCamera()
UpdateResolution();
}
}

private void UpdateTorch()
{
if (_captureDevice is not null && _captureDevice.HasTorch && _captureDevice.TorchAvailable)
Expand Down Expand Up @@ -223,14 +225,25 @@ private void FocusOnTap()
CaptureDeviceLock(() =>
{
_captureDevice.FocusPointOfInterest = _videoPreviewLayer.CaptureDevicePointOfInterestForPoint(_uITapGestureRecognizer.LocationInView(_barcodeView));
if (_captureDevice.IsFocusModeSupported(AVCaptureFocusMode.ContinuousAutoFocus))
_captureDevice.FocusMode = AVCaptureFocusMode.ContinuousAutoFocus;
else
_captureDevice.FocusMode = AVCaptureFocusMode.AutoFocus;
_captureDevice.FocusMode = AVCaptureFocusMode.AutoFocus;
_captureDevice.SubjectAreaChangeMonitoringEnabled = true;
});
}
}

private void ResetFocus()
{
CaptureDeviceLock(() =>
{
if (_captureDevice.IsFocusModeSupported(AVCaptureFocusMode.ContinuousAutoFocus))
_captureDevice.FocusMode = AVCaptureFocusMode.ContinuousAutoFocus;
else
_captureDevice.FocusMode = AVCaptureFocusMode.AutoFocus;

_captureDevice.SubjectAreaChangeMonitoringEnabled = false;
});
}

private static NSString GetCaptureSessionResolution(CaptureQuality quality)
{
return quality switch
Expand All @@ -248,18 +261,20 @@ private void CaptureDeviceLock(Action handler)
{
lock (_configLock)
{
try
{
_captureDevice.LockForConfiguration(out _);
handler();
}
catch (Exception)
{

}
finally
if (_captureDevice?.LockForConfiguration(out _) ?? false)
{
_captureDevice?.UnlockForConfiguration();
try
{
handler();
}
catch (Exception)
{

}
finally
{
_captureDevice.UnlockForConfiguration();
}
}
}
});
Expand All @@ -273,6 +288,8 @@ private void DisposeView()
{
this.Stop();

_subjectAreaChangedNotificaion?.Dispose();
_subjectAreaChangedNotificaion = null;
_barcodeView?.Dispose();
_barcodeView = null;
_uITapGestureRecognizer?.Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using CoreImage;
using Foundation;
using Microsoft.Maui.Platform;
using System.Text;
using UIKit;
using Vision;

Expand Down Expand Up @@ -42,14 +43,13 @@ internal static HashSet<BarcodeResult> ProcessBarcodeResult(VNBarcodeObservation

foreach (var barcode in result)
{

resultList.Add(new BarcodeResult()
{
BarcodeType = BarcodeTypes.Unknown,
BarcodeFormat = ConvertFromIOSFormats(barcode.Symbology),
DisplayValue = barcode.PayloadStringValue,
RawValue = barcode.PayloadStringValue,
RawBytes = GetRawBytes(barcode) ?? System.Text.Encoding.ASCII.GetBytes(barcode.PayloadStringValue),
RawBytes = GetRawBytes(barcode) ?? Encoding.ASCII.GetBytes(barcode.PayloadStringValue),
BoundingBox = previewLayer?.MapToLayerCoordinates(InvertY(barcode.BoundingBox)).ToRectangle() ?? barcode.BoundingBox.ToRectangle()
});
};
Expand Down Expand Up @@ -107,6 +107,8 @@ internal static VNBarcodeSymbology[] SelectedSymbologies(BarcodeFormats barcodeF
symbologiesList.Add(VNBarcodeSymbology.MicroPdf417);
if (barcodeFormats.HasFlag(BarcodeFormats.QRCode))
symbologiesList.Add(VNBarcodeSymbology.QR);
if (barcodeFormats.HasFlag(BarcodeFormats.Upca))
symbologiesList.Add(VNBarcodeSymbology.Ean13);
if (barcodeFormats.HasFlag(BarcodeFormats.Upce))
symbologiesList.Add(VNBarcodeSymbology.Upce);

Expand Down
File renamed without changes.
3 changes: 2 additions & 1 deletion BarcodeScanning.Test/BarcodeScanning.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios</TargetFrameworks>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<OutputType>Exe</OutputType>
<RootNamespace>BarcodeScanning.Test</RootNamespace>
<UseMaui>true</UseMaui>
Expand All @@ -21,6 +21,7 @@
<ApplicationVersion>1</ApplicationVersion>

<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion BarcodeScanning.Test/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
InputTransparent="True"/>

<Grid ColumnDefinitions="*, *, *, *, *" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,0.1">
<Picker x:Name="Quality" Title="Quality" Grid.Column="2" BackgroundColor="Transparent" WidthRequest="45" HeightRequest="45" SelectedIndexChanged="Quality_SelectedIndexChanged"/>
<Picker x:Name="Quality" Grid.Column="2" BackgroundColor="Transparent" WidthRequest="45" HeightRequest="45" SelectedIndexChanged="Quality_SelectedIndexChanged"/>

<Button Text="C" Grid.Column="0" WidthRequest="50" HeightRequest="50" CornerRadius="25" Clicked="CameraButton_Clicked"/>
<Button Text="T" Grid.Column="1" WidthRequest="50" HeightRequest="50" CornerRadius="25" Clicked="TorchButton_Clicked"/>
Expand Down
2 changes: 2 additions & 0 deletions BarcodeScanning.Test/MainPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public MainPage()
qualitys.Add("Highest");

Quality.ItemsSource = qualitys;
if (DeviceInfo.Platform != DevicePlatform.MacCatalyst)
Quality.Title = "Quality";
}

protected override async void OnAppearing()
Expand Down
2 changes: 2 additions & 0 deletions BarcodeScanning.Test/Platforms/MacCatalyst/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
<key>NSCameraUsageDescription</key>
<string>Camera usage for barcode scanning</string>
</dict>
</plist>
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Barcode scanning library based on native platform APIs for barcode detection:
1. [Google ML Kit](https://developers.google.com/ml-kit)
2. [Apple Vision framework](https://developer.apple.com/documentation/vision)

This library was inspired by existing MAUI barcode scanning libraries: [BarcodeScanner.Mobile](https://github.com/JimmyPun610/BarcodeScanner.Mobile) & [Zxing.Net.MAUI](https://github.com/Redth/ZXing.Net.Maui), but comes with many code improvements and uses native ML APIs on both Android and iOS.
This library was inspired by existing MAUI barcode scanning libraries: [BarcodeScanner.Mobile](https://github.com/JimmyPun610/BarcodeScanner.Mobile) & [Zxing.Net.MAUI](https://github.com/Redth/ZXing.Net.Maui), but comes with many code improvements and uses native ML APIs on both Android and iOS/macOS.

## Key features
1. Uses native APIs for maximal performance and barcode readability,
Expand Down Expand Up @@ -36,8 +36,8 @@ This library was inspired by existing MAUI barcode scanning libraries: [BarcodeS
```xml
<uses-permission android:name="android.permission.CAMERA" />
```
#### iOS
Edit `info.plist` file (under the Platforms\iOS folder) and add the following permissions inside of the `dict` node:
#### iOS/macOS
Edit `info.plist` file (under the Platforms\iOS or Platforms\MacCatalyst folder) and add the following permissions inside of the `dict` node:
```xml
<key>NSCameraUsageDescription</key>
<string>Enable camera for barcode scanning.</string>
Expand Down Expand Up @@ -76,11 +76,11 @@ This library was inspired by existing MAUI barcode scanning libraries: [BarcodeS
## Supported barcode symbologies
#### Android
1D: Codabar, Code 39, Code 93, Code 128, EAN-8, EAN-13, ITF, UPC-A, UPC-E; 2D: Aztec, Data Matrix, PDF417, QR Code
#### iOS
1D: Codabar, Code 39, Code 93, Code 128, EAN-8, EAN-13, GS1 DataBar, ITF, UPC-E; 2D: Aztec, Data Matrix, MicroPDF417, MicroQR, PDF417, QR Code
#### iOS/macOS
1D: Codabar, Code 39, Code 93, Code 128, EAN-8, EAN-13, GS1 DataBar, ITF, UPC-A, UPC-E; 2D: Aztec, Data Matrix, MicroPDF417, MicroQR, PDF417, QR Code

## Bindable properties
A list of bindable properties with descriptions can be found in [CameraView.cs](https://github.com/afriscic/BarcodeScanning.Native.Maui/blob/master/BarcodeScanning.Native.Maui/CameraView.cs) source file.
## TODO Windows and macOS support
Windows and macOS are currently unsupported, but support can be added in the future. Vision framework is compatible with macOS so this implementation wouldn't be difficult. For Windows, barcode detection could be supported through [Zxing.Net](https://github.com/micjahn/ZXing.Net) project.
## TODO Windows support
Windows is currently unsupported, but support can be added in the future through [Zxing.Net](https://github.com/micjahn/ZXing.Net) project.

0 comments on commit b8d099b

Please sign in to comment.