从System.Drawing.Bitmap加载WPF BitmapImage

Kev*_*vin 217 c# wpf bitmap

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

对此最好的方法是什么?

Paw*_*ski 260

如何从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();
}
Run Code Online (Sandbox Code Playgroud)

  • 使用ImageFormat.Bmp要快一个数量级. (35认同)
  • 如果其他人遇到此代码的问题:我必须在设置`bi.StreamSource`之前添加`ms.Seek(0,SeekOrigin.Begin);`.我正在使用.NET 4.0. (20认同)
  • 您可以在System.Drawing.Bitmap上添加此代码作为扩展方法,类似于ToBitmapImage() (10认同)
  • 有人会考虑编辑这个答案,以便将所有(正确的)评论整合到其中吗?目前它被大量投票,但根本不清楚是答案或答案+评论是否正确...... (7认同)
  • @mls对任何版本的.net都适用.我要潜入那里并修复那段代码; 没有人告诉Pawel. (6认同)
  • 还应该补充一点,当ImageFormat.Bmp更快时,它会抛弃原始Bitmap具有的任何透明度 (6认同)
  • @Will,不,不需要关闭它.我使用"使用"语句.:p (4认同)
  • 219个人赞成压缩和解压缩PNG?认真吗 (2认同)

Kev*_*vin 81

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

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

我也最终绑定到BitmapSource而不是像我原来的问题中的BitmapImage

  • 请注意,此代码会泄漏HBitmap.有关修复,请参见http://stackoverflow.com/questions/1118496/using-image-control-in-wpf-to-display-system-drawing-bitmap/1118557#1118557 (39认同)
  • **警告**:[这会泄漏GDI句柄](http://stackoverflow.com/questions/1546091/wpf-createbitmapsourcefromhbitmap-memory-leak)每次使用它,所以在10k调用后它将停止工作(65k如果你很幸运的话).如[GetHbitmap](http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.gethbitmap.aspx)中所述,您绝对必须**p /在该句柄上调用`DeleteObject` . (27认同)
  • 大!为什么不选择自己的答案作为问题的答案?你现在好多了. (2认同)

Ala*_*tts 53

我知道这已经得到了解答,但这里有一些扩展方法(适用于.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;
    }
Run Code Online (Sandbox Code Playgroud)

和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);
}
Run Code Online (Sandbox Code Playgroud)

  • 使用非托管句柄(例如 HBITMAP)时,请考虑使用 SafeHandles,请参阅 http://stackoverflow.com/questions/1546091/wpf-createbitmapsourcefromhbitmap-memory-leak/7035036#7035036 (2认同)

Dan*_*olf 22

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

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我正在使用这个,但使用 ImageFormat.Png。否则我会在图像上得到黑色背景:http://stackoverflow.com/questions/4067448/converting-image-to-bitmap-turns-background-black (3认同)

Hal*_*rim 10

最简单的方法是如果你可以直接从文件中制作WPF位图.

否则,您将不得不使用System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap.


Ton*_*ony 9

// 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;
}
Run Code Online (Sandbox Code Playgroud)


Lou*_*nco 5

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

我写了这篇KB来向客户解释:

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

那里有代码可以做到.你需要用Bitmap替换AtalaImage并做我们正在做的事情 - 它应该非常简单.

  • 如果由于某种原因仍在寻找这个特定的答案,可以在 archive.org 上找到它:https://web.archive.org/web/20160622213213/http://www.atalasoft.com/KB/Article.aspx ?id=10156 (2认同)

And*_*eas 5

您只需编写自定义位图源,即可在两个名称空间(媒体和图形)之间共享pixeldata。转换将立即发生,并且不会分配额外的内存。如果您不想显式创建位图的副本,则可以使用这种方法。

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
}
Run Code Online (Sandbox Code Playgroud)