ImageTools.Android.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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.PickVideoAsync();
  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.CaptureVideoAsync();
  85. if (fileResult == null)
  86. return null;
  87. await using var stream = await fileResult.OpenReadAsync();
  88. using var memStream = new MemoryStream();
  89. stream.CopyTo(memStream);
  90. return new ImageFile(fileResult.FileName, memStream.ToArray());
  91. }
  92. private async Task<ImageFile> ProcessFile(Stream stream, int? compression, Size? constraints)
  93. {
  94. //await using var stream = await fileResult.OpenReadAsync();
  95. var orientation = GetImageOrientation(stream);
  96. var source = await BitmapFactory.DecodeStreamAsync(stream);
  97. var rotated = RotateImage(source, orientation);
  98. var scaled = ScaleImage(rotated, constraints);
  99. var jpegFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), $"{Guid.NewGuid()}.jpg");
  100. using (var outStream = new MemoryStream())
  101. {
  102. await scaled.CompressAsync(Bitmap.CompressFormat.Jpeg, compression ?? 100, outStream);
  103. outStream.Position = 0;
  104. await File.WriteAllBytesAsync(jpegFilename, outStream.ToArray());
  105. }
  106. return new ImageFile(jpegFilename);
  107. }
  108. private static Bitmap RotateImage(Bitmap source, ImageOrientation orientation)
  109. {
  110. var matrix = new Matrix();
  111. switch (orientation)
  112. {
  113. case ImageOrientation.Normal:
  114. break;
  115. case ImageOrientation.FlipHorizontal:
  116. break;
  117. case ImageOrientation.Rotate180:
  118. break;
  119. case ImageOrientation.FlipVertical:
  120. matrix.PreRotate(180);
  121. break;
  122. case ImageOrientation.Transpose:
  123. matrix.PreRotate(90);
  124. break;
  125. case ImageOrientation.Rotate90:
  126. matrix.PreRotate(90);
  127. break;
  128. case ImageOrientation.Transverse:
  129. matrix.PreRotate(-90);
  130. break;
  131. case ImageOrientation.Rotate270:
  132. matrix.PreRotate(-90);
  133. break;
  134. }
  135. return Bitmap.CreateBitmap(
  136. source,
  137. 0,
  138. 0,
  139. source.Width,
  140. source.Height,
  141. matrix,
  142. true);
  143. }
  144. private ImageOrientation GetImageOrientation(Stream stream)
  145. {
  146. var exif = new ExifInterface(stream);
  147. var tag = exif.GetAttribute(ExifInterface.TagOrientation);
  148. var orientation = string.IsNullOrEmpty(tag) ?
  149. ImageOrientation.Undefined :
  150. (ImageOrientation)Enum.Parse(typeof(ImageOrientation), tag);
  151. exif.Dispose();
  152. stream.Position = 0;
  153. return orientation;
  154. }
  155. private static Bitmap ScaleImage(Bitmap source, Size? constraints)
  156. {
  157. var maxwidth = constraints?.Width ?? source.Width;
  158. var maxheight = constraints?.Height ?? source.Height;
  159. var wRatio = maxwidth < source.Width
  160. ? maxwidth / (double)source.Width
  161. : 1.0F;
  162. var hRatio = maxheight < source.Height
  163. ? maxheight / (double)source.Height
  164. : 1.0F;
  165. var ratio = Math.Min(hRatio, wRatio);
  166. var result = (ratio < 1.0F)
  167. ? Bitmap.CreateScaledBitmap(
  168. source,
  169. (int)(source.Width * ratio),
  170. (int)(source.Height * ratio),
  171. true)
  172. : source;
  173. return result;
  174. }
  175. public byte[] RotateImage(byte[] source, float angle, int compression = 100)
  176. {
  177. if (angle % 360 == 0)
  178. return source;
  179. byte[] result = { };
  180. using (var image = BitmapFactory.DecodeByteArray(source, 0, source.Length))
  181. {
  182. if (image != null)
  183. {
  184. var matrix = new Matrix();
  185. matrix.PreRotate(angle);
  186. var rotated = Bitmap.CreateBitmap(
  187. image,
  188. 0,
  189. 0,
  190. image.Width,
  191. image.Height,
  192. matrix,
  193. true);
  194. if (rotated != null)
  195. {
  196. using (var ms = new MemoryStream())
  197. {
  198. rotated.Compress(Bitmap.CompressFormat.Jpeg, compression, ms);
  199. result = ms.ToArray();
  200. }
  201. }
  202. }
  203. }
  204. return result;
  205. }
  206. public byte[] ScaleImage(byte[] source, Size? constraints, int compression = 100)
  207. {
  208. byte[] result = { };
  209. using (var image = BitmapFactory.DecodeByteArray(source, 0, source.Length))
  210. {
  211. if (image != null)
  212. {
  213. var scaled = ScaleImage(image, constraints);
  214. using (var ms = new MemoryStream())
  215. {
  216. scaled.Compress(Bitmap.CompressFormat.Jpeg, compression, ms);
  217. result = ms.ToArray();
  218. }
  219. }
  220. }
  221. return result;
  222. }
  223. }
  224. }