Graphics.DrawImage is very slow

A

Alex Clark

Hi,

I'm trying to improve the painting performance of a control on my form.

The scenario is this - I have a custom control which inherits from Control,
and I manually paint the entire GUI for it. The GUI is fairly static, so
once I've drawn the appropriate image, I cache that to an Image object and
re-use that.

My code snippet is as follows:

Protected Overrides Sub OnPaint(ByVal e As
System.Windows.Forms.PaintEventArgs)

e.Graphics.DrawImage(_displayImage, e.ClipRectangle, e.ClipRectangle,
GraphicsUnit.Pixel)

End Sub


Where _displayImage is the cached Image object.

Performance absolutely blows. It's fine to initially paint the screen, but
if I (for example) load up an instance of calculator and drag it over my
application, repainting of these controls is incredibly slow. I can
literally saturate the GUI of my app (when it's covered with these custom
controls) with ghostly trails of the calculator window. Once I stop
dragging the calc window, *then* my controls seem to update.

Is there some smarter solution? I've tried using Graphics.DrawImageUnscaled
but it seems to have no effect on performance. My control is set to
DoubleBuffer, UserPaint, and AllPaintingInWMPaint. I'm surprised that this
isn't faster, as all I'm doing is drawing an in-memory bitmap to the screen.
I'm even using clipping so that it should only repaint the affected area.

Why so slow? :-(

Thanks,
Alex
 
A

Alex Clark

Hi Peter,

(VB.NET Code)

This is what I've got in a testbed app. This is the only code I've written
inside Form1.

'---------------
Protected Overrides Sub OnPaintBackground(ByVal e As
System.Windows.Forms.PaintEventArgs)
MyBase.OnPaintBackground(e)

e.Graphics.DrawImageUnscaled(GetBackgroundImage(e), 0, 0)

End Sub

Private Function GetBackgroundImage(ByVal e As PaintEventArgs) As Image

Static _image As Image
If Not IsNothing(_image) Then Return _image

Dim rect As Rectangle
Dim rgn As New Region
Dim rnd As New Random(DateTime.Now.Millisecond)
Dim xc, yc, iDeg As Integer


If Not IsNothing(_image) Then
Return _image
Else
_image = New Bitmap(picDraw.Width, picDraw.Height)
End If


rect = picDraw.ClientRectangle


Using br As New LinearGradientBrush(rect, Color.Red, Color.Blue,
LinearGradientMode.ForwardDiagonal)

Using br2 As New LinearGradientBrush(rect, Color.Green,
Color.Orange, LinearGradientMode.ForwardDiagonal)

Using g As Graphics = Graphics.FromImage(_image)

g.SetClip(e.ClipRectangle, CombineMode.Replace)
g.FillRectangle(br, rect)

For x As Integer = 1 To 2000

xc = rnd.Next(0, picDraw.Width)
yc = rnd.Next(0, picDraw.Height)

g.DrawLine(Pens.White, xc, yc, xc + 10, yc + 10)

rnd = New Random(rnd.Next)
iDeg = rnd.Next(10, 360)
g.FillPie(br2, New Rectangle(xc, yc, 10, 10), 0,
iDeg)

Next

End Using

End Using

End Using

Return _image

End Function
'----------------------

Note that, in the call to GetBackgroundImage, if the image has already been
created it just returns that. This is obviously a simplified scenario
because it doesn't repaint for resize etc, it literally only gets created
one time. Performance is better, but even in Release mode I'm not finding
it to be fantastic. I can drag calculator over the form and have a slightly
laggy update of the image. I suspect in my app it's more of a problem
because I have a variety of controls which are all custom painting, and a
selection of background threads doing monitoring work as well, which is
probably stealing some degree of priority from the GUI thread.

It's probably a moot point anyway, because we're heading for a WPF rewrite
soon and a lot of the tests I've done with that seem to display far better
front end performance. I just wondered if I was making some kind of
schoolboy error in the calls to DrawImage, and if there was a much faster
way to do it.

Thanks,
Alex
 
A

Alex Clark

Hi Peter,

No, it's not the code I'm using, I was just using it to demonstrate a
complex bitmap being created one time and then drawn on each call to
OnPaint.

However, only after I posted the code did I recall I'd been testing painting
through transparent controls (the OnPaint is for the Form, but creating the
bitmap checks the size of a PictureBox control). I was actually overlaying
a transparent PictureBox on the form (dockstyle set to fill). Once I
removed that in my test app, the repainting performance was super snappy -
even in *debug* mode.

So that sorta puts me straight back to the drawing board in terms of my own
app, but at least it eliminates a good few things. I think I'll try to
"turn off" the background threads and see if that boosts foreground painting
performance.



Peter Duniho said:
[...] I just wondered if I was making some kind of
schoolboy error in the calls to DrawImage, and if there was a much faster
way to do it.

I don't see anything in the code you posted that would explain a
performance issue (though I'm not sure why you have the second check for a
null _image value). I do wonder if the code you posted is _really_ the
code you're using, since a static local would be shared across all
instances of the control, which seems like an odd thing to do.

But as far as the performance issue goes, whatever it is, it's in code you
didn't share. I'm sure your code is somehow responsible for the slowdown,
but whether it's a matter of a "schoolboy error" or something that's
simply inherent in the design and not easily fixed, I can't say.

Pete
 
A

Alex Clark

Hi Peter,

I wasn't trying to insult or "dumb down" with the example, I just wanted to
create something as concise as possible. I thought at the time that because
it was a complex bitmap with lots of gradients that maybe *that* was the
cause of the slow drawing performance, i.e. that maybe GDI+ was having
issues painting things more complex than just solid colours.

While trying to deconstruct my app I found what I believe to be the cause.
The container control that these controls are in also has custom foreground
painting, but *that* is what seems to have performance issues. My guess is
that as calc (or any app) is dragged over the top of my form, the container
control gets the WM_INVALIDATE message (it's the first thing to be obscured)
and then my child custom controls get invalidated. Because it happens
synchronously, and my container control is slow to paint, it's holding up
the child controls from processing the message and repainting themselves.

Thanks for your help,
Alex
 
Z

Zhi-Xin Ye [MSFT]

Dear Alex,

How did you paint the container control and the custom control? If you
don't put the custom controls on the container control, do you still have
the performance problem?

I'm looking forward to hearing from you.

Sincerely,
Zhi-Xin Ye
Microsoft Managed Newsgroup Support Team

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://support.microsoft.com/select/default.aspx?target=assistance&ln=en-us.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
A

Alex Clark

Hi Zhi-Xin,

It's a long story but I think I nailed most of the performance issues, it's
repainting much faster and smoother now, so the issue is solved.

Many thanks,
Alex
 
B

bill

Hi, Alex,

Could you share how you solved the problem? I got into the same issue.

thanks,

bill
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top