从System.Drawing.Bitmap中加载WPF BitmapImage

我有一个System.Drawing.Bitmap实例,并希望以System.Windows.Media.Imaging.BitmapImage的形式将其提供给我的WPF应用程序。

最好的方法是什么?

238591 次浏览

最简单的方法是直接从文件中生成WPF位图。

否则你将不得不使用system . windows . interop . image . createbitmapsourcefromhbitmap。

我在一家图像供应商工作,写了一个WPF到我们的图像格式的适配器,它类似于System.Drawing.Bitmap。

我写了这个KB来向我们的客户解释:

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

这里有代码可以做到这一点。你需要用Bitmap替换AtalaImage,并做与我们正在做的相同的事情——这应该是相当简单的。

感谢Hallgrim,以下是我最终得到的代码:

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

我也最终绑定到BitmapSource,而不是在我最初的问题中BitmapImage

如何从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();
}

我知道这个问题已经被回答了,但是这里有两个扩展方法(用于。net 3.0+)可以进行转换。:)

        /// <summary>
/// Converts a <see cref="System.Drawing.Image"/> into a WPF <see cref="BitmapSource"/>.
/// </summary>
/// <param name="source">The source image.</param>
/// <returns>A BitmapSource</returns>
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;
}


/// <summary>
/// Converts a <see cref="System.Drawing.Bitmap"/> into a WPF <see cref="BitmapSource"/>.
/// </summary>
/// <remarks>Uses GDI to do the conversion. Hence the call to the marshalled DeleteObject.
/// </remarks>
/// <param name="source">The source bitmap.</param>
/// <returns>A BitmapSource</returns>
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;
}

和NativeMethods类(安抚FxCop)

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

我花了一些时间让转换双向工作,所以这里是我提出的两个扩展方法:

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;
}
}
}

我来到这个问题,因为我试图做同样的事情,但在我的情况下,位图是来自资源/文件。我发现最好的解决方案如下所示:

http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.aspx

// Create the image element.
Image simpleImage = new Image();
simpleImage.Width = 200;
simpleImage.Margin = new Thickness(5);


// Create source.
BitmapImage bi = new BitmapImage();
// BitmapImage.UriSource must be in a BeginInit/EndInit block.
bi.BeginInit();
bi.UriSource = new Uri(@"/sampleImages/cherries_larger.jpg",UriKind.RelativeOrAbsolute);
bi.EndInit();
// Set the image source.
simpleImage.Source = bi;
// at class level;
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);    // https://stackoverflow.com/a/1546121/194717




/// <summary>
/// Converts a <see cref="System.Drawing.Bitmap"/> into a WPF <see cref="BitmapSource"/>.
/// </summary>
/// <remarks>Uses GDI to do the conversion. Hence the call to the marshalled DeleteObject.
/// </remarks>
/// <param name="source">The source bitmap.</param>
/// <returns>A BitmapSource</returns>
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;
}

我的观点来自于一些资源。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;
}
}
}
}

通过编写自定义位图源,你可以在两个命名空间(Media和Drawing)之间共享像素数据。转换将立即进行,并且不会分配额外的内存。如果你不想显式地创建你的位图的副本,这是你想要的方法。

class SharedBitmapSource : BitmapSource, IDisposable
{
#region Public Properties


/// <summary>
/// I made it public so u can reuse it and get the best our of both namespaces
/// </summary>
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
}