# [.net] Surface conversion to bitmap

## Recommended Posts

Montynis    122
Hello, I am trying to convert a surface to a bitmap without using SurfaceLoader.Save() or SurfaceLoader.SaveToStream() methods (because these operations are quite expensive in my application where I have convert surface to a bitmap fifty times during one second). Has anybody some ideas how to do that? Thanks in advance.

##### Share on other sites
Dave Hunt    4872
If you use a memory stream instead of a file stream then the SaveTo/FromStream methods are very fast. I don't know if they're fifty times per second fast, but you could try it out.

##### Share on other sites
Montynis    122
I have tried your suggested method, Dave, and it is working fast enough for me.

Now I have different problem. I want to set BackgroundImage of my form from stream like this:

Surface surface = _device.GetBackBuffer(0, 0, BackBufferType.Mono);

using (Stream stream = SurfaceLoader.SaveToStream(ImageFileFormat.Bmp, surface))
{
Bitmap bmp = new Bitmap(stream);
this.BackgroundImage = bmp;
}

But I get OutOfMemoryException on Application.DoEvents() line. Where is the problem?

##### Share on other sites
alex_myrpg    351
I believe you'll need to dispose of the bitmap each time you set BackgroundImage to something new, to avoid the memory leaks. Try this code:

Surface surface = _device.GetBackBuffer(0, 0, BackBufferType.Mono);

using (Stream stream = SurfaceLoader.SaveToStream(ImageFileFormat.Bmp, surface))
{
if (this.BackgroundImage != null)
this.BackgroundImage.Dispose();

this.BackgroundImage = new Bitmap(stream);
}

Hope it helps.

##### Share on other sites
Montynis    122
I have just tried your code, alex_myrpg, but I got the same exception on this line:

if (Save.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
}

(after changing BackgroundImage of my form, SaveFileDialog is called).

##### Share on other sites
alex_myrpg    351
Ooh, how could I have missed the other object that needs to be disposed?... Try this now please:

using (Surface surface = _device.GetBackBuffer(0, 0, BackBufferType.Mono))
using (Stream stream = SurfaceLoader.SaveToStream(ImageFileFormat.Bmp, surface))
{
if (this.BackgroundImage != null) // I'm not sure if you need these two lines,
this.BackgroundImage.Dispose(); // so try the code with and without them.

this.BackgroundImage = new Bitmap(stream);
}
}

As you can see you ought to have two using statements so that both the Surface and Stream objects are disposed after they've been used.

##### Share on other sites
Montynis    122
Thanks for help, alex_myrpg. I have just tried your code, but I still get the same exception this time in this part of code:

protected override void WndProc(ref Message m)
{

Int32 RightButtonsWidth = (24 * 4);
Int32 RightButtonsHeight = 21;

Rectangle r = new Rectangle(Width - RightButtonsWidth, 0, RightButtonsWidth, RightButtonsHeight);
Boolean UseDefaultBehavior = true;

switch (m.Msg)
{

case WM_NCLBUTTONDOWN:

if (!r.Contains(Control.MousePosition))
{
UseDefaultBehavior = false;
}

if (UseDefaultBehavior)
{
base.WndProc(ref m);
}
break;

case WM_NCLBUTTONDBLCLK:
this.WindowState = FormWindowState.Maximized;
MessageBox.Show("Do not try to resize")
break;

default:
base.WndProc(ref m);
break;
}
}

Exception falls on this line of code:

base.WndProc(ref m);

I don't understand what's wrong :(.

##### Share on other sites
remigius    1172
The error message in this part of the code might still indicate a problem with the Bitmaps. IIRC the WndProc is also responsible for handling repaint messages, so something could very well be going wrong when repainting. I think the following lines of code might be responsible for that, if they are executed from a thread other than the event thread (which is likely):

if (this.BackgroundImage != null) // I'm not sure if you need these two lines, this.BackgroundImage.Dispose(); // so try the code with and without them.

This code will dispose of the background Bitmap's unmanaged resources, so the internal memory pointer used by a paint call on the event thread is invalid. An invalid pointer typically indicates memory for the data couldn't be allocated, so this is why you get the OutOfMemory exception (it's just generally worth stressing that OutOfMemory exceptions often indicate invalid pointers). To prevent this from happening, you should assign the new bitmap before disposing the old one, ensuring the background image data is valid, like this:

Bitmap tmp = this.BackgroundImage;this.BackgroundImage = new Bitmap(stream);// clean up after setting new bitmapif (tmp != null){  tmp.Dispose();}

This should help, but it's no solid guarantee it will work properly all the time. If the event thread happens to decide to repaint the background just as you're setting the new image, you might still experience problems. To make sure this works in all multithreading scenario's, I think you should use a lock{} block (on the same object) around both the background setting code and any paint calls in your WndProc override. I've had a similar problem in a project of mine and it's a big pain, so any comments and/or corrections on this would be much appreciated.

However you might want to consider presenting the backbuffer to the control directly. If this is an option in your scenario, it will save you a ton of headaches. True, you can't overlay Windows Controls on the target control with this approach, but I think finding a compromise here is much more efficient and a lot more robust than the Bitmap approach, so it might be a worthwhile alternative.

Hope this helps :)

##### Share on other sites
alex_myrpg    351
remigius seems to have reasoned it correctly there - unforunately what seems like a relatively simple error has a rather difficult solution, but let's hope that solves it for you now.

##### Share on other sites
Montynis    122
Well, thanks everyone for help. I will try setting BackgroundImage from file, not from a stream. Maybe then I will avoid all these problems.