From 70161554c5e27b20339ca7cc0dc21dd64226dc89 Mon Sep 17 00:00:00 2001 From: Jamie Hodgson Date: Mon, 11 Nov 2024 14:56:22 +0100 Subject: [PATCH] use zxingcpp --- Stratum.Droid/Assets/about.html | 6 +- Stratum.Droid/Stratum.Droid.csproj | 3 +- Stratum.Droid/src/Activity/ScanActivity.cs | 2 +- .../src/QrCode/QrCodeImageAnalyser.cs | 67 ++++++++++--------- Stratum.Droid/src/QrCode/QrCodeImageReader.cs | 40 +++++------ Stratum.Test/Stratum.Test.csproj | 6 +- Stratum.Test/src/Service/BackupServiceTest.cs | 22 ++---- 7 files changed, 70 insertions(+), 76 deletions(-) diff --git a/Stratum.Droid/Assets/about.html b/Stratum.Droid/Assets/about.html index 8955bdafa2..112906aa62 100644 --- a/Stratum.Droid/Assets/about.html +++ b/Stratum.Droid/Assets/about.html @@ -92,10 +92,10 @@

Konscious.Security.Cryptography

MIT License

https://github.com/kmaragon/Konscious.Security.Cryptography -

ZXing.Net

-

Copyright © Michael Jahn, ZXing authors

+

ZXing-C++

+

Copyright © Axel Waggershauser, ZXing authors

Apache-2.0 License

- https://github.com/micjahn/ZXing.Net + https://github.com/zxing-cpp/zxing-cpp %LICENSE diff --git a/Stratum.Droid/Stratum.Droid.csproj b/Stratum.Droid/Stratum.Droid.csproj index d4c7d59af0..9061c9d937 100644 --- a/Stratum.Droid/Stratum.Droid.csproj +++ b/Stratum.Droid/Stratum.Droid.csproj @@ -10,6 +10,7 @@ 1.0 12 {versionCode} + True true @@ -51,7 +52,7 @@ - + diff --git a/Stratum.Droid/src/Activity/ScanActivity.cs b/Stratum.Droid/src/Activity/ScanActivity.cs index 4f04d27664..12ec7ad6b1 100644 --- a/Stratum.Droid/src/Activity/ScanActivity.cs +++ b/Stratum.Droid/src/Activity/ScanActivity.cs @@ -49,7 +49,7 @@ protected override async void OnCreate(Bundle savedInstanceState) var analysis = new ImageAnalysis.Builder() .SetBackpressureStrategy(ImageAnalysis.StrategyKeepOnlyLatest) - .SetOutputImageFormat(ImageAnalysis.OutputImageFormatYuv420888) + .SetOutputImageFormat(ImageAnalysis.OutputImageFormatRgba8888) .Build(); var analyser = new QrCodeImageAnalyser(); diff --git a/Stratum.Droid/src/QrCode/QrCodeImageAnalyser.cs b/Stratum.Droid/src/QrCode/QrCodeImageAnalyser.cs index 6859919a84..844c16c891 100644 --- a/Stratum.Droid/src/QrCode/QrCodeImageAnalyser.cs +++ b/Stratum.Droid/src/QrCode/QrCodeImageAnalyser.cs @@ -1,35 +1,30 @@ -// Copyright (C) 2023 jmh +// Copyright (C) 2024 jmh // SPDX-License-Identifier: GPL-3.0-only using System; -using System.Collections.Generic; -using Android.Graphics; using Android.Util; using AndroidX.Camera.Core; -using ZXing; -using ZXing.Common; +using Stratum.ZXing; +using Serilog; +using ImageFormat = Stratum.ZXing.ImageFormat; +using Log = Serilog.Log; namespace Stratum.Droid.QrCode { public class QrCodeImageAnalyser : Java.Lang.Object, ImageAnalysis.IAnalyzer { public event EventHandler QrCodeScanned; - public Size DefaultTargetResolution => new(640, 480); + public Size DefaultTargetResolution => new(1920, 1080); - private readonly BarcodeReader _barcodeReader; - - public QrCodeImageAnalyser() + private readonly ILogger _log = Log.ForContext(); + + private readonly QrCodeReader _qrCodeReader = new(new ReaderOptions { - _barcodeReader = new BarcodeReader(null, null, ls => new HybridBinarizer(ls)) - { - AutoRotate = true, - Options = new DecodingOptions - { - PossibleFormats = new List { BarcodeFormat.QR_CODE }, - TryInverted = true - } - }; - } + TryRotate = true, + TryHarder = true, + TryInvert = true, + Binarizer = Binarizer.LocalAverage + }); public void Analyze(IImageProxy imageProxy) { @@ -37,7 +32,7 @@ public void Analyze(IImageProxy imageProxy) { return; } - + try { AnalyseInternal(imageProxy); @@ -47,23 +42,35 @@ public void Analyze(IImageProxy imageProxy) imageProxy.Close(); } } - + private void AnalyseInternal(IImageProxy imageProxy) { - using var plane = imageProxy.Image.GetPlanes()[0]; - - var bytes = new byte[plane.Buffer.Capacity()]; - plane.Buffer.Get(bytes); + using var rgbaPlane = imageProxy.Image.GetPlanes()[0]; + ReadOnlySpan bytes; - var source = new PlanarYUVLuminanceSource( - bytes, imageProxy.Width, imageProxy.Height, 0, 0, imageProxy.Width, imageProxy.Height, false); + unsafe + { + var bufferAddress = rgbaPlane.Buffer.GetDirectBufferAddress().ToPointer(); + bytes = new ReadOnlySpan(bufferAddress, rgbaPlane.Buffer.Capacity()); + } - var result = _barcodeReader.Decode(source); + using var imageView = new ImageView(bytes, imageProxy.Width, imageProxy.Height, ImageFormat.RGBA); + string result; + try + { + result = _qrCodeReader.Read(imageView); + } + catch (QrCodeException e) + { + _log.Warning(e, "Error scanning QR code: {Type}", e.Type); + return; + } + if (result != null) { - QrCodeScanned?.Invoke(this, result.Text); + QrCodeScanned?.Invoke(this, result); } } } -} \ No newline at end of file +} diff --git a/Stratum.Droid/src/QrCode/QrCodeImageReader.cs b/Stratum.Droid/src/QrCode/QrCodeImageReader.cs index 086636e31d..f447c685ec 100644 --- a/Stratum.Droid/src/QrCode/QrCodeImageReader.cs +++ b/Stratum.Droid/src/QrCode/QrCodeImageReader.cs @@ -1,16 +1,15 @@ -// Copyright (C) 2023 jmh +// Copyright (C) 2024 jmh // SPDX-License-Identifier: GPL-3.0-only using System; -using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Android.Content; using Android.Graphics; -using Java.Nio; using Stratum.Droid.Util; -using ZXing; -using ZXing.Common; +using Stratum.ZXing; +using Java.Nio; +using ImageFormat = Stratum.ZXing.ImageFormat; using Uri = Android.Net.Uri; namespace Stratum.Droid.QrCode @@ -20,7 +19,7 @@ public static class QrCodeImageReader public static async Task ScanImageFromFileAsync(Context context, Uri uri) { Bitmap bitmap; - + try { var data = await FileUtil.ReadFileAsync(context, uri); @@ -30,34 +29,29 @@ public static async Task ScanImageFromFileAsync(Context context, Uri uri { throw new IOException("Failed to read file", e); } - + if (bitmap == null) { throw new IOException("Failed to decode bitmap"); } - var reader = new BarcodeReader(null, null, ls => new HybridBinarizer(ls)) + var reader = new QrCodeReader(new ReaderOptions { - AutoRotate = true, - Options = new DecodingOptions - { - PossibleFormats = new List { BarcodeFormat.QR_CODE }, - TryHarder = true, - TryInverted = true - } - }; + Binarizer = Binarizer.LocalAverage, + TryRotate = true, + TryHarder = true, + TryInvert = true + }); using var buffer = ByteBuffer.Allocate(bitmap.ByteCount); await bitmap.CopyPixelsToBufferAsync(buffer); buffer.Rewind(); - + var bytes = new byte[buffer.Remaining()]; buffer.Get(bytes); - - var source = new RGBLuminanceSource(bytes, bitmap.Width, bitmap.Height, RGBLuminanceSource.BitmapFormat.RGBA32); - var result = await Task.Run(() => reader.Decode(source)); - - return result?.Text; + + using var imageView = new ImageView(bytes, bitmap.Width, bitmap.Height, ImageFormat.RGBA); + return await Task.Run(() => reader.Read(imageView)); } } -} \ No newline at end of file +} diff --git a/Stratum.Test/Stratum.Test.csproj b/Stratum.Test/Stratum.Test.csproj index ae87bd7362..42130f246a 100644 --- a/Stratum.Test/Stratum.Test.csproj +++ b/Stratum.Test/Stratum.Test.csproj @@ -17,13 +17,13 @@ - + + all - - + diff --git a/Stratum.Test/src/Service/BackupServiceTest.cs b/Stratum.Test/src/Service/BackupServiceTest.cs index 42f21b5616..e78c54f637 100644 --- a/Stratum.Test/src/Service/BackupServiceTest.cs +++ b/Stratum.Test/src/Service/BackupServiceTest.cs @@ -9,13 +9,11 @@ using Stratum.Core.Persistence; using Stratum.Core.Service; using Stratum.Core.Service.Impl; +using Stratum.ZXing; using HtmlAgilityPack; using Moq; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; +using SkiaSharp; using Xunit; -using ZXing; -using ZXing.Common; namespace Stratum.Test.Service { @@ -105,13 +103,7 @@ public async Task CreateHtmlBackupAsync() var rows = document.DocumentNode.SelectNodes("//tr"); Assert.Equal(2, rows.Count); - var barcodeReader = new ZXing.ImageSharp.BarcodeReader - { - Options = new DecodingOptions - { - PossibleFormats = new List { BarcodeFormat.QR_CODE } - } - }; + var qrCodeReader = new QrCodeReader(); void AssertRowMatches(HtmlNode node, Authenticator auth) { @@ -123,10 +115,10 @@ void AssertRowMatches(HtmlNode node, Authenticator auth) var img = tds[3].SelectSingleNode("img"); var src = img.Attributes["src"].Value; var data = Convert.FromBase64String(src["data:image/png;base64,".Length..]); - - using var decodedImage = Image.Load(data); - var qrCodeResult = barcodeReader.Decode(decodedImage); - Assert.Equal(auth.GetUri(), qrCodeResult.Text); + + using var image = SKBitmap.Decode(data); + using var imageView = new ImageView(image.GetPixelSpan(), image.Width, image.Height, ImageFormat.Lum); + Assert.Equal(auth.GetUri(), qrCodeReader.Read(imageView)); } AssertRowMatches(rows[0], authA);