Tải BitmapImage WPF từ System.Drawing.Bitmap


223

Tôi có một ví dụ về a System.Drawing.Bitmapvà muốn cung cấp nó cho ứng dụng WPF của mình dưới dạng a System.Windows.Media.Imaging.BitmapImage.

Điều gì sẽ là cách tiếp cận tốt nhất cho việc này?

Câu trả lời:


265

Làm thế nào về việc tải nó từ 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();
}

11
Bạn có thể thêm mã này làm phương thức mở rộng trên System.Drawing.Bitmap, giống như ToBitmapImage ()
Luke Puplett

35
Sử dụng ImageFormat.Bmp là một thứ tự cường độ nhanh hơn.
RandomEngy

20
Trong trường hợp những người khác gặp vấn đề với mã này: tôi phải thêm ms.Seek(0, SeekOrigin.Begin);trước khi cài đặt bi.StreamSource. Tôi đang sử dụng .NET 4.0.
mlsteeves

6
@mls sẽ đúng với bất kỳ phiên bản nào của .net. Tôi sẽ lẻn vào đó và sửa mã đó; không ai nói với Pawel.

7
Ai đó sẽ xem xét chỉnh sửa câu trả lời này để tất cả các ý kiến ​​(chính xác) được tích hợp vào nó? Tại thời điểm này, nó được nâng cấp mạnh mẽ, nhưng không rõ liệu đó là câu trả lời hay câu trả lời + nhận xét là 'đúng' ...
Stewol

81

Cảm ơn Hallgrim, đây là mã tôi đã kết thúc:

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

Tôi cũng đã kết thúc ràng buộc với BitmapSource thay vì BitmapImage như trong câu hỏi ban đầu của tôi


2
Tuyệt quá! Tại sao bạn không chọn câu trả lời của riêng mình làm câu trả lời cho câu hỏi? Bây giờ của bạn tốt hơn nhiều.
Hallgrim

1
Vì câu trả lời của bạn đã được chấp nhận, bạn có thể chỉnh sửa câu trả lời của mình để hoàn chỉnh hơn.
Alan Jackson

39
Hãy nhớ rằng mã này rò rỉ một HBitmap. Xem stackoverflow.com/questions/1118496/
Khắc

28
Cảnh báo : Điều này làm rò rỉ một tay cầm GDI mỗi lần nó được sử dụng, vì vậy sau 10k cuộc gọi, nó sẽ ngừng hoạt động (65k nếu bạn may mắn). Như được ghi lại trong GetHbitmap , bạn hoàn toàn phải p / gọi DeleteObjecttrên xử lý đó.
Roman Starkov

1
Đối với tham số cuối cùng, tôi đã sử dụng BitmapSizeOptions.FromEmptyOptions()và nó hoạt động tốt với tình huống của tôi.
Tarik

53

Tôi biết điều này đã được trả lời, nhưng đây là một vài phương thức mở rộng (cho .NET 3.0+) thực hiện chuyển đổi. :)

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

và lớp NativeMethods (để xoa dịu 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);
}

1
Khi sử dụng tay cầm không được quản lý (ví dụ HBITMAP), hãy cân nhắc sử dụng SafeHandles, xem stackoverflow.com/questions/1546091/
mẹo

22

Tôi phải mất một thời gian để chuyển đổi hoạt động theo cả hai cách, vì vậy đây là hai phương pháp mở rộng mà tôi đã nghĩ ra:

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

2
Tôi đang sử dụng cái này, nhưng sử dụng ImageFormat.Png. Nếu không, tôi nhận được một nền đen trên hình ảnh: stackoverflow.com/questions
4067448 / Khăn

10

Điều đơn giản nhất là nếu bạn có thể tạo bitmap WPF từ một tệp trực tiếp.

Nếu không, bạn sẽ phải sử dụng System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap.


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

"DeleteObject ()" là gì?
James Esh


6

Bạn chỉ có thể chia sẻ pixeldata giữa cả hai không gian tên (Phương tiện và Bản vẽ) bằng cách viết một bitmap nguồn tùy chỉnh. Việc chuyển đổi sẽ diễn ra ngay lập tức và không có bộ nhớ bổ sung sẽ được phân bổ. Nếu bạn không muốn rõ ràng tạo một bản sao Bitmap của mình thì đây là phương pháp bạn muốn.

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
}

bạn có thể gửi một ví dụ?
râm mát

1
Chính xác những gì tôi đang tìm kiếm, tôi hy vọng nó hoạt động khi tôi biên dịch nó = D
Greg

Vì vậy, nếu bạn có Properties.Resource.Image và bạn muốn vẽ nó vào một khung vẽ, phải mất 133 dòng mã? WPF không ổn.
Glenn Maynard

Có thể làm điều đó trong một dòng. Nhưng nếu bạn muốn làm điều đó mà không tạo ra một bản sao sâu của hình ảnh. Đây là con đường để đi.
Andreas

5

Tôi làm việc tại một nhà cung cấp hình ảnh và đã viết một bộ chuyển đổi cho WPF sang định dạng hình ảnh của chúng tôi tương tự như System.Drawing.Bitmap.

Tôi đã viết KB này để giải thích cho khách hàng của chúng tôi:

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

Và có mã ở đó làm điều đó. Bạn cần thay thế AtalaImage bằng Bitmap và thực hiện điều tương tự mà chúng tôi đang làm - việc này khá đơn giản.


Cảm ơn Lou - đã có thể làm những gì tôi cần với một dòng mã
Kevin

4

Tôi đảm nhận việc này được xây dựng từ một số tài nguyên. 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;
            }
        }
    }
}

2

Tôi đã đến câu hỏi này bởi vì tôi đang cố gắng làm điều tương tự, nhưng trong trường hợp của tôi, Bitmap là từ một tài nguyên / tệp. Tôi tìm thấy giải pháp tốt nhất là như được mô tả trong liên kết sau:

http://msdn.microsoft.com/en-us/l 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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.