123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- using Android.Gms.Extensions;
- using Android.Graphics;
- using Android.Runtime;
- using Android.Util;
- using AndroidX.Camera.View.Transform;
- using InABox.Avalonia.Platform.Barcodes;
- using Java.Net;
- using Java.Util;
- using Microsoft.Maui.Graphics;
- using Microsoft.Maui.Storage;
- using Xamarin.Google.MLKit.Vision.Barcode.Common;
- using Xamarin.Google.MLKit.Vision.BarCode;
- using Xamarin.Google.MLKit.Vision.Common;
- using Image = Android.Media.Image;
- using Paint = Android.Graphics.Paint;
- using RectF = Microsoft.Maui.Graphics.RectF;
- using Size = Android.Util.Size;
- using ARectF = Android.Graphics.RectF;
- namespace InABox.Avalonia.Platform.Android.Barcodes;
- public static partial class Methods
- {
- private static readonly bool neonSupported = IsNeonSupported();
- private static readonly ParallelOptions parallelOptions = new()
- {
- MaxDegreeOfParallelism = Environment.ProcessorCount * 2
- };
-
- public static async Task<IReadOnlySet<BarcodeResult>> ScanFromImageAsync(byte[] imageArray)
- => await ProcessBitmapAsync(await BitmapFactory.DecodeByteArrayAsync(imageArray, 0, imageArray.Length));
- public static async Task<IReadOnlySet<BarcodeResult>> ScanFromImageAsync(FileResult file)
- => await ProcessBitmapAsync(await BitmapFactory.DecodeStreamAsync(await file.OpenReadAsync()));
- public static async Task<IReadOnlySet<BarcodeResult>> ScanFromImageAsync(string url)
- => await ProcessBitmapAsync(await BitmapFactory.DecodeStreamAsync(new URL(url).OpenStream()));
- public static async Task<IReadOnlySet<BarcodeResult>> ScanFromImageAsync(Stream stream)
- => await ProcessBitmapAsync(await BitmapFactory.DecodeStreamAsync(stream));
- private static async Task<IReadOnlySet<BarcodeResult>> ProcessBitmapAsync(Bitmap? bitmap)
- {
- var barcodeResults = new HashSet<BarcodeResult>();
- if (bitmap is null)
- return barcodeResults;
-
- using var scanner = BarcodeScanning.GetClient(new BarcodeScannerOptions.Builder()
- .SetBarcodeFormats(Barcode.FormatAllFormats)
- .Build());
- using var image = InputImage.FromBitmap(bitmap, 0);
- using var results = await scanner.Process(image).AsAsync<Java.Lang.Object>();
- ProcessBarcodeResult(results, barcodeResults);
- using var invertedBitmap = Bitmap.CreateBitmap(bitmap.Height, bitmap.Width, bitmap.GetConfig());
- using var canvas = new Canvas(invertedBitmap);
- using var paint = new Paint();
- using var matrixInvert = new ColorMatrix();
- matrixInvert.Set(
- [
- -1.0f, 0.0f, 0.0f, 0.0f, 255.0f,
- 0.0f, -1.0f, 0.0f, 0.0f, 255.0f,
- 0.0f, 0.0f, -1.0f, 0.0f, 255.0f,
- 0.0f, 0.0f, 0.0f, 1.0f, 0.0f
- ]);
- using var filter = new ColorMatrixColorFilter(matrixInvert);
- paint.SetColorFilter(filter);
- canvas.DrawBitmap(bitmap, 0, 0, paint);
- using var invertedImage = InputImage.FromBitmap(invertedBitmap, 0);
- using var invertedResults = await scanner.Process(invertedImage).AsAsync<Java.Lang.Object>();
- ProcessBarcodeResult(invertedResults, barcodeResults);
- return barcodeResults;
- }
-
- private static void ProcessBarcodeResult(Java.Lang.Object? inputResults, HashSet<BarcodeResult> outputResults)
- {
- if (inputResults is not JavaList javaList)
- return;
- foreach (Barcode barcode in javaList)
- {
- if (barcode is null)
- continue;
- if (string.IsNullOrEmpty(barcode.DisplayValue) && string.IsNullOrEmpty(barcode.RawValue))
- continue;
- outputResults.Add(barcode.AsBarcodeResult());
- }
- }
- // [LibraryImport("libInvertBytes.so")]
- // private static partial int InvertBytes(IntPtr data, int length);
- internal static void InvertLuminance(Image image)
- {
- var yBuffer = image.GetPlanes()?[0].Buffer;
- if (yBuffer is null)
- return;
- if (yBuffer.IsDirect)
- {
- var data = yBuffer.GetDirectBufferAddress();
- var length = yBuffer.Capacity();
- if (!neonSupported/* || InvertBytes(data, length) != 0*/)
- {
- unsafe
- {
- var dataPtr = (ulong*)data;
- Parallel.For(0, length >> 3, parallelOptions, (i) => dataPtr[i] = ~dataPtr[i]);
- }
- }
- }
- else
- {
- using var bits = BitSet.ValueOf(yBuffer);
- bits?.Flip(0, bits.Length());
- yBuffer.Rewind();
- yBuffer.Put(bits?.ToByteArray() ?? []);
- }
- }
- internal static BarcodeTypes ConvertBarcodeResultTypes(int barcodeValueType)
- {
- return barcodeValueType switch
- {
- Barcode.TypeCalendarEvent => BarcodeTypes.CalendarEvent,
- Barcode.TypeContactInfo => BarcodeTypes.ContactInfo,
- Barcode.TypeDriverLicense => BarcodeTypes.DriversLicense,
- Barcode.TypeEmail => BarcodeTypes.Email,
- Barcode.TypeGeo => BarcodeTypes.GeographicCoordinates,
- Barcode.TypeIsbn => BarcodeTypes.Isbn,
- Barcode.TypePhone => BarcodeTypes.Phone,
- Barcode.TypeProduct => BarcodeTypes.Product,
- Barcode.TypeSms => BarcodeTypes.Sms,
- Barcode.TypeText => BarcodeTypes.Text,
- Barcode.TypeUrl => BarcodeTypes.Url,
- Barcode.TypeWifi => BarcodeTypes.WiFi,
- _ => BarcodeTypes.Unknown
- };
- }
- internal static int ConvertBarcodeFormats(BarcodeFormats barcodeFormats)
- {
- var formats = Barcode.FormatAllFormats;
- if (barcodeFormats.HasFlag(BarcodeFormats.Code128))
- formats |= Barcode.FormatCode128;
- if (barcodeFormats.HasFlag(BarcodeFormats.Code39))
- formats |= Barcode.FormatCode39;
- if (barcodeFormats.HasFlag(BarcodeFormats.Code93))
- formats |= Barcode.FormatCode93;
- if (barcodeFormats.HasFlag(BarcodeFormats.CodaBar))
- formats |= Barcode.FormatCodabar;
- if (barcodeFormats.HasFlag(BarcodeFormats.DataMatrix))
- formats |= Barcode.FormatDataMatrix;
- if (barcodeFormats.HasFlag(BarcodeFormats.Ean13))
- formats |= Barcode.FormatEan13;
- if (barcodeFormats.HasFlag(BarcodeFormats.Ean8))
- formats |= Barcode.FormatEan8;
- if (barcodeFormats.HasFlag(BarcodeFormats.Itf))
- formats |= Barcode.FormatItf;
- if (barcodeFormats.HasFlag(BarcodeFormats.QRCode))
- formats |= Barcode.FormatQrCode;
- if (barcodeFormats.HasFlag(BarcodeFormats.Upca))
- formats |= Barcode.FormatUpcA;
- if (barcodeFormats.HasFlag(BarcodeFormats.Upce))
- formats |= Barcode.FormatUpcE;
- if (barcodeFormats.HasFlag(BarcodeFormats.Pdf417))
- formats |= Barcode.FormatPdf417;
- if (barcodeFormats.HasFlag(BarcodeFormats.Aztec))
- formats |= Barcode.FormatAztec;
- if (barcodeFormats.HasFlag(BarcodeFormats.All))
- formats = Barcode.FormatAllFormats;
- return formats;
- }
- internal static Size TargetResolution(CaptureQuality? captureQuality)
- {
- return captureQuality switch
- {
- CaptureQuality.Low => new Size(854, 480),
- CaptureQuality.Medium => new Size(1280, 720),
- CaptureQuality.High => new Size(1920, 1080),
- CaptureQuality.Highest => new Size(3840, 2160),
- _ => new Size(1280, 720)
- };
- }
- private static bool IsNeonSupported()
- {
- try
- {
- var info = File.ReadAllText("/proc/cpuinfo");
- return info.Contains("neon") || info.Contains("asimd");
- }
- catch (Exception)
- {
- return false;
- }
- }
- internal static RectF AsRectangleF(this ARectF rect)
- {
- return new(rect.Left, rect.Top, rect.Width(), rect.Height());
- }
- internal static BarcodeResult AsBarcodeResult(this Barcode barcode, CoordinateTransform? coordinateTransform = null)
- {
- RectF imageRect, previewRect;
- if (barcode.BoundingBox is null)
- {
- imageRect = RectF.Zero;
- previewRect = RectF.Zero;
- }
- else
- {
- using var barcodeBox = new ARectF(barcode.BoundingBox);
- imageRect = barcodeBox.AsRectangleF();
- if (coordinateTransform is null)
- {
- previewRect = new();
- }
- else
- {
- coordinateTransform.MapRect(barcodeBox);
- previewRect = barcodeBox.AsRectangleF();
- }
- }
- return new BarcodeResult()
- {
- BarcodeType = Methods.ConvertBarcodeResultTypes(barcode.ValueType),
- BarcodeFormat = (BarcodeFormats)barcode.Format,
- DisplayValue = barcode.DisplayValue ?? string.Empty,
- RawValue = barcode.RawValue ?? string.Empty,
- RawBytes = barcode.GetRawBytes() ?? [],
- PreviewBoundingBox = previewRect,
- ImageBoundingBox = imageRect
- };
- }
- }
|