从 System.Drawing.Bitmap 加载 WPF BitmapImage
Posted
技术标签:
【中文标题】从 System.Drawing.Bitmap 加载 WPF BitmapImage【英文标题】:Load a WPF BitmapImage from a System.Drawing.Bitmap 【发布时间】:2010-09-10 19:15:33 【问题描述】:我有一个 System.Drawing.Bitmap
的实例,并希望以 System.Windows.Media.Imaging.BitmapImage
的形式将其提供给我的 WPF 应用程序。
最好的方法是什么?
【问题讨论】:
【参考方案1】:从 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();
【讨论】:
您可以将此代码添加为 System.Drawing.Bitmap 的扩展方法,类似于 ToBitmapImage() 使用 ImageFormat.Bmp 快一个数量级。 如果其他人对此代码有问题:我必须在设置bi.StreamSource
之前添加 ms.Seek(0, SeekOrigin.Begin);
。我正在使用 .NET 4.0。
@mls 对任何版本的 .net 都是如此。我要潜入那里修复代码;没有人告诉帕维尔。
有人会考虑编辑这个答案,以便将所有(正确的)cmets 集成到其中吗?目前它的投票率很高,但根本不清楚答案或答案+cmets是“正确的”......【参考方案2】:
感谢 Hallgrim,这是我最终得到的代码:
ScreenCapture = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bmp.GetHbitmap(),
IntPtr.Zero,
System.Windows.Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight(width, height));
我也最终绑定到了 BitmapSource 而不是 BitmapImage ,就像我原来的问题一样
【讨论】:
太棒了!为什么不选择自己的答案作为问题的答案?你的现在好多了。 由于您的答案已经被接受,您可以编辑您的答案以使其更完整。 请注意此代码会泄漏 HBitmap。请参阅***.com/questions/1118496/… 进行修复 警告:This leaks a GDI handle 每次使用它,所以在 10k 调用后它会停止工作(如果你幸运的话,它会停止工作 65k)。如 GetHbitmap 中所述,您绝对必须在该句柄上 p/invokeDeleteObject
。
对于最后一个参数,我使用了BitmapSizeOptions.FromEmptyOptions()
,它适用于我的情况。【参考方案3】:
我知道这已得到解答,但这里有几个扩展方法(用于 .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);
【讨论】:
使用非托管句柄(例如 HBITMAP)时,请考虑使用 SafeHandles,请参阅 ***.com/questions/1546091/…【参考方案4】:我花了一些时间才让转换双向工作,所以这是我想出的两种扩展方法:
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;
【讨论】:
我正在使用这个,但是使用 ImageFormat.Png。否则我会在图像上看到黑色背景:***.com/questions/4067448/… 不错的答案和最好的事情:没有互操作。 @DanielWolf:但霍斯特是对的:BMP 格式不支持透明度。应更正此答案以改用 PNG。 如果你想要透明度,BmpBitmapEncoder
应该是 PngBitmapEncoder
。否则你会变黑。
@HorstWalter, david.pfx:感谢您的 cmets,这是有道理的。我已经有几年没有使用 .NET,所以我无法快速尝试这些更改。你们谁能编辑我的代码,确保它仍然有效?【参考方案5】:
最简单的事情是如果您可以直接从文件制作 WPF 位图。
否则您将不得不使用 System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap。
【讨论】:
【参考方案6】:// at class level;
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject); // https://***.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;
【讨论】:
什么是“DeleteObject()”? 见***.com/questions/1546091/…【参考方案7】:您可以通过编写自定义位图源在两个命名空间(媒体和绘图)之间共享像素数据。转换将立即发生,不会分配额外的内存。如果您不想显式创建位图的副本,这就是您想要的方法。
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
【讨论】:
你能举个例子吗? 正是我要找的东西,我希望在我编译它时这能工作=D 那么如果你有Properties.Resources.Image,你想把它画到画布上,需要133行代码? WPF 不行。 一行可以搞定。但是,如果您想在不制作图像数据的深层副本的情况下执行此操作。这是要走的路。【参考方案8】:我在一家图像供应商工作,并为我们的图像格式编写了一个 WPF 适配器,类似于 System.Drawing.Bitmap。
我写这个知识库是为了向我们的客户解释它:
http://www.atalasoft.com/kb/article.aspx?id=10156
那里有代码可以做到这一点。您需要将 AtalaImage 替换为 Bitmap 并执行我们正在执行的等效操作 - 它应该非常简单。
【讨论】:
感谢 Lou - 能够用一行代码完成我需要的工作 答案中的链接已失效 "404: Page Not Found". 如果有人出于某种原因仍在寻找这个特定的答案,可以在 archive.org 上找到它:web.archive.org/web/20160622213213/http://www.atalasoft.com/KB/…【参考方案9】:我对此的看法基于大量资源。 https://***.com/a/7035036https://***.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;
【讨论】:
【参考方案10】:我来这个问题是因为我试图做同样的事情,但在我的情况下,位图来自资源/文件。我发现最好的解决方案如以下链接所述:
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;
【讨论】:
以上是关于从 System.Drawing.Bitmap 加载 WPF BitmapImage的主要内容,如果未能解决你的问题,请参考以下文章
是否可以从 System.Drawing.Bitmap 创建 Avalonia.Media.Imaging.Bitmap?
从 Android.Graphics.Bitmap.GetPixels 获得的 RGB 值与 System.Drawing.Bitmap.GetPixel 略有不同
将 HBITMAP 句柄从非托管代码传递到托管代码以创建 System.Drawing.Bitmap 的安全性