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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
231
0
226 165
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

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

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

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

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

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

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

Спасибо, Лу - смог сделать то, что мне нужно, с помощью одной строчки кода

Kevin 19.09.2008 00:21

Ссылка в ответ мертва "404 Страница не найдена".

Pang 22.10.2020 09:31
Ответ принят как подходящий

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

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

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

Большой! Почему бы вам не выбрать свой собственный ответ в качестве ответа на вопрос? У тебя сейчас намного лучше.

Hallgrim 19.09.2008 01:28

Поскольку ваш ответ уже принят, вы можете отредактировать свой ответ, чтобы сделать его более полным.

Alan Jackson 28.05.2009 09:35

Имейте в виду, что этот код пропускает HBitmap. Смотрите stackoverflow.com/questions/1118496/… для исправления

Lars Truijens 13.07.2009 15:54

Также обратите внимание, что этот код создаст מ InteropBitmap. У меня возникла проблема при использовании XamlWriter, а затем XamlReader - InteropBitmap не поддерживается. Подробнее см. эта ссылка.

AVIDeveloper 15.09.2011 14:11
Предупреждение: Это приводит к утечке дескриптора GDI every single time it's used, so after 10k calls it will stop working (65k if you're lucky). As documented in GetHbitmap, you absolutely должен p/invoke DeleteObject on that handle.
Roman Starkov 28.12.2011 04:24

В качестве последнего параметра я использовал BitmapSizeOptions.FromEmptyOptions(), и он отлично подходит для моей ситуации.

Tarik 22.08.2016 21:09

ваш вопрос касается BitmapImage из System.Drawing.Bitmap. но ваш код возвращает BitmapSource, а не BitmapImage.

naseer mohammad 05.09.2018 17:40

Использование Interop увеличивает риск: проблемы с безопасностью, ошибки и т. д. На мой взгляд, плохой выбор, кроме как в крайнем случае.

david.pfx 07.10.2020 08:34

Как насчет загрузки из 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 ()

Luke Puplett 15.02.2010 16:46

Использование ImageFormat.Bmp на порядок быстрее.

RandomEngy 07.03.2010 23:35

Если у других возникают проблемы с этим кодом: мне пришлось добавить ms.Seek(0, SeekOrigin.Begin); перед установкой bi.StreamSource. Я использую .NET 4.0.

mlsteeves 07.05.2010 17:57

@mls, что было бы верно для любой версии .net. Я собираюсь проникнуть туда и исправить этот код; никто не скажет Павлу.

user1228 27.09.2010 22:00

Важное дополнение к этому коду: добавьте bitmap.CacheOption = BitmapCacheOption.OnLoad; между BeginInit() и EndInit(). В противном случае поток может быть закрыт (особенно если он заключен в using) к тому времени, когда BitmapImage попытается его прочитать. См. Ответ здесь: stackoverflow.com/questions/5346727/…

Pieter Müller 06.01.2012 13:51

Для меня это сначала не сработало, но затем я изменил строку bi.StreamSource = ms; на bi.StreamSource = new MemoryStream(ms.ToArray());, и это изменение было всем, что было нужно!

Cancer 14.06.2012 14:55

Может ли кто-нибудь отредактировать этот ответ, чтобы в него были включены все (правильные) комментарии? На данный момент за него много голосов, но совсем не ясно, правильные ли это ответ или ответ + комментарии ...

Benjol 25.04.2013 11:59

Следует также добавить, что, хотя ImageFormat.Bmp быстрее, он отбрасывает любую прозрачность, которая была в исходном Bitmap.

Pyritie 07.02.2014 18:23

при использовании в качестве расширения, как упоминалось ранее, убедитесь, что Freeze - bitmapImage, прежде чем возвращать его.

Wobbles 03.04.2015 16:46

219 человек проголосовали за сжатие и распаковку PNG? Серьезно?

Glenn Maynard 23.02.2017 03:01

Я знаю, что на это был дан ответ, но вот несколько методов расширения (для .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, см. stackoverflow.com/questions/1546091/…

Jack Ukleja 12.08.2011 06:38

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

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. В противном случае у меня будет черный фон на изображении: stackoverflow.com/questions/4067448/…

Horst Walter 30.11.2012 17:44

Хороший ответ и самое лучшее: нет Interop.

david.pfx 07.10.2020 08:35

@DanielWolf: но Хорст прав: формат BMP не поддерживает прозрачность. Этот ответ следует исправить, чтобы вместо этого использовать PNG.

david.pfx 07.10.2020 13:13

И BmpBitmapEncoder должен быть PngBitmapEncoder, если вам нужна прозрачность. В противном случае вы получите черный цвет.

david.pfx 08.10.2020 03:17

@HorstWalter, david.pfx: Спасибо за ваши комментарии, это имеет смысл. Я не использовал .NET несколько лет, поэтому не могу быстро попробовать эти изменения. Может ли кто-нибудь из вас отредактировать мой код, чтобы убедиться, что он по-прежнему работает?

Daniel Wolf 08.10.2020 12:41

Используйте PngBitmapEncoder вместо BmpBitmapEncoder () в методе ToWinFormsBitmap, чтобы сохранить прозрачность и хорошее качество.

fsbflavio 07.01.2021 20:15

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

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;

// 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 ()?

James Esh 15.05.2015 18:19

См. stackoverflow.com/questions/1546091/…

tofutim 11.08.2015 22:59

Мой взгляд на это основан на ряде ресурсов. https://stackoverflow.com/a/7035036https://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;
            }
        }
    }
}

Вы можете просто поделиться пиксельными данными между обоими пространствами имен (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
}

можешь опубликовать пример?

shady 02.03.2016 19:33

Именно то, что я искал, надеюсь, это сработает, когда я его скомпилирую = D

Greg 23.09.2016 09:45

Итак, если у вас есть Properties.Resources.Image и вы хотите нарисовать его на холсте, потребуется 133 строки кода? WPF не в порядке.

Glenn Maynard 23.02.2017 03:02

Это можно сделать одной строкой. Но если вы хотите сделать это, не делая глубокую копию изображений. Это путь.

Andreas 23.02.2017 14:15

Другие вопросы по теме