[.net] GDI+ redraw selection box

Started by
2 comments, last by jods 18 years, 10 months ago
I'm building a program with objects draw onto a GDI+ surface that can be selected and moved around. I'm using a double-buffering scheme to avoid flicker and have actually added a third buffer for the selection rectangles around the objects and the selection box that the user catches objects in to select them. The problem is, the program is still slow when the user is dragging around a selection rectangle or when the selection box is being used. Is there a way to avoid having to copy my screen buffers every time the mouse moves? I remember there being a way in GDI that you could create a selection rectangle by simply inverting the pixels for your rectangle and then inverting them back when the mouse moved. Has anyone applied this technique in GDI+? I apologize if I'm being vague on this problem. I'm not sure of the proper terminology for all of this.
Advertisement
You are vague about what and how you draw. GDI+ surely is not fast, but I'd say that unless you are doing really complex painting, it shouldn't be that slow either. Try to find out why your code is slow.

Copying the screen buffers is most probably NOT the bottleneck. Painting them could be. And since you are using double-buffering, no there's no way to prevent the buffer-to-screen copy, that's the whole point about double-buffering!

The method you're looking for to do reversible painting is:
System.Windows.Forms.ControlPaint.DrawReversibleFrame
(a method DrawReversibleLine also exists)

Hope this helps,
jods
Thanks for the reply. I never knew about ControlPaint before. Here is what basically happens in my rendering procedure:

1. Buffer 1 is cleared and all objects are drawn as images with DrawImageUnscaled
2. Buffer 1 is copied to Buffer 2 using DrawImageUnscaled
3. Buffer 2 is filled with rectangles around the selected objects
4. Buffer 2 is copied to the screen buffer with DrawImageUnscaled

Is using DrawImageUnscaled wise here? I've allocated Buffers 1 and 2 by creating Image objects and then creating graphics objects from those.

To increase speed, I've eliminated step 1 in cases where the selection rectangles are the only things changing.
How do you do the double buffering ? It looks like you're doing it yourself.
If you're using .NET 2.0, just use (Control.)DoubleBuffered = true and that will do the trick.
With .NET 1.1, it's a little bit more complicated: (Control.)Setstyle(Controlstyles.DoubleBuffer | Controlstyles.UserPaint | Controlstyles.AllPaintingInWmPaint, true).
That being said, step 4 looks useless to me. .NET will take care of all the buffer allocations, resizing, blitting, etc.

Caching the painting (Step 2) is a good idea if the painting at step 1 is very long and complex and you can avoid doing it often.

Now, I can't believe that just doing 2. and 3. (i.e. updating the selection only) is *that* slow.

A few tips to increase performances:
1. Don't allocate and deallocate a lot of GDI+ objects at each pass. I.e. try not to do lots of:
[source lang=c#]using (Brush b = new SolidBrush(...))using (Pen p = new Pen(...)){   // paint some stuff with b and p}

But rather build your objects once and store them in a private member (even use a static one, if it's possible). Dispose them when disposing your class.

2. Try not to draw all your shapes, but just the ones that are in view.
3. If you really have to, diminish quality (turn off anti-aliasing, etc.)
4. Try not to draw huge surfaces (e.g. 1600x1200). If you really have to make your surface that big, try to paint only the invalidated regions.

If the speed is inacceptable, try to use a profiler to see where all the time is really spent. Try to find out why and ways to make it better.

And the very last solution: GDI+ is not a high-performance rendering library. It's not hardware-accelerated. So if you really need big graphical performances, use OpenGL, DirectX, or even GDI (which hw-optimised, but also has a lot less features).

Good luck,
jods

This topic is closed to new replies.

Advertisement