Bir WPF BitmapImage'ı bir Sistemden Yükleme.


223

Bir örneğim var System.Drawing.Bitmapve bunu WPF uygulamam için a şeklinde kullanılabilir hale getirmek istiyorum System.Windows.Media.Imaging.BitmapImage.

Bunun için en iyi yaklaşım ne olabilir?

Yanıtlar:


265

MemoryStream'den yüklemeye ne dersiniz?

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
Bu kodu System.Drawing.Bitmap'de ToBitmapImage () gibi bir uzantı yöntemi olarak ekleyebilirsiniz
Luke Puplett

35
ImageFormat.Bmp kullanmak daha hızlı bir büyüklük sırasıdır.
RandomEngy

20
Diğerleri bu kod ile sorun yaşıyorsanız: ms.Seek(0, SeekOrigin.Begin);Ayarlamadan önce eklemek zorunda kaldı bi.StreamSource. .NET 4.0 kullanıyorum.
mlsteeves

6
mls .net'in herhangi bir sürümü için geçerli olacaktır. Oraya gizlice girip kodu çözeceğim; kimse Pawel'e söylemiyor.

7
Birisi, tüm (doğru) yorumların entegre olması için bu cevabı düzenlemeyi düşünür mü? Şu anda ağır bir şekilde iptal edildi, ancak cevap ya da cevap + 'doğru' olan yorumlar mı hiç net değil ...
Benjol

81

Hallgrim sayesinde, işte bitmiş olduğum kod:

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

Ayrıca orijinal sorumda olduğu gibi bir BitmapImage yerine bir BitmapSource'a bağlandım


2
Harika! Neden sorunun cevabı olarak kendi cevabınızı seçmiyorsunuz? Seninki şimdi çok daha iyi.
Hallgrim

1
Sizinki zaten kabul edilen cevap olduğundan, cevabınızı daha eksiksiz hale getirmek için düzenleyebilirsiniz.
Alan Jackson

39
Bu kodun bir HBitmap sızdırdığını unutmayın. Düzeltme için stackoverflow.com/questions/1118496/…
adresine

29
Uyarı : Bu, her kullanıldığında bir GDI tutamacını sızdırıyor , bu nedenle 10 bin çağrıdan sonra çalışmayı durduruyor (şanslıysanız 65 bin). GetHbitmap'te belgelendiği gibi , kesinlikle bu tanıtıcıyı p / çağırmanız gerekirDeleteObject .
Roman Starkov

1
Son parametre için kullandım BitmapSizeOptions.FromEmptyOptions()ve durumum için gayet iyi çalışıyor.
Tarık

53

Bunun cevaplandığını biliyorum, ancak burada dönüştürme yapan birkaç uzantı yöntemi (.NET 3.0+ için) var. :)

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

ve NativeMethods sınıfı (FxCop'u yatıştırmak için)

    /// <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
Yönetilmeyen tutamaçları kullanırken (örn. HBITMAP)
SafeHandles

22

Dönüşümün her iki şekilde de çalışması biraz zaman aldı, bu yüzden geldiğim iki uzantı yöntemi:

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
Bunu kullanıyorum, ancak ImageFormat.Png kullanın. Aksi takdirde görüntüde siyah bir arka plan görüyorum: stackoverflow.com/questions/4067448/…
Horst Walter

10

En kolay şey, WPF bitmap'in doğrudan bir dosyadan yapılabilmesidir.

Aksi takdirde System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap'i kullanmanız gerekir.


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 ()" nedir?
James Esh


6

Özel bir bitmapsource yazarak piksel verilerini her iki ad alanı (Medya ve Çizim) arasında paylaşabilirsiniz. Dönüştürme hemen gerçekleşecek ve ek bellek ayrılmayacak. Bitmap'inizin bir kopyasını açıkça oluşturmak istemiyorsanız, bu istediğiniz yöntemdir.

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
}

bir örnek gönderebilir misin
gölgeli

1
Tam olarak ne arıyordum, umarım bunu derlediğimde çalışır = D
Greg

Yani Properties.Resources.Image varsa ve bir tuval içine çizmek istiyorsanız, 133 satır kod alır? WPF iyi değil.
Glenn Maynard

Tek bir satırda yapmak mümkündür. Ancak bunu, görüntü verilerinin derin bir kopyasını yapmadan yapmak istiyorsanız. Bu yol.
Andreas

5

Bir görüntüleme satıcısında çalışmak ve bir System.Drawing.Bitmap benzer görüntü biçimimize WPF için bir adaptör yazdı.

Bu KB'yi müşterilerimize açıklamak için yazdım:

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

Ve orada bunu yapan bir kod var. AtalaImage'ı Bitmap ile değiştirmeniz ve yaptığımız eşdeğer şeyi yapmanız gerekiyor - oldukça basit olmalı.


Teşekkürler Lou - bir satır kod ile ihtiyacım olanı başardı
Kevin

4

Bunu çeşitli kaynaklardan yaptım. 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

Aynı soruya çalıştığım için bu soruya geldim, ama benim durumumda Bitmap bir kaynak / dosyadan geliyor. En iyi çözümü aşağıdaki bağlantıda açıklandığı gibi buldum:

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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.