Is there a good way to convert between BitmapSource and Bitmap?
Asked Answered
H

6

50

As far as I can tell the only way to convert from BitmapSource to Bitmap is through unsafe code... Like this (from Lesters WPF blog):

myBitmapSource.CopyPixels(bits, stride, 0);

unsafe
{
  fixed (byte* pBits = bits)
  {
      IntPtr ptr = new IntPtr(pBits);

      System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(
        width,
        height,
        stride,
        System.Drawing.Imaging.PixelFormat.Format32bppPArgb,ptr);

      return bitmap;
  }
}

To do the reverse:

System.Windows.Media.Imaging.BitmapSource bitmapSource =
  System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
    bitmap.GetHbitmap(),
    IntPtr.Zero,
    Int32Rect.Empty,
    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

Is there an easier way in the framework? And what is the reason it isn't in there (if it's not)? I would think it's fairly usable.

The reason I need it is because I use AForge to do certain image operations in an WPF app. WPF wants to show BitmapSource/ImageSource but AForge works on Bitmaps.

Hypoxia answered 17/2, 2010 at 20:59 Comment(3)
To do the reverse, you really must delete the bitmap handle you get with GetHbitmap. This bug is all over the internet. It's unfixable. The world is slowly leaking GDI handles; we'll soon be swimming in them!Satanism
romkyns is referring to this: #1546591Ellynellynn
If you do not want to create a copy in memory a sharedbitmapsource is what you want. https://mcmap.net/q/116728/-load-a-wpf-bitmapimage-from-a-system-drawing-bitmapTentative
G
75

It is possible to do without using unsafe code by using Bitmap.LockBits and copy the pixels from the BitmapSource straight to the Bitmap

Bitmap GetBitmap(BitmapSource source) {
  Bitmap bmp = new Bitmap(
    source.PixelWidth,
    source.PixelHeight,
    PixelFormat.Format32bppPArgb);
  BitmapData data = bmp.LockBits(
    new Rectangle(Point.Empty, bmp.Size),
    ImageLockMode.WriteOnly,
    PixelFormat.Format32bppPArgb);
  source.CopyPixels(
    Int32Rect.Empty,
    data.Scan0,
    data.Height * data.Stride,
    data.Stride);
  bmp.UnlockBits(data);
  return bmp;
}
Granado answered 24/5, 2010 at 13:35 Comment(8)
That would work only if the pixel format is known beforehand, its pretty much the way I went with and additional function to map between pixel formats.Hypoxia
Does this work with WPF? The Bitmap seems to come from System.Drawing and used in WinForms. The BitmapSource is used in WPF though.Selfrising
The code above is used to convert a WPF BitmapSource to a Windows Forms Bitmap, the code should "work" in WPF, provided the correct assemblies are referenced, however it will not be very useful since if you already have a BitmapSource you can use it directly in WPF.Granado
:-) Sorry, I didn't read the header very well. I was looking for a way to convert between pixelformats. Found it here: msdn.microsoft.com/en-us/library/aa970785(v=vs.110).aspxSelfrising
If both Windows.Media and System.Drawing.Imaging are referenced in the file, that PixelFormat should be specifically written out as System.Drawing.Imaging.PixelFormat though. Same thing with System.Drawing.Point.Paginal
Why you use Format32bppPArgb for PixelFormat? shall we use it always or it depends on condition?Esmaria
This answer is wrong in 05/2019, the result is a messed up imageArmilla
At minimum there should be code to check the Bitmap format and convert it or you'll lose transparency (among other things). Or at least use PixelFormat.Format32bppArgb which seems to be the most common format for clipboard images from the Web browser. Here's the version I use: gist.github.com/RickStrahl/fe395578bb0f95e63cb65011fc207fc1Flick
S
44

You can just use these two methods:

public static BitmapSource ConvertBitmap(Bitmap source)
{
    return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                  source.GetHbitmap(),
                  IntPtr.Zero,
                  Int32Rect.Empty,
                  BitmapSizeOptions.FromEmptyOptions());
}

public static Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
    Bitmap bitmap;
    using (var outStream = new MemoryStream())
    {
        BitmapEncoder enc = new BmpBitmapEncoder();
        enc.Frames.Add(BitmapFrame.Create(bitmapsource));
        enc.Save(outStream);
        bitmap = new Bitmap(outStream);
    }
    return bitmap;
}

It works perfectly for me.

Shuma answered 21/2, 2014 at 10:31 Comment(5)
I was trying this for ages! Thank you so much! Works perfectly!Wardell
This is a robust solution, but beware that its not as efficient as the bitmap is copied to the memory stream then copied into the new bitmap's memory a second time. For hi-res images this could be a performance issue. The accepted answer addresses this.Nucleotide
MSDN says: "You must keep the stream open for the lifetime of the Bitmap." Workaround is clone bitmap to new and Dispose bitmap created from stream.Chlorpromazine
ConvertBitmap() leaks memory. See this answer for a similar alternative which does not.Mcauley
as for 05/2019 this throws GDI+ error and is not correctArmilla
L
1

Is this what your looking for?

Bitmap bmp = System.Drawing.Image.FromHbitmap(pBits);
Lacielacing answered 17/2, 2010 at 22:23 Comment(1)
I don't think this is right -- you're passing in a pointer to array of bytes where it instead expected a Win32 Bitmap Handle. -- Thanks for pointing out that this function exists though, that's neat.Benbena
J
1

Here a code to set transparent background to any bitmap resource within a Resource Dictionary (not Resources.resx often used in Windows.Forms age). I call this methode before InitializeComponent() - methode. The methodes 'ConvertBitmap(Bitmap source)' and BitmapFromSource(BitmapSource bitmapsource) are mentioned in post from melvas above.

private void SetBitmapResourcesTransparent()
    {
        Image img;
        BitmapSource bmpSource;
        System.Drawing.Bitmap bmp;
        foreach (ResourceDictionary resdict in Application.Current.Resources.MergedDictionaries)
        {
            foreach (DictionaryEntry dictEntry in resdict)
            {
                // search for bitmap resource
                if ((img = dictEntry.Value as Image) is Image 
                    && (bmpSource = img.Source as BitmapSource) is BitmapSource
                    && (bmp = BitmapFromSource(bmpSource)) != null)
                {
                    // make bitmap transparent and assign it back to ressource
                    bmp.MakeTransparent(System.Drawing.Color.Magenta);
                    bmpSource = ConvertBitmap(bmp);
                    img.Source = bmpSource;
                }
            }

        }

    }
Joselow answered 18/11, 2014 at 11:35 Comment(0)
C
1

This is neat and faster than light:

  return Imaging.CreateBitmapSourceFromHBitmap( bitmap.GetHbitmap(), IntPtr.Zero,
      Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions() );
Chiastic answered 10/6, 2015 at 12:12 Comment(0)
T
0

You can share the pixeldata between both namespaces. You don't have to convert.

Use the SharedBitmapSource. https://mcmap.net/q/116728/-load-a-wpf-bitmapimage-from-a-system-drawing-bitmap

Tentative answered 23/6, 2016 at 13:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.