| 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        };    }}
 |