ClipboardImageOperations.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using System.Runtime.InteropServices;
  2. using Avalonia.Input;
  3. using Avalonia.Input.Platform;
  4. using Avalonia.Media.Imaging;
  5. using PicView.Avalonia.Navigation;
  6. using PicView.Avalonia.ViewModels;
  7. using PicView.Core.DebugTools;
  8. using PicView.Core.ImageDecoding;
  9. using PicView.Core.Localization;
  10. namespace PicView.Avalonia.Clipboard;
  11. /// <summary>
  12. /// Handles clipboard operations related to images
  13. /// </summary>
  14. public static class ClipboardImageOperations
  15. {
  16. /// <summary>
  17. /// Copies the current image to the clipboard
  18. /// </summary>
  19. /// <param name="vm">The main view model</param>
  20. /// <returns>A task representing the asynchronous operation</returns>
  21. public static async Task<bool> CopyImageToClipboard(MainViewModel vm)
  22. {
  23. var clipboard = ClipboardService.GetClipboard();
  24. if (clipboard == null || vm.PicViewer.ImageSource.CurrentValue is not Bitmap bitmap)
  25. {
  26. return false;
  27. }
  28. return await ClipboardService.ExecuteClipboardOperation(async () =>
  29. {
  30. await clipboard.ClearAsync();
  31. // Handle for Windows
  32. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  33. {
  34. await vm.PlatformService.CopyImageToClipboard(bitmap);
  35. return true;
  36. }
  37. // Handle for other platforms
  38. using var ms = new MemoryStream();
  39. bitmap.Save(ms);
  40. var dataObject = new DataObject();
  41. dataObject.Set("image/png", ms.ToArray());
  42. await clipboard.SetDataObjectAsync(dataObject);
  43. return true;
  44. });
  45. }
  46. /// <summary>
  47. /// Copies an image as base64 string to the clipboard
  48. /// </summary>
  49. /// <param name="path">Optional path to the image file</param>
  50. /// <param name="vm">The main view model</param>
  51. /// <returns>A task representing the asynchronous operation</returns>
  52. public static async Task<bool> CopyBase64ToClipboard(string path, MainViewModel vm)
  53. {
  54. var clipboard = ClipboardService.GetClipboard();
  55. if (clipboard == null)
  56. {
  57. return false;
  58. }
  59. return await ClipboardService.ExecuteClipboardOperation(async () =>
  60. {
  61. try
  62. {
  63. var base64 = await GetBase64String(path, vm);
  64. if (string.IsNullOrEmpty(base64))
  65. {
  66. return false;
  67. }
  68. await clipboard.SetTextAsync(base64);
  69. return true;
  70. }
  71. catch (Exception ex)
  72. {
  73. DebugHelper.LogDebug(nameof(ClipboardImageOperations), nameof(CopyBase64ToClipboard), ex);
  74. return false;
  75. }
  76. });
  77. }
  78. private static async Task<string> GetBase64String(string path, MainViewModel vm)
  79. {
  80. if (!string.IsNullOrWhiteSpace(path))
  81. {
  82. return Convert.ToBase64String(await File.ReadAllBytesAsync(path));
  83. }
  84. switch (vm.PicViewer.ImageType.CurrentValue)
  85. {
  86. case ImageType.AnimatedGif:
  87. case ImageType.AnimatedWebp:
  88. case ImageType.Bitmap:
  89. if (vm.PicViewer.ImageSource.CurrentValue is not Bitmap bitmap)
  90. {
  91. return string.Empty;
  92. }
  93. using (var stream = new MemoryStream())
  94. {
  95. bitmap.Save(stream, 100);
  96. return Convert.ToBase64String(stream.ToArray());
  97. }
  98. case ImageType.Svg:
  99. return string.Empty;
  100. default:
  101. throw new ArgumentOutOfRangeException(nameof(vm.PicViewer.ImageType), $"Unsupported image type: {vm.PicViewer.ImageType}");
  102. }
  103. }
  104. /// <summary>
  105. /// Pastes an image from the clipboard
  106. /// </summary>
  107. /// <param name="vm">The main view model</param>
  108. /// <returns>A task representing the asynchronous operation</returns>
  109. public static async Task PasteClipboardImage(MainViewModel vm)
  110. {
  111. var clipboard = ClipboardService.GetClipboard();
  112. if (clipboard == null)
  113. {
  114. return;
  115. }
  116. try
  117. {
  118. var name = TranslationManager.Translation.ClipboardImage;
  119. // Try standard image formats
  120. var bitmap = await TryGetBitmapFromClipboard(clipboard);
  121. // Try Windows-specific clipboard handling if needed
  122. if (bitmap == null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  123. {
  124. bitmap = await vm.PlatformService.GetImageFromClipboard();
  125. }
  126. // Set the image if we got one
  127. if (bitmap != null)
  128. {
  129. await UpdateImage.SetSingleImageAsync(bitmap, ImageType.Bitmap, name, vm);
  130. }
  131. }
  132. catch (Exception ex)
  133. {
  134. DebugHelper.LogDebug(nameof(ClipboardImageOperations), nameof(PasteClipboardImage), ex);
  135. }
  136. }
  137. private static async Task<Bitmap?> TryGetBitmapFromClipboard(IClipboard clipboard)
  138. {
  139. // List of formats to try
  140. var formats = new[]
  141. {
  142. "PNG", "image/jpeg", "image/png", "image/bmp", "BMP",
  143. "JPG", "JPEG", "image/tiff", "GIF", "image/gif"
  144. };
  145. foreach (var format in formats)
  146. {
  147. try
  148. {
  149. var data = await clipboard.GetDataAsync(format);
  150. if (data is not byte[] dataBytes)
  151. {
  152. continue;
  153. }
  154. using var memoryStream = new MemoryStream(dataBytes);
  155. return new Bitmap(memoryStream);
  156. }
  157. catch (Exception ex)
  158. {
  159. // Ignore format errors and try next format
  160. DebugHelper.LogDebug(nameof(ClipboardImageOperations), nameof(TryGetBitmapFromClipboard), ex);
  161. }
  162. }
  163. return null;
  164. }
  165. }