diff --git a/BigIslandBarcode/App.xaml.cs b/BigIslandBarcode/App.xaml.cs
index d570a94..675cc5a 100644
--- a/BigIslandBarcode/App.xaml.cs
+++ b/BigIslandBarcode/App.xaml.cs
@@ -11,7 +11,7 @@ public App()
{
InitializeComponent();
- MainPage = new MainPage();
+ MainPage = new NavigationPage(new MainPage());
}
}
}
diff --git a/BigIslandBarcode/MainPage.xaml b/BigIslandBarcode/MainPage.xaml
index 42d5db3..f1118b9 100644
--- a/BigIslandBarcode/MainPage.xaml
+++ b/BigIslandBarcode/MainPage.xaml
@@ -15,7 +15,21 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BigIslandBarcode/PickImage.xaml.cs b/BigIslandBarcode/PickImage.xaml.cs
new file mode 100644
index 0000000..1f6f901
--- /dev/null
+++ b/BigIslandBarcode/PickImage.xaml.cs
@@ -0,0 +1,164 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Maui.ApplicationModel;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Storage;
+using ZXing.Net.Maui;
+using ZXing.Net.Maui.Readers;
+
+namespace BigIslandBarcode;
+
+public partial class PickImagePage : ContentPage
+{
+ readonly IBarcodeReader _reader;
+ bool _working = false;
+
+ public PickImagePage()
+ {
+ InitializeComponent();
+
+ _reader =
+ new ZXingBarcodeReader
+ {
+ Options = new BarcodeReaderOptions
+ {
+ Formats = BarcodeFormats.All,
+ AutoRotate = true
+ }
+ };
+
+ NavigatedTo += PageNavigatedTo;
+ }
+
+ async void PageNavigatedTo(object? sender, NavigatedToEventArgs e)
+ => await CallFilePicker();
+
+ async void PickClicked(object sender, EventArgs e)
+ => await CallFilePicker();
+
+ async Task CallFilePicker()
+ {
+ if (!_working)
+ {
+ _working = true;
+
+ FileInfo.Text = "Awaiting file selection";
+ ParseResult.Text = "";
+ ImageOutput.Source = null;
+
+ try
+ {
+ var result =
+ await FilePicker.PickAsync(
+ new PickOptions
+ {
+ FileTypes = FilePickerFileType.Images,
+ PickerTitle = "Choose your barcode"
+ }
+ );
+
+ if (result == null)
+ {
+ FileInfo.Text = "No file selected";
+ _working = false;
+ }
+ else
+ {
+ ActivityContainer.IsVisible = true;
+ ActivityIndicator.IsRunning = true;
+ PickButton.IsEnabled = false;
+ barcodeGenerator.IsVisible = false;
+
+ var worker = new BackgroundWorker();
+ worker.DoWork += Worker_DoWork;
+ worker.RunWorkerCompleted += Worker_Completed;
+
+ worker.RunWorkerAsync(result);
+ }
+ }
+ catch (Exception ex)
+ {
+ FileInfo.Text = $"Something's wrong: {ex.Message}";
+ ParseResult.Text = $"Something's wrong: {ex.Message}";
+
+ _working = false;
+ }
+ }
+ else
+ {
+ await DisplayAlert("Task Already Running", "Please cancel the runnning task first", "Okay");
+ }
+ }
+
+ async void Worker_DoWork(object? sender, DoWorkEventArgs e)
+ {
+ var fileResult = e.Argument as FileResult;
+ BarcodeResult? result = null;
+
+ var sW = new Stopwatch();
+
+ if (fileResult != null)
+ {
+
+ await MainThread.InvokeOnMainThreadAsync(() => ActivityLabel.Text = "Loading file");
+
+ using var stream = await fileResult.OpenReadAsync();
+
+ await MainThread.InvokeOnMainThreadAsync(() =>
+ {
+ FileInfo.Text = $"Name: {fileResult.FileName} - Size: {(stream.Length / 1024d):#.##} KiB";
+ ActivityLabel.Text = "Decoding";
+ });
+
+ sW.Start();
+
+ var decodeResult = _reader.Decode(stream);
+
+ sW.Stop();
+
+ result = decodeResult?.FirstOrDefault();
+ }
+
+ await MainThread.InvokeOnMainThreadAsync(() =>
+ {
+ if (fileResult != null)
+ ImageOutput.Source = ImageSource.FromFile(fileResult.FullPath);
+
+ if (result != null)
+ {
+ ParseResult.Text = $"Found Barcode (in {sW.ElapsedMilliseconds}ms)\nValue: {result.Value}";
+
+ try
+ {
+ barcodeGenerator.Value = result.Value;
+ barcodeGenerator.Format = result.Format;
+ barcodeGenerator.IsVisible = true;
+ }
+ catch (Exception ex)
+ {
+ ParseResult.Text += "\r\nError displaying barcode: " + ex.Message;
+ }
+ }
+ else
+ {
+ ParseResult.Text = $"No Barcode Found (in {sW.ElapsedMilliseconds}ms)";
+ }
+
+ Worker_Completed(true, null!);
+ });
+ }
+
+ void Worker_Completed(object? sender, RunWorkerCompletedEventArgs e)
+ {
+ if (sender is bool finished && finished == true)
+ {
+ ActivityContainer.IsVisible = false;
+ ActivityIndicator.IsRunning = false;
+ PickButton.IsEnabled = true;
+ _working = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/BigIslandBarcode/TakeImage.xaml b/BigIslandBarcode/TakeImage.xaml
new file mode 100644
index 0000000..cb5c699
--- /dev/null
+++ b/BigIslandBarcode/TakeImage.xaml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BigIslandBarcode/TakeImage.xaml.cs b/BigIslandBarcode/TakeImage.xaml.cs
new file mode 100644
index 0000000..2ff6354
--- /dev/null
+++ b/BigIslandBarcode/TakeImage.xaml.cs
@@ -0,0 +1,175 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Maui.ApplicationModel;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Media;
+using Microsoft.Maui.Storage;
+using ZXing.Net.Maui;
+using ZXing.Net.Maui.Readers;
+
+namespace BigIslandBarcode;
+
+public partial class TakeImagePage : ContentPage
+{
+ readonly IBarcodeReader _reader;
+ bool _working = false;
+
+ public TakeImagePage()
+ {
+ InitializeComponent();
+
+ _reader =
+ new ZXingBarcodeReader
+ {
+ Options = new BarcodeReaderOptions
+ {
+ Formats = BarcodeFormats.All,
+ AutoRotate = true
+ }
+ };
+ }
+
+ async void CaptureClicked(object sender, EventArgs e)
+ => await CallFilePicker();
+
+ async Task CallFilePicker()
+ {
+ if (!_working)
+ {
+ _working = true;
+
+ FileInfo.Text = "Awaiting file selection";
+ ParseResult.Text = "";
+ ImageOutput.Source = null;
+
+ var mediaPicker = MediaPicker.Default;
+
+ if (!mediaPicker.IsCaptureSupported)
+ {
+ FileInfo.Text = $"No capture device is available";
+
+ _working = false;
+
+ return;
+ }
+
+ try
+ {
+ var result = await MediaPicker.Default.CapturePhotoAsync(new MediaPickerOptions
+ {
+ Title = "Take a photo of the barcode"
+ });
+
+ if (result == null)
+ {
+ FileInfo.Text = "No picture taken";
+ _working = false;
+ }
+ else
+ {
+ ActivityContainer.IsVisible = true;
+ ActivityIndicator.IsRunning = true;
+ CaptureButton.IsEnabled = false;
+ barcodeGenerator.IsVisible = false;
+
+ var worker = new BackgroundWorker();
+ worker.DoWork += Worker_DoWork;
+ worker.RunWorkerCompleted += Worker_Completed;
+
+ worker.RunWorkerAsync(result);
+ }
+ }
+ catch (Exception ex)
+ {
+ FileInfo.Text = $"Something's wrong: {ex.Message}";
+
+ _working = false;
+ }
+ }
+ else
+ {
+ await DisplayAlert("Task Already Running", "Please cancel the runnning task first", "Okay");
+ }
+ }
+
+ async void Worker_DoWork(object sender, DoWorkEventArgs e)
+ {
+ var fileResult = e.Argument as FileResult;
+ BarcodeResult result = null;
+ string error = null;
+
+ var sW = new Stopwatch();
+
+ if (fileResult != null)
+ {
+
+ await MainThread.InvokeOnMainThreadAsync(() => ActivityLabel.Text = "Loading file");
+
+ using var stream = await fileResult.OpenReadAsync();
+
+ await MainThread.InvokeOnMainThreadAsync(() =>
+ {
+ FileInfo.Text = $"Name: {fileResult.FileName} - Size: {(stream.Length / 1024d):#.##} KiB";
+ ActivityLabel.Text = "Decoding";
+ });
+
+ try
+ {
+ sW.Start();
+
+ var decodeResult = _reader.Decode(stream);
+
+ sW.Stop();
+
+ result = decodeResult?.FirstOrDefault();
+ }
+ catch (Exception ex)
+ {
+ error = $"Error: {ex.Message}";
+ }
+ }
+
+ await MainThread.InvokeOnMainThreadAsync(() =>
+ {
+ if (fileResult != null)
+ ImageOutput.Source = ImageSource.FromFile(fileResult.FullPath);
+
+ if (result != null)
+ {
+ ParseResult.Text = $"Found Barcode (in {sW.ElapsedMilliseconds}ms)\nValue: {result.Value}";
+
+ try
+ {
+ barcodeGenerator.IsVisible = true;
+ barcodeGenerator.Value = result.Value;
+ barcodeGenerator.Format = result.Format;
+ }
+ catch (Exception ex)
+ {
+ barcodeGenerator.IsVisible = false;
+ ParseResult.Text = ex.Message;
+ }
+ }
+ else
+ {
+ ParseResult.Text = error ?? $"No Barcode Found (in {sW.ElapsedMilliseconds}ms)";
+ }
+
+ Worker_Completed(true, null!);
+ });
+ }
+
+ void Worker_Completed(object sender, RunWorkerCompletedEventArgs e)
+ {
+ if (sender is bool finished && finished == true)
+ {
+ ActivityContainer.IsVisible = false;
+ ActivityIndicator.IsRunning = false;
+ CaptureButton.IsEnabled = true;
+ _working = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ZXing.Net.MAUI/IBarcodeReader.cs b/ZXing.Net.MAUI/IBarcodeReader.cs
index 24649a1..ddce73c 100644
--- a/ZXing.Net.MAUI/IBarcodeReader.cs
+++ b/ZXing.Net.MAUI/IBarcodeReader.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.IO;
namespace ZXing.Net.Maui.Readers
{
@@ -11,5 +7,7 @@ public interface IBarcodeReader
BarcodeReaderOptions Options { get; set; }
BarcodeResult[] Decode(PixelBufferHolder image);
+
+ BarcodeResult[] Decode(Stream stream);
}
}
diff --git a/ZXing.Net.MAUI/PixelBufferHolder.cs b/ZXing.Net.MAUI/PixelBufferHolder.cs
index bcebd8a..0f68b6d 100644
--- a/ZXing.Net.MAUI/PixelBufferHolder.cs
+++ b/ZXing.Net.MAUI/PixelBufferHolder.cs
@@ -1,4 +1,10 @@
using Microsoft.Maui.Graphics;
+using System.IO;
+using System;
+#if ANDROID || WINDOWS
+using System.Linq;
+using System.Runtime.InteropServices;
+#endif
namespace ZXing.Net.Maui.Readers
{
@@ -17,5 +23,97 @@ public record PixelBufferHolder
#endif
Data { get; init; }
+
+ internal byte[] ByteData { get; set; }
+
+ public PixelBufferHolder() { }
+
+ ///
+ /// Create the necessary from a stream
+ ///
+ /// The stream to pick pixel data from
+ ///
+ ///
+ public static PixelBufferHolder FromStream(Stream stream)
+ {
+#if WINDOWS
+
+ var decoder = Run(Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream.AsRandomAccessStream()));
+
+ var image =
+ new
+ {
+ Width = decoder.PixelWidth,
+ Height = decoder.PixelHeight
+ };
+
+ var pixelData = Run(decoder.GetPixelDataAsync());
+
+ var data = pixelData.DetachPixelData();
+
+#else
+
+ var image =
+ (Microsoft.Maui.Graphics.Platform.PlatformImage.FromStream(stream)
+ as Microsoft.Maui.Graphics.Platform.PlatformImage)!;
+
+#if IOS || MACCATALYST
+
+ var uiImage = image.PlatformRepresentation;
+
+ var pixelBuffer = uiImage.CIImage?.PixelBuffer;
+
+ if (pixelBuffer != null)
+ return new PixelBufferHolder
+ {
+ Size = new(image.Width, image.Height),
+ Data = pixelBuffer
+ };
+
+ var data = uiImage.CGImage?.DataProvider.CopyData()?.ToArray();
+
+ if (data == null)
+ throw new NullReferenceException("Could not convert stream to native bytes");
+
+#elif ANDROID
+
+ var pixelArr = new int[(int)(image.Width * image.Height)];
+
+ image!.PlatformRepresentation.GetPixels(pixelArr, 0, (int)image.Width, 0, 0, (int)image.Width, (int)image.Height);
+ image!.PlatformRepresentation.Recycle();
+
+ var data =
+ MemoryMarshal.Cast(CollectionsMarshal.AsSpan(pixelArr.ToList()))
+ .ToArray();
+
+#else
+
+ throw new PlatformNotSupportedException();
+
+#endif
+
+#endif
+
+ return new PixelBufferHolder
+ {
+ Size = new(image.Width, image.Height),
+ ByteData = data
+ };
+ }
+
+#if WINDOWS
+ static T Run(Windows.Foundation.IAsyncOperation operation)
+ {
+
+ var task = System.Threading.Tasks.Task.Run(async () => await operation);
+
+ task.Wait();
+
+ if (task.Exception != null)
+ throw task.Exception;
+
+ return task.Result;
+ }
+#endif
}
-}
+}
\ No newline at end of file
diff --git a/ZXing.Net.MAUI/ZXingBarcodeReader.cs b/ZXing.Net.MAUI/ZXingBarcodeReader.cs
index f7c39da..c29a672 100644
--- a/ZXing.Net.MAUI/ZXingBarcodeReader.cs
+++ b/ZXing.Net.MAUI/ZXingBarcodeReader.cs
@@ -1,8 +1,8 @@
-using Microsoft.Maui.Graphics;
+using System.IO;
namespace ZXing.Net.Maui.Readers
{
- public class ZXingBarcodeReader : Readers.IBarcodeReader
+ public class ZXingBarcodeReader : IBarcodeReader
{
BarcodeReaderGeneric zxingReader;
@@ -28,16 +28,7 @@ public BarcodeReaderOptions Options
public BarcodeResult[] Decode(PixelBufferHolder image)
{
- var w = (int)image.Size.Width;
- var h = (int)image.Size.Height;
-
- LuminanceSource ls = default;
-
-#if ANDROID
- ls = new ByteBufferYUVLuminanceSource(image.Data, w, h, 0, 0, w, h);
-#elif MACCATALYST || IOS
- ls = new CVPixelBufferBGRA32LuminanceSource(image.Data, w, h);
-#endif
+ LuminanceSource ls = GetLuminanceSource(image);
if (Options.Multiple)
return zxingReader.DecodeMultiple(ls)?.ToBarcodeResults();
@@ -48,5 +39,30 @@ public BarcodeResult[] Decode(PixelBufferHolder image)
return null;
}
+
+ public BarcodeResult[] Decode(Stream stream)
+ => Decode(PixelBufferHolder.FromStream(stream));
+
+ static LuminanceSource GetLuminanceSource(PixelBufferHolder image)
+ {
+ var w = (int)image.Size.Width;
+ var h = (int)image.Size.Height;
+
+#if MACCATALYST || IOS
+ if (image.Data != null)
+ return new CVPixelBufferBGRA32LuminanceSource(image.Data, w, h);
+#elif ANDROID
+ if (image.Data != null)
+ return new ByteBufferYUVLuminanceSource(image.Data, w, h, 0, 0, w, h);
+#endif
+
+ return
+ new RGBLuminanceSource(
+ image.ByteData,
+ w,
+ h,
+ RGBLuminanceSource.BitmapFormat.Unknown
+ );
+ }
}
}