using System.Drawing; using Android.Graphics; using Android.Media; using Avalonia.Controls; using InABox.Core; using Java.IO; using Microsoft.Maui.Media; using Bitmap = Android.Graphics.Bitmap; using File = System.IO.File; using Path = System.IO.Path; using Stream = System.IO.Stream; namespace InABox.Avalonia.Platform.Android { public class Android_ImageTools : IImageTools { public Logger? Logger { get; set; } public byte[] CreateVideoThumbnail(byte[] video, int maxwidth, int maxheight) { byte[] result = null; var filename = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), $"{Guid.NewGuid().ToString()}.tmp" ); File.WriteAllBytes(filename,video); using (FileInputStream fs = new FileInputStream(filename)) { FileDescriptor fd = fs.FD; MediaMetadataRetriever retriever = new MediaMetadataRetriever(); retriever.SetDataSource(fd); Bitmap bitmap = retriever.GetFrameAtTime(0); if (bitmap != null) { bitmap = ScaleImage(bitmap, new Size(maxwidth, maxheight)); MemoryStream stream = new MemoryStream(); bitmap.Compress(Bitmap.CompressFormat.Png, 60, stream); result = stream.ToArray(); } } File.Delete(filename); return result; } public byte[] CreateThumbnail(byte[] source, int maxwidth, int maxheight) { return ScaleImage(source, new Size(maxwidth, maxheight), 60); } public enum ImageOrientation { Undefined = 0, Normal = 1, FlipHorizontal = 2, Rotate180 = 3, FlipVertical = 4, Transpose = 5, Rotate90 = 6, Transverse = 7, Rotate270 = 8 } public async Task PickPhotoAsync(TopLevel window, int? compression, Size? constraints) { var fileResult = await MediaPicker.PickPhotoAsync(); if (fileResult == null) return null; await using var stream = await fileResult.OpenReadAsync(); return await ProcessFile(stream, compression, constraints); } public async Task CapturePhotoAsync(TopLevel window, int? compression, Size? constraints) { var fileResult = await MediaPicker.CapturePhotoAsync(); if (fileResult == null) return null; await using var stream = await fileResult.OpenReadAsync(); return await ProcessFile(stream, compression, constraints); } public async Task PickVideoAsync(TopLevel window) { var fileResult = await MediaPicker.PickPhotoAsync(); if (fileResult == null) return null; await using var stream = await fileResult.OpenReadAsync(); return await ProcessFile(stream, null, null); } public async Task CaptureVideoAsync(TopLevel window) { var fileResult = await MediaPicker.CapturePhotoAsync(); if (fileResult == null) return null; await using var stream = await fileResult.OpenReadAsync(); return await ProcessFile(stream, null, null); } private async Task ProcessFile(Stream stream, int? compression, Size? constraints) { //await using var stream = await fileResult.OpenReadAsync(); var orientation = GetImageOrientation(stream); var source = await BitmapFactory.DecodeStreamAsync(stream); var rotated = RotateImage(source, orientation); var scaled = ScaleImage(rotated, constraints); var jpegFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), $"{Guid.NewGuid()}.jpg"); using (var outStream = new MemoryStream()) { await scaled.CompressAsync(Bitmap.CompressFormat.Jpeg, compression ?? 100, outStream); outStream.Position = 0; await File.WriteAllBytesAsync(jpegFilename, outStream.ToArray()); } return new ImageFile(jpegFilename); } private static Bitmap RotateImage(Bitmap source, ImageOrientation orientation) { var matrix = new Matrix(); switch (orientation) { case ImageOrientation.Normal: break; case ImageOrientation.FlipHorizontal: break; case ImageOrientation.Rotate180: break; case ImageOrientation.FlipVertical: matrix.PreRotate(180); break; case ImageOrientation.Transpose: matrix.PreRotate(90); break; case ImageOrientation.Rotate90: matrix.PreRotate(90); break; case ImageOrientation.Transverse: matrix.PreRotate(-90); break; case ImageOrientation.Rotate270: matrix.PreRotate(-90); break; } return Bitmap.CreateBitmap( source, 0, 0, source.Width, source.Height, matrix, true); } private ImageOrientation GetImageOrientation(Stream stream) { var exif = new ExifInterface(stream); var tag = exif.GetAttribute(ExifInterface.TagOrientation); var orientation = string.IsNullOrEmpty(tag) ? ImageOrientation.Undefined : (ImageOrientation)Enum.Parse(typeof(ImageOrientation), tag); exif.Dispose(); stream.Position = 0; return orientation; } private static Bitmap ScaleImage(Bitmap source, Size? constraints) { var maxwidth = constraints?.Width ?? source.Width; var maxheight = constraints?.Height ?? source.Height; var wRatio = maxwidth < source.Width ? maxwidth / (double)source.Width : 1.0F; var hRatio = maxheight < source.Height ? maxheight / (double)source.Height : 1.0F; var ratio = Math.Min(hRatio, wRatio); var result = (ratio < 1.0F) ? Bitmap.CreateScaledBitmap( source, (int)(source.Width * ratio), (int)(source.Height * ratio), true) : source; return result; } public byte[] RotateImage(byte[] source, float angle, int compression = 100) { if (angle % 360 == 0) return source; byte[] result = { }; using (var image = BitmapFactory.DecodeByteArray(source, 0, source.Length)) { if (image != null) { var matrix = new Matrix(); matrix.PreRotate(angle); var rotated = Bitmap.CreateBitmap( image, 0, 0, image.Width, image.Height, matrix, true); if (rotated != null) { using (var ms = new MemoryStream()) { rotated.Compress(Bitmap.CompressFormat.Jpeg, compression, ms); result = ms.ToArray(); } } } } return result; } public byte[] ScaleImage(byte[] source, Size? constraints, int compression = 100) { byte[] result = { }; using (var image = BitmapFactory.DecodeByteArray(source, 0, source.Length)) { if (image != null) { var scaled = ScaleImage(image, constraints); using (var ms = new MemoryStream()) { scaled.Compress(Bitmap.CompressFormat.Jpeg, compression, ms); result = ms.ToArray(); } } } return result; } } }