ImageTools.Android.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. using System.Drawing;
  2. using Android.Graphics;
  3. using Android.Media;
  4. using Avalonia.Controls;
  5. using InABox.Core;
  6. using Java.IO;
  7. using Microsoft.Maui.Media;
  8. using Bitmap = Android.Graphics.Bitmap;
  9. using File = System.IO.File;
  10. using Path = System.IO.Path;
  11. using Stream = System.IO.Stream;
  12. namespace InABox.Avalonia.Platform.Android
  13. {
  14. public class Android_ImageTools : IImageTools
  15. {
  16. public Logger? Logger { get; set; }
  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. using (FileInputStream fs = new FileInputStream(filename))
  26. {
  27. FileDescriptor fd = fs.FD;
  28. MediaMetadataRetriever retriever = new MediaMetadataRetriever();
  29. retriever.SetDataSource(fd);
  30. Bitmap bitmap = retriever.GetFrameAtTime(0);
  31. if (bitmap != null)
  32. {
  33. bitmap = ScaleImage(bitmap, new Size(maxwidth, maxheight));
  34. MemoryStream stream = new MemoryStream();
  35. bitmap.Compress(Bitmap.CompressFormat.Png, 60, stream);
  36. result = stream.ToArray();
  37. }
  38. }
  39. File.Delete(filename);
  40. return result;
  41. }
  42. public byte[] CreateThumbnail(byte[] source, int maxwidth, int maxheight)
  43. {
  44. return ScaleImage(source, new Size(maxwidth, maxheight), 60);
  45. }
  46. public enum ImageOrientation
  47. {
  48. Undefined = 0,
  49. Normal = 1,
  50. FlipHorizontal = 2,
  51. Rotate180 = 3,
  52. FlipVertical = 4,
  53. Transpose = 5,
  54. Rotate90 = 6,
  55. Transverse = 7,
  56. Rotate270 = 8
  57. }
  58. public async Task<ImageFile?> PickPhotoAsync(TopLevel window, int? compression, Size? constraints)
  59. {
  60. var fileResult = await MediaPicker.PickPhotoAsync();
  61. if (fileResult == null)
  62. return null;
  63. await using var stream = await fileResult.OpenReadAsync();
  64. return await ProcessFile(stream, compression, constraints);
  65. }
  66. public async Task<ImageFile?> CapturePhotoAsync(TopLevel window, int? compression, Size? constraints)
  67. {
  68. var fileResult = await MediaPicker.CapturePhotoAsync();
  69. if (fileResult == null)
  70. return null;
  71. await using var stream = await fileResult.OpenReadAsync();
  72. return await ProcessFile(stream, compression, constraints);
  73. }
  74. public async Task<ImageFile?> PickVideoAsync(TopLevel window)
  75. {
  76. var fileResult = await MediaPicker.PickPhotoAsync();
  77. if (fileResult == null)
  78. return null;
  79. await using var stream = await fileResult.OpenReadAsync();
  80. return await ProcessFile(stream, null, null);
  81. }
  82. public async Task<ImageFile?> CaptureVideoAsync(TopLevel window)
  83. {
  84. var fileResult = await MediaPicker.CapturePhotoAsync();
  85. if (fileResult == null)
  86. return null;
  87. await using var stream = await fileResult.OpenReadAsync();
  88. return await ProcessFile(stream, null, null);
  89. }
  90. private async Task<ImageFile> ProcessFile(Stream stream, int? compression, Size? constraints)
  91. {
  92. //await using var stream = await fileResult.OpenReadAsync();
  93. var orientation = GetImageOrientation(stream);
  94. var source = await BitmapFactory.DecodeStreamAsync(stream);
  95. var rotated = RotateImage(source, orientation);
  96. var scaled = ScaleImage(rotated, constraints);
  97. var jpegFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), $"{Guid.NewGuid()}.jpg");
  98. using (var outStream = new MemoryStream())
  99. {
  100. await scaled.CompressAsync(Bitmap.CompressFormat.Jpeg, compression ?? 100, outStream);
  101. outStream.Position = 0;
  102. await File.WriteAllBytesAsync(jpegFilename, outStream.ToArray());
  103. }
  104. return new ImageFile(jpegFilename);
  105. }
  106. private static Bitmap RotateImage(Bitmap source, ImageOrientation orientation)
  107. {
  108. var matrix = new Matrix();
  109. switch (orientation)
  110. {
  111. case ImageOrientation.Normal:
  112. break;
  113. case ImageOrientation.FlipHorizontal:
  114. break;
  115. case ImageOrientation.Rotate180:
  116. break;
  117. case ImageOrientation.FlipVertical:
  118. matrix.PreRotate(180);
  119. break;
  120. case ImageOrientation.Transpose:
  121. matrix.PreRotate(90);
  122. break;
  123. case ImageOrientation.Rotate90:
  124. matrix.PreRotate(90);
  125. break;
  126. case ImageOrientation.Transverse:
  127. matrix.PreRotate(-90);
  128. break;
  129. case ImageOrientation.Rotate270:
  130. matrix.PreRotate(-90);
  131. break;
  132. }
  133. return Bitmap.CreateBitmap(
  134. source,
  135. 0,
  136. 0,
  137. source.Width,
  138. source.Height,
  139. matrix,
  140. true);
  141. }
  142. private ImageOrientation GetImageOrientation(Stream stream)
  143. {
  144. var exif = new ExifInterface(stream);
  145. var tag = exif.GetAttribute(ExifInterface.TagOrientation);
  146. var orientation = string.IsNullOrEmpty(tag) ?
  147. ImageOrientation.Undefined :
  148. (ImageOrientation)Enum.Parse(typeof(ImageOrientation), tag);
  149. exif.Dispose();
  150. stream.Position = 0;
  151. return orientation;
  152. }
  153. private static Bitmap ScaleImage(Bitmap source, Size? constraints)
  154. {
  155. var maxwidth = constraints?.Width ?? source.Width;
  156. var maxheight = constraints?.Height ?? source.Height;
  157. var wRatio = maxwidth < source.Width
  158. ? maxwidth / (double)source.Width
  159. : 1.0F;
  160. var hRatio = maxheight < source.Height
  161. ? maxheight / (double)source.Height
  162. : 1.0F;
  163. var ratio = Math.Min(hRatio, wRatio);
  164. var result = (ratio < 1.0F)
  165. ? Bitmap.CreateScaledBitmap(
  166. source,
  167. (int)(source.Width * ratio),
  168. (int)(source.Height * ratio),
  169. true)
  170. : source;
  171. return result;
  172. }
  173. public byte[] RotateImage(byte[] source, float angle, int compression = 100)
  174. {
  175. if (angle % 360 == 0)
  176. return source;
  177. byte[] result = { };
  178. using (var image = BitmapFactory.DecodeByteArray(source, 0, source.Length))
  179. {
  180. if (image != null)
  181. {
  182. var matrix = new Matrix();
  183. matrix.PreRotate(angle);
  184. var rotated = Bitmap.CreateBitmap(
  185. image,
  186. 0,
  187. 0,
  188. image.Width,
  189. image.Height,
  190. matrix,
  191. true);
  192. if (rotated != null)
  193. {
  194. using (var ms = new MemoryStream())
  195. {
  196. rotated.Compress(Bitmap.CompressFormat.Jpeg, compression, ms);
  197. result = ms.ToArray();
  198. }
  199. }
  200. }
  201. }
  202. return result;
  203. }
  204. public byte[] ScaleImage(byte[] source, Size? constraints, int compression = 100)
  205. {
  206. byte[] result = { };
  207. using (var image = BitmapFactory.DecodeByteArray(source, 0, source.Length))
  208. {
  209. if (image != null)
  210. {
  211. var scaled = ScaleImage(image, constraints);
  212. using (var ms = new MemoryStream())
  213. {
  214. scaled.Compress(Bitmap.CompressFormat.Jpeg, compression, ms);
  215. result = ms.ToArray();
  216. }
  217. }
  218. }
  219. return result;
  220. }
  221. }
  222. }