C# 从 System.Drawing.Bitmap 加载 WPF BitmapImage
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/94456/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Load a WPF BitmapImage from a System.Drawing.Bitmap
提问by Kevin
I have an instance of a System.Drawing.Bitmap
and would like to make it available to my WPF app in the form of a System.Windows.Media.Imaging.BitmapImage
.
我有一个 a 的实例,System.Drawing.Bitmap
并希望以System.Windows.Media.Imaging.BitmapImage
.
What would be the best approach for this?
什么是最好的方法?
采纳答案by Kevin
Thanks to Hallgrim, here is the code I ended up with:
感谢 Hallgrim,这是我最终得到的代码:
ScreenCapture = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bmp.GetHbitmap(),
IntPtr.Zero,
System.Windows.Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight(width, height));
I also ended up binding to a BitmapSource instead of a BitmapImage as in my original question
我也最终绑定到 BitmapSource 而不是 BitmapImage,就像我原来的问题一样
回答by Hallgrim
The easiest thing is if you can make the WPF bitmap from a file directly.
最简单的事情是,如果您可以直接从文件制作 WPF 位图。
Otherwise you will have to use System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap.
否则,您将不得不使用 System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap。
回答by Lou Franco
I work at an imaging vendor and wrote an adapter for WPF to our image format which is similar to a System.Drawing.Bitmap.
我在一家成像供应商工作,并为我们的图像格式编写了一个 WPF 适配器,类似于 System.Drawing.Bitmap。
I wrote this KB to explain it to our customers:
我写了这个知识库来向我们的客户解释:
http://www.atalasoft.com/kb/article.aspx?id=10156
http://www.atalasoft.com/kb/article.aspx?id=10156
And there is code there that does it. You need to replace AtalaImage with Bitmap and do the equivalent thing that we are doing -- it should be pretty straightforward.
那里有代码可以做到这一点。你需要用 Bitmap 替换 AtalaImage 并做我们正在做的等效的事情——它应该非常简单。
回答by Pawel Lesnikowski
How about loading it from MemoryStream?
从 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();
}
回答by Alastair Pitts
I know this has been answered, but here are a couple of extension methods (for .NET 3.0+) that do the conversion. :)
我知道这已经得到回答,但这里有几个扩展方法(适用于 .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;
}
and the NativeMethods class (to appease FxCop)
和 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);
}
回答by Daniel Wolf
It took me some time to get the conversion working both ways, so here are the two extension methods I came up with:
我花了一些时间让转换双向工作,所以这里是我想出的两种扩展方法:
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;
}
}
}
回答by Roland
I came to this question because I was trying to do the same, but in my case the Bitmap is from a resource/file. I found the best solution is as described in the following link:
我来到这个问题是因为我试图做同样的事情,但在我的情况下,位图来自资源/文件。我发现最佳解决方案如以下链接中所述:
http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.aspx
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;
回答by Tony
// 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;
}
回答by weston
My take on this built from a number of resources. https://stackoverflow.com/a/7035036https://stackoverflow.com/a/1470182/360211
我对此的看法建立在许多资源的基础上。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;
}
}
}
}
回答by Andreas
You can just share the pixeldata between a both namespaces ( Media and Drawing) by writing a custom bitmapsource. The conversion will happen immediately and no additional memory will be allocated. If you do not want to explicitly create a copy of your Bitmap this is the method you want.
您可以通过编写自定义位图源在两个命名空间(媒体和绘图)之间共享像素数据。转换将立即发生,不会分配额外的内存。如果您不想显式创建位图的副本,这就是您想要的方法。
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
}