Bitmap.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. using System;
  2. using System.IO;
  3. using System.Runtime.CompilerServices;
  4. using System.Runtime.InteropServices;
  5. using Avalonia.Platform;
  6. using Avalonia.Utilities;
  7. namespace Avalonia.Media.Imaging
  8. {
  9. /// <summary>
  10. /// Holds a bitmap image.
  11. /// </summary>
  12. public class Bitmap : IBitmap, IImageBrushSource
  13. {
  14. private bool _isTranscoded;
  15. /// <summary>
  16. /// Loads a Bitmap from a stream and decodes at the desired width. Aspect ratio is maintained.
  17. /// This is more efficient than loading and then resizing.
  18. /// </summary>
  19. /// <param name="stream">The stream to read the bitmap from. This can be any supported image format.</param>
  20. /// <param name="width">The desired width of the resulting bitmap.</param>
  21. /// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should any scaling be required.</param>
  22. /// <returns>An instance of the <see cref="Bitmap"/> class.</returns>
  23. public static Bitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
  24. {
  25. return new Bitmap(GetFactory().LoadBitmapToWidth(stream, width, interpolationMode));
  26. }
  27. /// <summary>
  28. /// Loads a Bitmap from a stream and decodes at the desired height. Aspect ratio is maintained.
  29. /// This is more efficient than loading and then resizing.
  30. /// </summary>
  31. /// <param name="stream">The stream to read the bitmap from. This can be any supported image format.</param>
  32. /// <param name="height">The desired height of the resulting bitmap.</param>
  33. /// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should any scaling be required.</param>
  34. /// <returns>An instance of the <see cref="Bitmap"/> class.</returns>
  35. public static Bitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
  36. {
  37. return new Bitmap(GetFactory().LoadBitmapToHeight(stream, height, interpolationMode));
  38. }
  39. /// <summary>
  40. /// Creates a Bitmap scaled to a specified size from the current bitmap.
  41. /// </summary>
  42. /// <param name="destinationSize">The destination size.</param>
  43. /// <param name="interpolationMode">The <see cref="BitmapInterpolationMode"/> to use should any scaling be required.</param>
  44. /// <returns>An instance of the <see cref="Bitmap"/> class.</returns>
  45. public Bitmap CreateScaledBitmap(PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
  46. {
  47. return new Bitmap(GetFactory().ResizeBitmap(PlatformImpl.Item, destinationSize, interpolationMode));
  48. }
  49. /// <summary>
  50. /// Initializes a new instance of the <see cref="Bitmap"/> class.
  51. /// </summary>
  52. /// <param name="fileName">The filename of the bitmap.</param>
  53. public Bitmap(string fileName)
  54. {
  55. PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(fileName));
  56. }
  57. /// <summary>
  58. /// Initializes a new instance of the <see cref="Bitmap"/> class.
  59. /// </summary>
  60. /// <param name="stream">The stream to read the bitmap from.</param>
  61. public Bitmap(Stream stream)
  62. {
  63. PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(stream));
  64. }
  65. /// <summary>
  66. /// Initializes a new instance of the <see cref="Bitmap"/> class.
  67. /// </summary>
  68. /// <param name="impl">A platform-specific bitmap implementation.</param>
  69. internal Bitmap(IRef<IBitmapImpl> impl)
  70. {
  71. PlatformImpl = impl.Clone();
  72. }
  73. /// <summary>
  74. /// Initializes a new instance of the <see cref="Bitmap"/> class.
  75. /// </summary>
  76. /// <param name="impl">A platform-specific bitmap implementation. Bitmap class takes the ownership.</param>
  77. protected Bitmap(IBitmapImpl impl)
  78. {
  79. PlatformImpl = RefCountable.Create(impl);
  80. }
  81. /// <inheritdoc/>
  82. public virtual void Dispose()
  83. {
  84. PlatformImpl.Dispose();
  85. }
  86. /// <summary>
  87. /// Initializes a new instance of the <see cref="Bitmap"/> class.
  88. /// </summary>
  89. /// <param name="format">The pixel format.</param>
  90. /// <param name="alphaFormat">The alpha format.</param>
  91. /// <param name="data">The pointer to the source bytes.</param>
  92. /// <param name="size">The size of the bitmap in device pixels.</param>
  93. /// <param name="dpi">The DPI of the bitmap.</param>
  94. /// <param name="stride">The number of bytes per row.</param>
  95. public Bitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
  96. {
  97. var factory = GetFactory();
  98. if (factory.IsSupportedBitmapPixelFormat(format))
  99. PlatformImpl = RefCountable.Create(factory.LoadBitmap(format, alphaFormat, data, size, dpi, stride));
  100. else
  101. {
  102. var transcoded = Marshal.AllocHGlobal(size.Width * size.Height * 4);
  103. var transcodedStride = size.Width * 4;
  104. try
  105. {
  106. PixelFormatReader.Transcode(transcoded, data, size, stride, transcodedStride, format);
  107. var transcodedAlphaFormat = format.HasAlpha ? alphaFormat : AlphaFormat.Opaque;
  108. PlatformImpl = RefCountable.Create(factory.LoadBitmap(PixelFormat.Rgba8888, transcodedAlphaFormat,
  109. transcoded, size, dpi, transcodedStride));
  110. }
  111. finally
  112. {
  113. Marshal.FreeHGlobal(transcoded);
  114. }
  115. _isTranscoded = true;
  116. }
  117. }
  118. /// <inheritdoc/>
  119. public Vector Dpi => PlatformImpl.Item.Dpi;
  120. /// <inheritdoc/>
  121. public Size Size => PlatformImpl.Item.PixelSize.ToSizeWithDpi(Dpi);
  122. /// <inheritdoc/>
  123. public PixelSize PixelSize => PlatformImpl.Item.PixelSize;
  124. /// <summary>
  125. /// Gets the platform-specific bitmap implementation.
  126. /// </summary>
  127. internal IRef<IBitmapImpl> PlatformImpl { get; }
  128. IRef<IBitmapImpl> IBitmap.PlatformImpl => PlatformImpl;
  129. /// <summary>
  130. /// Saves the bitmap to a file.
  131. /// </summary>
  132. /// <param name="fileName">The filename.</param>
  133. /// <param name="quality">
  134. /// The optional quality for compression.
  135. /// The quality value is interpreted from 0 - 100. If quality is null the default quality
  136. /// setting is applied.
  137. /// </param>
  138. public void Save(string fileName, int? quality = null)
  139. {
  140. PlatformImpl.Item.Save(fileName, quality);
  141. }
  142. /// <summary>
  143. /// Saves the bitmap to a stream.
  144. /// </summary>
  145. /// <param name="stream">The stream.</param>
  146. /// <param name="quality">
  147. /// The optional quality for compression.
  148. /// The quality value is interpreted from 0 - 100. If quality is null the default quality
  149. /// setting is applied.
  150. /// </param>
  151. public void Save(Stream stream, int? quality = null)
  152. {
  153. PlatformImpl.Item.Save(stream, quality);
  154. }
  155. public virtual PixelFormat? Format => (PlatformImpl.Item as IReadableBitmapImpl)?.Format;
  156. private protected unsafe void CopyPixelsCore(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride,
  157. ILockedFramebuffer fb)
  158. {
  159. if ((sourceRect.Width <= 0 || sourceRect.Height <= 0) && (sourceRect.X != 0 || sourceRect.Y != 0))
  160. throw new ArgumentOutOfRangeException(nameof(sourceRect));
  161. if (sourceRect.X < 0 || sourceRect.Y < 0)
  162. throw new ArgumentOutOfRangeException(nameof(sourceRect));
  163. if (sourceRect.Width <= 0)
  164. sourceRect = sourceRect.WithWidth(PixelSize.Width);
  165. if (sourceRect.Height <= 0)
  166. sourceRect = sourceRect.WithHeight(PixelSize.Height);
  167. if (sourceRect.Right > PixelSize.Width || sourceRect.Bottom > PixelSize.Height)
  168. throw new ArgumentOutOfRangeException(nameof(sourceRect));
  169. int minStride = checked(((sourceRect.Width * fb.Format.BitsPerPixel) + 7) / 8);
  170. if (stride < minStride)
  171. throw new ArgumentOutOfRangeException(nameof(stride));
  172. var minBufferSize = stride * sourceRect.Height;
  173. if (minBufferSize > bufferSize)
  174. throw new ArgumentOutOfRangeException(nameof(bufferSize));
  175. for (var y = 0; y < sourceRect.Height; y++)
  176. {
  177. var srcAddress = fb.Address + fb.RowBytes * y;
  178. var dstAddress = buffer + stride * y;
  179. Unsafe.CopyBlock(dstAddress.ToPointer(), srcAddress.ToPointer(), (uint)minStride);
  180. }
  181. }
  182. public virtual void CopyPixels(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride)
  183. {
  184. if (
  185. Format == null
  186. || PlatformImpl.Item is not IReadableBitmapImpl readable
  187. || Format != readable.Format
  188. )
  189. throw new NotSupportedException("CopyPixels is not supported for this bitmap type");
  190. if (_isTranscoded)
  191. throw new NotSupportedException("CopyPixels is not supported for transcoded bitmaps");
  192. using (var fb = readable.Lock())
  193. CopyPixelsCore(sourceRect, buffer, bufferSize, stride, fb);
  194. }
  195. /// <inheritdoc/>
  196. void IImage.Draw(
  197. DrawingContext context,
  198. Rect sourceRect,
  199. Rect destRect)
  200. {
  201. context.DrawBitmap(
  202. PlatformImpl,
  203. 1,
  204. sourceRect,
  205. destRect);
  206. }
  207. private static IPlatformRenderInterface GetFactory()
  208. {
  209. return AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
  210. }
  211. IRef<IBitmapImpl> IImageBrushSource.Bitmap => PlatformImpl;
  212. }
  213. }