Carregar um BitmapImage WPF de um System.Drawing.Bitmap

Eu tenho uma instância de um System.Drawing.Bitmap e gostaria de disponibilizá-lo para o meu aplicativo WPF na forma de um System.Windows.Media.Imaging.BitmapImage .

Qual seria a melhor abordagem para isso?

Que tal carregá-lo do MemoryStream?

 using(MemoryStream memory = new MemoryStream()) { bitmap.Save(memory, ImageFormat.Png); memory.Position = 0; BitmapImage bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); } 

Graças a Hallgrim, aqui está o código que acabei com:

 ScreenCapture = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( bmp.GetHbitmap(), IntPtr.Zero, System.Windows.Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(width, height)); 

Eu também acabei ligando para um BitmapSource em vez de um BitmapImage como na minha pergunta original

Eu sei que isso foi respondido, mas aqui estão alguns methods de extensão (para .NET 3.0+) que fazem a conversão. 🙂

  ///  /// Converts a  into a WPF . ///  /// The source image. /// A BitmapSource public static BitmapSource ToBitmapSource(this System.Drawing.Image source) { System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(source); var bitSrc = bitmap.ToBitmapSource(); bitmap.Dispose(); bitmap = null; return bitSrc; } ///  /// Converts a  into a WPF . ///  /// Uses GDI to do the conversion. Hence the call to the marshalled DeleteObject. ///  /// The source bitmap. /// A BitmapSource public static BitmapSource ToBitmapSource(this System.Drawing.Bitmap source) { BitmapSource bitSrc = null; var hBitmap = source.GetHbitmap(); try { bitSrc = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } catch (Win32Exception) { bitSrc = null; } finally { NativeMethods.DeleteObject(hBitmap); } return bitSrc; } 

e a class NativeMethods (para apaziguar o FxCop)

  ///  /// FxCop requires all Marshalled functions to be in a class called NativeMethods. ///  internal static class NativeMethods { [DllImport("gdi32.dll")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool DeleteObject(IntPtr hObject); } 

Levei algum tempo para fazer a conversão funcionar nos dois sentidos, então aqui estão os dois methods de extensão que eu criei:

 using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Windows.Media.Imaging; public static class BitmapConversion { public static Bitmap ToWinFormsBitmap(this BitmapSource bitmapsource) { using (MemoryStream stream = new MemoryStream()) { BitmapEncoder enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(bitmapsource)); enc.Save(stream); using (var tempBitmap = new Bitmap(stream)) { // According to MSDN, one "must keep the stream open for the lifetime of the Bitmap." // So we return a copy of the new bitmap, allowing us to dispose both the bitmap and the stream. return new Bitmap(tempBitmap); } } } public static BitmapSource ToWpfBitmap(this Bitmap bitmap) { using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream, ImageFormat.Bmp); stream.Position = 0; BitmapImage result = new BitmapImage(); result.BeginInit(); // According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed." // Force the bitmap to load right now so we can dispose the stream. result.CacheOption = BitmapCacheOption.OnLoad; result.StreamSource = stream; result.EndInit(); result.Freeze(); return result; } } } 

O mais fácil é fazer o bitmap do WPF diretamente de um arquivo.

Caso contrário, você terá que usar o System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap.

 // at class level; [System.Runtime.InteropServices.DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject); // https://stackoverflow.com/a/1546121/194717 ///  /// Converts a  into a WPF . ///  /// Uses GDI to do the conversion. Hence the call to the marshalled DeleteObject. ///  /// The source bitmap. /// A BitmapSource public static System.Windows.Media.Imaging.BitmapSource ToBitmapSource(this System.Drawing.Bitmap source) { var hBitmap = source.GetHbitmap(); var result = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); DeleteObject(hBitmap); return result; } 

Eu trabalho em um fornecedor de imagem e escrevi um adaptador para o WPF para o nosso formato de imagem que é semelhante a um System.Drawing.Bitmap.

Eu escrevi este KB para explicar aos nossos clientes:

http://www.atalasoft.com/kb/article.aspx?id=10156

E há código lá que faz isso. Você precisa replace o AtalaImage pelo Bitmap e fazer o equivalente que estamos fazendo – deve ser bastante simples.

Minha opinião sobre isso é construída a partir de vários resources. https://stackoverflow.com/a/7035036 https://stackoverflow.com/a/1470182/360211

 using System; using System.Drawing; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Security; using System.Windows; using System.Windows.Interop; using System.Windows.Media.Imaging; using Microsoft.Win32.SafeHandles; namespace WpfHelpers { public static class BitmapToBitmapSource { public static BitmapSource ToBitmapSource(this Bitmap source) { using (var handle = new SafeHBitmapHandle(source)) { return Imaging.CreateBitmapSourceFromHBitmap(handle.DangerousGetHandle(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } } [DllImport("gdi32")] private static extern int DeleteObject(IntPtr o); private sealed class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid { [SecurityCritical] public SafeHBitmapHandle(Bitmap bitmap) : base(true) { SetHandle(bitmap.GetHbitmap()); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] protected override bool ReleaseHandle() { return DeleteObject(handle) > 0; } } } } 

Eu cheguei a esta pergunta porque eu estava tentando fazer o mesmo, mas no meu caso o Bitmap é de um recurso / arquivo. Eu encontrei a melhor solução é como descrito no seguinte link:

http://sofpt.miximages.com/bitmap/cherries_larger.jpg“,UriKind.RelativeOrAbsolute); bi.EndInit(); // Set the image source. simpleImage.Source = bi;

Você pode apenas compartilhar os pixeldata entre os dois namespaces (Mídia e Desenho) escrevendo um bitmapsource personalizado. A conversão ocorrerá imediatamente e nenhuma memory adicional será alocada. Se você não quiser criar explicitamente uma cópia do seu Bitmap, esse será o método desejado.

 class SharedBitmapSource : BitmapSource, IDisposable { #region Public Properties ///  /// I made it public so u can reuse it and get the best our of both namespaces ///  public Bitmap Bitmap { get; private set; } public override double DpiX { get { return Bitmap.HorizontalResolution; } } public override double DpiY { get { return Bitmap.VerticalResolution; } } public override int PixelHeight { get { return Bitmap.Height; } } public override int PixelWidth { get { return Bitmap.Width; } } public override System.Windows.Media.PixelFormat Format { get { return ConvertPixelFormat(Bitmap.PixelFormat); } } public override BitmapPalette Palette { get { return null; } } #endregion #region Constructor/Destructor public SharedBitmapSource(int width, int height,System.Drawing.Imaging.PixelFormat sourceFormat) :this(new Bitmap(width,height, sourceFormat) ) { } public SharedBitmapSource(Bitmap bitmap) { Bitmap = bitmap; } // Use C# destructor syntax for finalization code. ~SharedBitmapSource() { // Simply call Dispose(false). Dispose(false); } #endregion #region Overrides public override void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset) { BitmapData sourceData = Bitmap.LockBits( new Rectangle(sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height), ImageLockMode.ReadOnly, Bitmap.PixelFormat); var length = sourceData.Stride * sourceData.Height; if (pixels is byte[]) { var bytes = pixels as byte[]; Marshal.Copy(sourceData.Scan0, bytes, 0, length); } Bitmap.UnlockBits(sourceData); } protected override Freezable CreateInstanceCore() { return (Freezable)Activator.CreateInstance(GetType()); } #endregion #region Public Methods public BitmapSource Resize(int newWidth, int newHeight) { Image newImage = new Bitmap(newWidth, newHeight); using (Graphics graphicsHandle = Graphics.FromImage(newImage)) { graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic; graphicsHandle.DrawImage(Bitmap, 0, 0, newWidth, newHeight); } return new SharedBitmapSource(newImage as Bitmap); } public new BitmapSource Clone() { return new SharedBitmapSource(new Bitmap(Bitmap)); } //Implement IDisposable. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion #region Protected/Private Methods private static System.Windows.Media.PixelFormat ConvertPixelFormat(System.Drawing.Imaging.PixelFormat sourceFormat) { switch (sourceFormat) { case System.Drawing.Imaging.PixelFormat.Format24bppRgb: return PixelFormats.Bgr24; case System.Drawing.Imaging.PixelFormat.Format32bppArgb: return PixelFormats.Pbgra32; case System.Drawing.Imaging.PixelFormat.Format32bppRgb: return PixelFormats.Bgr32; } return new System.Windows.Media.PixelFormat(); } private bool _disposed = false; protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // Free other state (managed objects). } // Free your own state (unmanaged objects). // Set large fields to null. _disposed = true; } } #endregion }