views:

45

answers:

2

I have a function that returns a bitmap meant to be used as background in a panel, and occasionally I'd have to call it to create a new background based on parameters.

(Since there are two drawing functions for this panel (the background doesn't need to be changed as often as the foreground) it's not a matter of just drawing on the Paint event.)

So my question is: Is there a (more than symbolic) performance gain if I get the old background buffer as a paramenter and draw on it instead of creating a new bitmap every time the function is called?

+2  A: 

Yes you gain quite a bit I would imagine.

For one, your memory constraints would be better bounded. If you're constantly creating Bitmaps, what's preventing your the client code from holding onto them and running you out of memory?

Allocation is usually one of the most expensive things in any large system. Reuse is definitely a good thing to have for objects that are expensive to create. You'll see less hiccups from Garbage Collection as well.

EDIT you can also consider maintaining your own pool of Bitmaps and not requiring the caller to pass in an existing one. Make sure you document that you own the Bitmaps and that the caller should treat them as read-only (can you wrap it in some immutable object?). That way you can create/dispose on your own time and not need anything from the client.

SB
Oh, actually I know I have to dispose bitmaps - my doubt is about the new-dispose-new-dispose[...] cycle. But even so the allocation hurts performance, correct?
Camilo Martin
@Camilo, One thing to watch for when repeatedly allocating large objects is that you can fragment the Large Object Heap, potentially leading to OutOfMemoryException being thrown, even though there is still memory available. I'm not sure if Bitmap has this problem (it might be wrapping an unmanaged memory buffer), but certainly you can have this problem if you frequently work with large byte[] objects.
Dan Bryant
Bitmap does wrap unmanaged memory if I recall.
SB
Dan Bryant's consideration about memory fragmentation is really relevant! Question solved I guess, I'll keep in mind this for bitmaps and other streams.
Camilo Martin
SB is right, bitmap pixels are stored in unmanaged memory, not the LOH.
Hans Passant
Now I've read your edit, what does mean to wrap in an immutable object? if it's just to avoid writing to it, I think there's no problem (I can just document and remember it).
Camilo Martin
Yea it essentially means not returning a modifiable object that gives them what they need. If you don't intend to expose the code to third party clients, then you're fine. Some clients may not use it per your design, so it's always good to prevent them when possible.
SB
+2  A: 

Yes, recreating a bitmap in code that runs while painting is usually far too expensive and slows down the painting too much. Keeping a copy of the bitmap solves the speed problem, at the expensive of needing more memory.

Note that the standard Control.BackgroundImage property is available for this, consider using it. You just need to add the code that updates that property (and calls Invalidate) when the conditions change that require a different background image. Drawing is automatic.

Secondary efficiency considerations are pre-scaling the bitmap to exactly fit the control's ClientSize, avoids having to rescale the bitmap at painting time. Big savings there, especially when the bitmap is large. But requires overriding the OnResize method so you can re-generate the scaled bitmap. When that slows down the painting too much while resizing the form then you need to wire the form's ResizeEnd event.

And creating the bitmap in the Format32bppPArgb pixel format, it draws about 10 times faster on most video adapters compared to any other format.

Hans Passant
I don't think `BackgroundImage` is meant for this kind of use (since there is no need for the foreground-to-background alpha), the `bitmap` is always made with the right size (drawn with `DrawImageUnscaled` too) and yes, I'm using the `Format32bppArgb`, it really speeds up tremendously. Plus you don't need to override `OnResize`, there's a control style flag that raises `Paint` at every resize. But thanks for the tips, because all of these took me time to learn! Somebody else will surely find them useful, +1 ;)
Camilo Martin
OnResize is necessary to make the bitmap bigger. So it is always drawn with the right size, regardless of the client size. If you draw it with the overload that takes the destination rectangle then you're resizing it, slowing down the paint.
Hans Passant
Paint calls RenderBackground and RenderForeground, they can get the control's size. This seems cleaner to me...
Camilo Martin