using System; using System.Drawing; using System.IO; using System.Threading.Tasks; using AVFoundation; using CoreGraphics; using CoreMedia; using Foundation; using UIKit; using Xamarin.Essentials; using MobileCoreServices; [assembly: Xamarin.Forms.Dependency(typeof(InABox.Mobile.iOS.ImageToolsiOS))] namespace InABox.Mobile.iOS { public class ImageToolsiOS : IImageTools { 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); // perhaps? on IOS, try to use - NSUrl.FromFilename (path) instead of - new Foundation.NSUrl(url) var asset = AVAsset.FromUrl(NSUrl.FromFilename(filename)); AVAssetImageGenerator imageGenerator = new AVAssetImageGenerator(asset); //AVAssetImageGenerator imageGenerator = new AVAssetImageGenerator(AVAsset.FromUrl((new Foundation.NSUrl(filename)))); imageGenerator.AppliesPreferredTrackTransform = true; CMTime actualTime; NSError error; CGImage cgImage = imageGenerator.CopyCGImageAtTime(new CMTime(1, 1), out actualTime, out error); using (var ms = new MemoryStream()) { var png = new UIImage(cgImage).AsPNG().AsStream(); png.CopyTo(ms); result = ms.ToArray(); } if (result != null) result = ScaleImage(result, new Size(maxwidth, maxheight), 60); File.Delete(filename); return result; } public byte[] CreateThumbnail(byte[] source, int maxwidth, int maxheight) { return ScaleImage(source, new Size(maxwidth, maxheight), 60); } public async Task PickPhotoAsync(int? compression, Size? constraints) { return await MainThread.InvokeOnMainThreadAsync(async () => await InternalGetPhotoAsync(UIImagePickerControllerSourceType.PhotoLibrary, compression, constraints)); } public async Task CapturePhotoAsync(int? compression, Size? constraints) { return await MainThread.InvokeOnMainThreadAsync(async () => await InternalGetPhotoAsync(UIImagePickerControllerSourceType.Camera, compression, constraints)); } private async Task InternalGetPhotoAsync(UIImagePickerControllerSourceType source, int? compression, Size? constraints) where TPermission : Permissions.BasePermission, new() { var taskCompletionSource = new TaskCompletionSource(); if (await Permissions.RequestAsync() == PermissionStatus.Granted) { var imagePicker = new UIImagePickerController { SourceType = source, MediaTypes = new string[] { UTType.Image } }; var viewController = Platform.GetCurrentUIViewController(); imagePicker.AllowsEditing = false; imagePicker.FinishedPickingMedia += async (sender, e) => { var jpegFilename = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid()}.jpg"); var source = e.Info[UIImagePickerController.OriginalImage] as UIImage; var rotated = AutoRotateImage(source); var scaled = ScaleImage(rotated, constraints); var result = scaled.AsJPEG(new nfloat(compression ?? 100)/100); await viewController.DismissViewControllerAsync(true); if (result.Save(jpegFilename, false, out var error)) { taskCompletionSource.TrySetResult(new FileResult(jpegFilename)); } else { taskCompletionSource.TrySetException(new Exception($"Error saving the image: {error}")); } imagePicker?.Dispose(); imagePicker = null; }; imagePicker.Canceled += async (sender, e) => { await viewController.DismissViewControllerAsync(true); taskCompletionSource.TrySetResult(null); imagePicker?.Dispose(); imagePicker = null; }; await viewController.PresentViewControllerAsync(imagePicker, true); } else { taskCompletionSource.TrySetResult(null); taskCompletionSource.TrySetException(new PermissionException("Camera permission not granted")); } return await taskCompletionSource.Task; } private UIImage AutoRotateImage(UIImage source) { var rotation = source.Orientation switch { UIImageOrientation.Right => 90F, UIImageOrientation.Up => 0F, UIImageOrientation.Left => -90F, UIImageOrientation.Down => 180F, _ => 0F }; return RotateImage(source, rotation); } private UIImage RotateImage(UIImage source, float rotation) { UIImage imageToReturn = null; float radians = -1 * ((float)(rotation * Math.PI) / 180); bool isLandscape = false; var x = source.Size.Width / 2; var y = source.Size.Height / 2; //https://stackoverflow.com/a/8536553 CGAffineTransform transform = new CGAffineTransform((nfloat)Math.Cos(radians), (nfloat)Math.Sin(radians), -(nfloat)Math.Sin(radians), (nfloat)Math.Cos(radians), (nfloat)(x - x * Math.Cos(radians)) + (nfloat)(y * Math.Sin(radians)), (nfloat)(y - x * Math.Sin(radians) - y * Math.Cos(radians))); var diff = (source.Size.Height - source.Size.Width) / 2; bool translateWidthAndHeight = false; if (rotation == 90) { translateWidthAndHeight = true; transform.Translate(diff, -diff); } else if (rotation == 180) { //Transform.Translate(image.Size.Width, -image.Size.Height); } else if (rotation == 270) { translateWidthAndHeight = true; transform.Translate(diff, -diff); } else if (rotation == 360) { } if (translateWidthAndHeight) { //now draw image using (var context = new CGBitmapContext(IntPtr.Zero, (int)source.Size.Height, (int)source.Size.Width, source.CGImage.BitsPerComponent, source.CGImage.BitsPerComponent * (int)source.Size.Width, source.CGImage.ColorSpace, source.CGImage.BitmapInfo)) { context.ConcatCTM(transform); context.DrawImage(new RectangleF(PointF.Empty, new SizeF((float)source.Size.Width, (float)source.Size.Height)), source.CGImage); using (var imageRef = context.ToImage()) { imageToReturn = new UIImage(imageRef); } } } else { //now draw image using (var context = new CGBitmapContext(IntPtr.Zero, (int)source.Size.Width, (int)source.Size.Height, source.CGImage.BitsPerComponent, source.CGImage.BitsPerComponent * (int)source.Size.Height, source.CGImage.ColorSpace, source.CGImage.BitmapInfo)) { context.ConcatCTM(transform); context.DrawImage(new RectangleF(PointF.Empty, new SizeF((float)source.Size.Width, (float)source.Size.Height)), source.CGImage); using (var imageRef = context.ToImage()) { imageToReturn = new UIImage(imageRef); } } } return imageToReturn; } private UIImage ScaleImage(UIImage sourceImage, Size? constraints) { var maxwidth = constraints?.Width ?? sourceImage.Size.Width; var maxheight = constraints?.Height ?? sourceImage.Size.Height; var wRatio = maxwidth < sourceImage.Size.Width ? maxwidth / (double)sourceImage.Size.Width : 1.0F; var hRatio = maxheight < sourceImage.Size.Height ? maxheight / (double)sourceImage.Size.Height : 1.0F; var ratio = Math.Min(hRatio, wRatio); if (ratio < 1.0F) { var width = ratio * sourceImage.Size.Width; var height = ratio * sourceImage.Size.Height; UIGraphics.BeginImageContext(new CGSize(width, height)); sourceImage.Draw(new CGRect(0, 0, width, height)); var resultImage = UIGraphics.GetImageFromCurrentImageContext(); UIGraphics.EndImageContext(); return resultImage; } return sourceImage; } public byte[] RotateImage(byte[] source, float angle, int quality = 100) { byte[] result = { }; using (UIImage src = UIImage.LoadFromData(NSData.FromArray(source))) { if (src != null) { var scaled = RotateImage(src, angle); using (NSData imageData = scaled.AsJPEG(new nfloat((float)quality / 100F))) { result = new byte[imageData.Length]; System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, result, 0, Convert.ToInt32(imageData.Length)); } } } return result; } public byte[] ScaleImage(byte[] source, Size? constraints, int quality = 100) { byte[] result = { }; using (UIImage src = UIImage.LoadFromData(NSData.FromArray(source))) { if (src != null) { var scaled = ScaleImage(src, constraints); using (NSData imageData = scaled.AsJPEG(new nfloat((float)quality / 100F))) { result = new byte[imageData.Length]; System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, result, 0, Convert.ToInt32(imageData.Length)); } } } return result; } } }