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.

JohannesH
  • 6,430
  • 5
  • 37
  • 71
  • 10
    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! – Roman Starkov Dec 31 '11 at 04:25
  • 2
    romkyns is referring to this: http://stackoverflow.com/questions/1546091/wpf-createbitmapsourcefromhbitmap-memory-leak – Paul Suart Apr 26 '12 at 21:24
  • If you do not want to create a copy in memory a sharedbitmapsource is what you want. stackoverflow.com/a/32841840/690656 – Andreas Oct 28 '15 at 10:16

6 Answers6

74

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;
}
josef.axa
  • 1,163
  • 12
  • 15
  • 5
    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. – JohannesH May 26 '10 at 20:43
  • Does this work with WPF? The Bitmap seems to come from System.Drawing and used in WinForms. The BitmapSource is used in WPF though. – Jonas May 09 '14 at 09:41
  • 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. – josef.axa May 09 '14 at 09:49
  • :-) Sorry, I didn't read the header very well. I was looking for a way to convert between pixelformats. Found it here: http://msdn.microsoft.com/en-us/library/aa970785(v=vs.110).aspx – Jonas May 09 '14 at 10:32
  • 2
    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`. – Nyerguds Aug 21 '16 at 14:34
  • Why you use `Format32bppPArgb` for `PixelFormat`? shall we use it always or it depends on condition? – S.Serpooshan Nov 12 '17 at 08:23
  • This answer is wrong in 05/2019, the result is a messed up image – xzesstence May 24 '19 at 10:57
  • 1
    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: https://gist.github.com/RickStrahl/fe395578bb0f95e63cb65011fc207fc1 – Rick Strahl Sep 09 '20 at 22:24
43

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.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
melvas
  • 2,346
  • 25
  • 29
  • I was trying this for ages! Thank you so much! Works perfectly! – Noel Widmer Sep 12 '14 at 07:10
  • 4
    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. – snort Feb 20 '15 at 17:37
  • 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. – Arci Mar 04 '15 at 15:19
  • 1
    `ConvertBitmap()` leaks memory. See [this answer](http://stackoverflow.com/a/1118557/365102) for a similar alternative which does not. – Mateen Ulhaq Apr 15 '16 at 19:00
  • as for 05/2019 this throws GDI+ error and is not correct – xzesstence May 24 '19 at 10:56
1

Is this what your looking for?

Bitmap bmp = System.Drawing.Image.FromHbitmap(pBits);
caesay
  • 16,932
  • 15
  • 95
  • 160
  • 2
    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. – BrainSlugs83 Nov 05 '13 at 06:01
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;
                }
            }

        }

    }
1

This is neat and faster than light:

  return Imaging.CreateBitmapSourceFromHBitmap( bitmap.GetHbitmap(), IntPtr.Zero,
      Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions() );
Mauro Sampietro
  • 2,739
  • 1
  • 24
  • 50
0

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

Use the SharedBitmapSource. https://stackoverflow.com/a/32841840/690656

Community
  • 1
  • 1
Andreas
  • 3,843
  • 3
  • 40
  • 53