|
@@ -1,11 +1,15 @@
|
|
|
|
|
|
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
|
|
@@ -13,8 +17,7 @@ namespace InABox.Mobile.iOS
|
|
|
|
|
|
public class ImageToolsiOS : IImageTools
|
|
|
{
|
|
|
-
|
|
|
- public byte[] CreateVideoThumbnail(byte[] video, float maxwidth, float maxheight)
|
|
|
+ public byte[] CreateVideoThumbnail(byte[] video, int maxwidth, int maxheight)
|
|
|
{
|
|
|
byte[] result = null;
|
|
|
var filename = Path.Combine(
|
|
@@ -38,39 +41,188 @@ namespace InABox.Mobile.iOS
|
|
|
}
|
|
|
|
|
|
if (result != null)
|
|
|
- result = CreateThumbnail(result, maxwidth, maxheight);
|
|
|
+ result = ScaleImage(result, new Size(maxwidth, maxheight), 60);
|
|
|
|
|
|
File.Delete(filename);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- public byte[] CreateThumbnail(byte[] source, float maxwidth, float maxheight)
|
|
|
+ public byte[] CreateThumbnail(byte[] source, int maxwidth, int maxheight)
|
|
|
+ {
|
|
|
+ return ScaleImage(source, new Size(maxwidth, maxheight), 60);
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<FileResult> PickPhotoAsync(int? compression, Size? constraints)
|
|
|
+ {
|
|
|
+ return await MainThread.InvokeOnMainThreadAsync(async () =>
|
|
|
+ await InternalGetPhotoAsync<Permissions.Photos>(UIImagePickerControllerSourceType.PhotoLibrary, compression, constraints));
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<FileResult> CapturePhotoAsync(int? compression, Size? constraints)
|
|
|
+ {
|
|
|
+ return await MainThread.InvokeOnMainThreadAsync(async () =>
|
|
|
+ await InternalGetPhotoAsync<Permissions.Camera>(UIImagePickerControllerSourceType.Camera, compression, constraints));
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<FileResult> InternalGetPhotoAsync<TPermission>(UIImagePickerControllerSourceType source, int? compression, Size? constraints)
|
|
|
+ where TPermission : Permissions.BasePermission, new()
|
|
|
+ {
|
|
|
+ var taskCompletionSource = new TaskCompletionSource<FileResult>();
|
|
|
+ if (await Permissions.RequestAsync<TPermission>() == 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)
|
|
|
+ {
|
|
|
+ CGImage imgRef = source.CGImage;
|
|
|
+ float width = imgRef.Width;
|
|
|
+ float height = imgRef.Height;
|
|
|
+ CGAffineTransform transform = CGAffineTransform.MakeIdentity();
|
|
|
+ RectangleF bounds = new RectangleF(0, 0, width, height);
|
|
|
+
|
|
|
+ float angle = Convert.ToSingle((rotation / 180f) * Math.PI);
|
|
|
+ transform = CGAffineTransform.MakeRotation(angle);
|
|
|
+
|
|
|
+ UIGraphics.BeginImageContext(bounds.Size);
|
|
|
+
|
|
|
+ CGContext context = UIGraphics.GetCurrentContext();
|
|
|
+
|
|
|
+ context.TranslateCTM(width / 2, height / 2);
|
|
|
+ context.SaveState();
|
|
|
+ context.ConcatCTM(transform);
|
|
|
+ context.SaveState();
|
|
|
+ context.ConcatCTM(CGAffineTransform.MakeScale(1.0f, -1.0f));
|
|
|
+
|
|
|
+ context.DrawImage(new RectangleF(-width / 2, -height / 2, width, height), imgRef);
|
|
|
+ context.RestoreState();
|
|
|
+
|
|
|
+ UIImage result = UIGraphics.GetImageFromCurrentImageContext();
|
|
|
+ UIGraphics.EndImageContext();
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)
|
|
|
{
|
|
|
- if ((src.Size.Width > 0.0F) && (src.Size.Height > 0.0F))
|
|
|
+ var scaled = RotateImage(src, angle);
|
|
|
+ using (NSData imageData = scaled.AsJPEG(new nfloat((float)quality / 100F)))
|
|
|
{
|
|
|
- var maxFactor = Math.Min(maxwidth / src.Size.Width, maxheight / src.Size.Height);
|
|
|
- var width = maxFactor * src.Size.Width;
|
|
|
- var height = maxFactor * src.Size.Height;
|
|
|
- UIGraphics.BeginImageContextWithOptions(new CGSize((float)width, (float)height), true, 1.0f);
|
|
|
- src.Draw(new CGRect(0, 0, (float)width, (float)height));
|
|
|
- var tgt = UIGraphics.GetImageFromCurrentImageContext();
|
|
|
- UIGraphics.EndImageContext();
|
|
|
-
|
|
|
- using (NSData imageData = tgt.AsJPEG())
|
|
|
- {
|
|
|
- result = new byte[imageData.Length];
|
|
|
- System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, result, 0,
|
|
|
- Convert.ToInt32(imageData.Length));
|
|
|
- }
|
|
|
+ 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;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
}
|