ImageToolsiOS.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. using System;
  2. using System.Drawing;
  3. using System.IO;
  4. using System.Threading.Tasks;
  5. using AVFoundation;
  6. using CoreGraphics;
  7. using CoreMedia;
  8. using Foundation;
  9. using UIKit;
  10. using Xamarin.Essentials;
  11. using MobileCoreServices;
  12. [assembly: Xamarin.Forms.Dependency(typeof(InABox.Mobile.iOS.ImageToolsiOS))]
  13. namespace InABox.Mobile.iOS
  14. {
  15. public class ImageToolsiOS : IImageTools
  16. {
  17. public byte[] CreateVideoThumbnail(byte[] video, int maxwidth, int maxheight)
  18. {
  19. byte[] result = null;
  20. var filename = Path.Combine(
  21. Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
  22. $"{Guid.NewGuid().ToString()}.tmp"
  23. );
  24. File.WriteAllBytes(filename,video);
  25. // perhaps? on IOS, try to use - NSUrl.FromFilename (path) instead of - new Foundation.NSUrl(url)
  26. var asset = AVAsset.FromUrl(NSUrl.FromFilename(filename));
  27. AVAssetImageGenerator imageGenerator = new AVAssetImageGenerator(asset);
  28. //AVAssetImageGenerator imageGenerator = new AVAssetImageGenerator(AVAsset.FromUrl((new Foundation.NSUrl(filename))));
  29. imageGenerator.AppliesPreferredTrackTransform = true;
  30. CMTime actualTime;
  31. NSError error;
  32. CGImage cgImage = imageGenerator.CopyCGImageAtTime(new CMTime(1, 1), out actualTime, out error);
  33. using (var ms = new MemoryStream())
  34. {
  35. var png = new UIImage(cgImage).AsPNG().AsStream();
  36. png.CopyTo(ms);
  37. result = ms.ToArray();
  38. }
  39. if (result != null)
  40. result = ScaleImage(result, new Size(maxwidth, maxheight), 60);
  41. File.Delete(filename);
  42. return result;
  43. }
  44. public byte[] CreateThumbnail(byte[] source, int maxwidth, int maxheight)
  45. {
  46. return ScaleImage(source, new Size(maxwidth, maxheight), 60);
  47. }
  48. public async Task<FileResult> PickPhotoAsync(int? compression, Size? constraints)
  49. {
  50. return await MainThread.InvokeOnMainThreadAsync(async () =>
  51. await InternalGetPhotoAsync<Permissions.Photos>(UIImagePickerControllerSourceType.PhotoLibrary, compression, constraints));
  52. }
  53. public async Task<FileResult> CapturePhotoAsync(int? compression, Size? constraints)
  54. {
  55. return await MainThread.InvokeOnMainThreadAsync(async () =>
  56. await InternalGetPhotoAsync<Permissions.Camera>(UIImagePickerControllerSourceType.Camera, compression, constraints));
  57. }
  58. private async Task<FileResult> InternalGetPhotoAsync<TPermission>(UIImagePickerControllerSourceType source, int? compression, Size? constraints)
  59. where TPermission : Permissions.BasePermission, new()
  60. {
  61. var taskCompletionSource = new TaskCompletionSource<FileResult>();
  62. if (await Permissions.RequestAsync<TPermission>() == PermissionStatus.Granted)
  63. {
  64. var imagePicker = new UIImagePickerController
  65. {
  66. SourceType = source,
  67. MediaTypes = new string[] { UTType.Image }
  68. };
  69. var viewController = Platform.GetCurrentUIViewController();
  70. imagePicker.AllowsEditing = false;
  71. imagePicker.FinishedPickingMedia += async (sender, e) =>
  72. {
  73. var jpegFilename = Path.Combine(FileSystem.CacheDirectory, $"{Guid.NewGuid()}.jpg");
  74. var source = e.Info[UIImagePickerController.OriginalImage] as UIImage;
  75. var rotated = AutoRotateImage(source);
  76. var scaled = ScaleImage(rotated, constraints);
  77. var result = scaled.AsJPEG(new nfloat(compression ?? 100)/100);
  78. await viewController.DismissViewControllerAsync(true);
  79. if (result.Save(jpegFilename, false, out var error))
  80. {
  81. taskCompletionSource.TrySetResult(new FileResult(jpegFilename));
  82. }
  83. else
  84. {
  85. taskCompletionSource.TrySetException(new Exception($"Error saving the image: {error}"));
  86. }
  87. imagePicker?.Dispose();
  88. imagePicker = null;
  89. };
  90. imagePicker.Canceled += async (sender, e) =>
  91. {
  92. await viewController.DismissViewControllerAsync(true);
  93. taskCompletionSource.TrySetResult(null);
  94. imagePicker?.Dispose();
  95. imagePicker = null;
  96. };
  97. await viewController.PresentViewControllerAsync(imagePicker, true);
  98. }
  99. else
  100. {
  101. taskCompletionSource.TrySetResult(null);
  102. taskCompletionSource.TrySetException(new PermissionException("Camera permission not granted"));
  103. }
  104. return await taskCompletionSource.Task;
  105. }
  106. private UIImage AutoRotateImage(UIImage source)
  107. {
  108. var rotation = source.Orientation switch
  109. {
  110. UIImageOrientation.Right => 90F,
  111. UIImageOrientation.Up => 0F,
  112. UIImageOrientation.Left => -90F,
  113. UIImageOrientation.Down => 180F,
  114. _ => 0F
  115. };
  116. return RotateImage(source, rotation);
  117. }
  118. private UIImage RotateImage(UIImage source, float rotation)
  119. {
  120. CGImage imgRef = source.CGImage;
  121. float width = imgRef.Width;
  122. float height = imgRef.Height;
  123. CGAffineTransform transform = CGAffineTransform.MakeIdentity();
  124. RectangleF bounds = new RectangleF(0, 0, width, height);
  125. float angle = Convert.ToSingle((rotation / 180f) * Math.PI);
  126. transform = CGAffineTransform.MakeRotation(angle);
  127. UIGraphics.BeginImageContext(bounds.Size);
  128. CGContext context = UIGraphics.GetCurrentContext();
  129. context.TranslateCTM(width / 2, height / 2);
  130. context.SaveState();
  131. context.ConcatCTM(transform);
  132. context.SaveState();
  133. context.ConcatCTM(CGAffineTransform.MakeScale(1.0f, -1.0f));
  134. context.DrawImage(new RectangleF(-width / 2, -height / 2, width, height), imgRef);
  135. context.RestoreState();
  136. UIImage result = UIGraphics.GetImageFromCurrentImageContext();
  137. UIGraphics.EndImageContext();
  138. return result;
  139. }
  140. private UIImage ScaleImage(UIImage sourceImage, Size? constraints)
  141. {
  142. var maxwidth = constraints?.Width ?? sourceImage.Size.Width;
  143. var maxheight = constraints?.Height ?? sourceImage.Size.Height;
  144. var wRatio = maxwidth < sourceImage.Size.Width
  145. ? maxwidth / (double)sourceImage.Size.Width
  146. : 1.0F;
  147. var hRatio = maxheight < sourceImage.Size.Height
  148. ? maxheight / (double)sourceImage.Size.Height
  149. : 1.0F;
  150. var ratio = Math.Min(hRatio, wRatio);
  151. if (ratio < 1.0F)
  152. {
  153. var width = ratio * sourceImage.Size.Width;
  154. var height = ratio * sourceImage.Size.Height;
  155. UIGraphics.BeginImageContext(new CGSize(width, height));
  156. sourceImage.Draw(new CGRect(0, 0, width, height));
  157. var resultImage = UIGraphics.GetImageFromCurrentImageContext();
  158. UIGraphics.EndImageContext();
  159. return resultImage;
  160. }
  161. return sourceImage;
  162. }
  163. public byte[] RotateImage(byte[] source, float angle, int quality = 100)
  164. {
  165. byte[] result = { };
  166. using (UIImage src = UIImage.LoadFromData(NSData.FromArray(source)))
  167. {
  168. if (src != null)
  169. {
  170. var scaled = RotateImage(src, angle);
  171. using (NSData imageData = scaled.AsJPEG(new nfloat((float)quality / 100F)))
  172. {
  173. result = new byte[imageData.Length];
  174. System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, result, 0,
  175. Convert.ToInt32(imageData.Length));
  176. }
  177. }
  178. }
  179. return result;
  180. }
  181. public byte[] ScaleImage(byte[] source, Size? constraints, int quality = 100)
  182. {
  183. byte[] result = { };
  184. using (UIImage src = UIImage.LoadFromData(NSData.FromArray(source)))
  185. {
  186. if (src != null)
  187. {
  188. var scaled = ScaleImage(src, constraints);
  189. using (NSData imageData = scaled.AsJPEG(new nfloat((float)quality / 100F)))
  190. {
  191. result = new byte[imageData.Length];
  192. System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, result, 0,
  193. Convert.ToInt32(imageData.Length));
  194. }
  195. }
  196. }
  197. return result;
  198. }
  199. }
  200. }