Загрузите изображение WPF BitmapImage из System.Drawing.Bitmap

У меня есть экземпляр System.Drawing.Bitmap, и я хотел бы сделать его доступным для моего приложения WPF в виде System.Windows.Media.Imaging.BitmapImage.

Какой для этого подход лучше всего?


person Kevin    schedule 18.09.2008    source источник


Ответы (10)


Как насчет загрузки из 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();
}
person Pawel Lesnikowski    schedule 01.07.2009
comment
Вы можете добавить этот код в качестве метода расширения в System.Drawing.Bitmap, например, ToBitmapImage () - person Luke Puplett; 15.02.2010
comment
Использование ImageFormat.Bmp на порядок быстрее. - person RandomEngy; 07.03.2010
comment
На случай, если у других возникнут проблемы с этим кодом: мне пришлось добавить ms.Seek(0, SeekOrigin.Begin); перед установкой bi.StreamSource. Я использую .NET 4.0. - person mlsteeves; 07.05.2010
comment
@mls, что было бы верно для любой версии .net. Я собираюсь проникнуть туда и исправить этот код; никто не скажет Павлу. - person ; 27.09.2010
comment
Важное дополнение к этому коду: добавьте bitmap.CacheOption = BitmapCacheOption.OnLoad; между BeginInit() и EndInit(). В противном случае поток может быть закрыт (особенно если заключен в using) к тому времени, когда BitmapImage попытается его прочитать. См. Ответ здесь: stackoverflow.com/questions/ 5346727 / - person Pieter Müller; 06.01.2012
comment
Для меня это сначала не сработало, но затем я изменил строку bi.StreamSource = ms; на bi.StreamSource = new MemoryStream(ms.ToArray());, и это изменение было всем, что было нужно! - person Umer; 14.06.2012
comment
Может ли кто-нибудь отредактировать этот ответ, чтобы в него были включены все (правильные) комментарии? На данный момент за него много голосов, но совсем не ясно, правильные ли это ответ или ответ + комментарии ... - person Benjol; 25.04.2013
comment
Следует также добавить, что, хотя ImageFormat.Bmp быстрее, он отбрасывает любую прозрачность, которая была у исходного Bitmap. - person Pyritie; 07.02.2014
comment
при использовании в качестве расширения, как упоминалось ранее, не забудьте Freeze bitmapImage перед его возвратом. - person Wobbles; 03.04.2015
comment
219 человек проголосовали за сжатие и распаковку PNG? Шутки в сторону? - person Glenn Maynard; 23.02.2017

Спасибо Халлгриму, вот код, который у меня получился:

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

Я также закончил привязку к BitmapSource вместо BitmapImage, как в моем исходном вопросе

person Kevin    schedule 18.09.2008
comment
Здорово! Почему бы вам не выбрать свой собственный ответ в качестве ответа на вопрос? У тебя сейчас намного лучше. - person Hallgrim; 19.09.2008
comment
Поскольку ваш ответ уже принят, вы можете отредактировать свой ответ, чтобы сделать его более полным. - person Alan Jackson; 28.05.2009
comment
Имейте в виду, что этот код пропускает HBitmap. См. stackoverflow.com/questions/1118496/ для исправления - person Lars Truijens; 13.07.2009
comment
Также обратите внимание, что этот код создаст מ InteropBitmap. У меня возникла проблема при использовании XamlWriter, а затем XamlReader - InteropBitmap не поддерживается. См. эту ссылку для подробнее. - person AVIDeveloper; 15.09.2011
comment
Предупреждение. Это приводит к утечке дескриптора GDI каждый раз, когда он используется, поэтому после 10к звонков он перестанет работать (65к, если повезет). Как указано в GetHbitmap, вы абсолютно < b> должен p / вызывать DeleteObject для этого дескриптора. - person Roman Starkov; 28.12.2011
comment
В качестве последнего параметра я использовал BitmapSizeOptions.FromEmptyOptions(), и он отлично подходит для моей ситуации. - person Tarik; 22.08.2016
comment
ваш вопрос касается BitmapImage из System.Drawing.Bitmap. но ваш код возвращает BitmapSource, а не BitmapImage. - person naseer mohammad; 05.09.2018
comment
Использование Interop увеличивает риск: проблемы с безопасностью, ошибки и т. Д. На мой взгляд, плохой выбор, кроме как в крайнем случае. - person david.pfx; 07.10.2020

Я знаю, что на это был дан ответ, но вот несколько методов расширения (для .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);
}
person Alastair Pitts    schedule 24.09.2009
comment
При использовании неуправляемых дескрипторов (например, HBITMAP) рассмотрите возможность использования SafeHandles, см. stackoverflow.com/questions/1546091/ - person Jack Ukleja; 12.08.2011

Мне потребовалось некоторое время, чтобы преобразование работало в обоих направлениях, поэтому вот два метода расширения, которые я придумал:

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;
        }
    }
}
person Daniel Wolf    schedule 21.07.2011
comment
Я использую это, но использую ImageFormat.Png. В противном случае я получаю черный фон на изображении: stackoverflow.com/questions/4067448/ - person Horst Walter; 30.11.2012
comment
Хороший ответ и самое лучшее: нет Interop. - person david.pfx; 07.10.2020
comment
@DanielWolf: но Хорст прав: формат BMP не поддерживает прозрачность. Этот ответ следует исправить, чтобы вместо этого использовать PNG. - person david.pfx; 07.10.2020
comment
И BmpBitmapEncoder должно быть PngBitmapEncoder, если вам нужна прозрачность. Иначе получится черный. - person david.pfx; 08.10.2020
comment
@HorstWalter, david.pfx: Спасибо за ваши комментарии, это имеет смысл. Я не использовал .NET несколько лет, поэтому не могу быстро попробовать эти изменения. Может ли кто-нибудь из вас отредактировать мой код, чтобы убедиться, что он по-прежнему работает? - person Daniel Wolf; 08.10.2020
comment
Используйте PngBitmapEncoder вместо BmpBitmapEncoder () в методе ToWinFormsBitmap, чтобы сохранить прозрачность и хорошее качество. - person fsbflavio; 07.01.2021

Самый простой вариант - создать растровое изображение WPF напрямую из файла.

В противном случае вам придется использовать System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap.

person Hallgrim    schedule 18.09.2008

Вы можете просто поделиться пиксельными данными между обоими пространствами имен (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
}
person Andreas    schedule 29.09.2015
comment
можешь опубликовать пример? - person shady; 02.03.2016
comment
Именно то, что я искал, надеюсь, это сработает, когда я его скомпилирую = D - person Greg; 23.09.2016
comment
Итак, если у вас есть Properties.Resources.Image и вы хотите нарисовать его на холсте, потребуется 133 строки кода? WPF не в порядке. - person Glenn Maynard; 23.02.2017
comment
Это можно сделать одной строкой. Но если вы хотите сделать это, не делая глубокую копию изображений. Это путь. - person Andreas; 23.02.2017

Я работаю у поставщика изображений и написал адаптер для WPF для нашего формата изображения, который похож на System.Drawing.Bitmap.

Я написал это KB, чтобы объяснить это нашим клиентам:

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

И там есть код, который это делает. Вам нужно заменить AtalaImage на Bitmap и сделать то же самое, что и мы, - это должно быть довольно просто.

person Lou Franco    schedule 18.09.2008
comment
Спасибо, Лу - смог сделать то, что мне нужно, с помощью одной строчки кода - person Kevin; 19.09.2008
comment
Ссылка в ответе не работает 404: страница не найдена. - person Pang; 22.10.2020
comment
Если кто-то по какой-то причине все еще ищет этот конкретный ответ, он доступен на archive.org: https://web.archive.org/web/20160622213213/http://www.atalasoft.com/KB/Article.aspx?Id=10156 - person JaykeBird; 06.05.2021

Мой взгляд на это основан на ряде ресурсов. 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;
            }
        }
    }
}
person weston    schedule 28.04.2015

Я пришел к этому вопросу, потому что пытался сделать то же самое, но в моем случае растровое изображение взято из ресурса / файла. Я нашел лучшее решение, как описано по следующей ссылке:

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;
person Roland    schedule 11.09.2011

person    schedule
comment
Что такое DeleteObject ()? - person James Esh; 15.05.2015
comment
См. stackoverflow.com/questions/1546091/ - person tofutim; 11.08.2015