[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

microsoft.public.dotnet.framework

Graphics.DrawImage is very slow

Alex Clark

8/19/2008 12:13:00 AM

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


12 Answers

Peter Duniho

8/19/2008 3:33:00 AM

0

On Mon, 18 Aug 2008 17:12:57 -0700, Alex Clark <quanta@noemail.noemail>
wrote:

> [...]
> 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.
>
> [...]
> Performance absolutely blows.

All due respect, that's not a very useful description of the problem. :)

> 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.

It sounds to me as though the controls aren't getting painted at all. So
I'm not sure what it means for it to be "incredibly slow".

That said, you mention that your application is "covered with these custom
controls". How many? The fact is, there are practical limits to just how
many controls you can put in a window and still expect reasonable drawing
performance. If you have a large number, performance will suffer.

It's also impossible to evaluate any sort of performance issue with just a
code snippet. You should post a concise-but-complete code sample that
reliably demonstrates the problem.

Also, because this is a performance issue, you should be as specific about
the details as you can. What methods are being called? How long do they
take to execute? How often are they called? How sensitive to the number
of controls being used is the issue? That sort of thing. Finally, it's
important to be clear about what the system configuration is, especially
the video card and CPU.

As a test, you could try using some built-in .NET control instead of your
own in an otherwise-identical program and see how performance compares.
That would at least give some information as to whether the performance
issue is typical, or specific only to your custom control.

Pete

Alex Clark

8/19/2008 8:15:00 PM

0

Hi Peter,

> All due respect, that's not a very useful description of the problem. :)
lol, I know, sorry, I'll try to clear that up a bit below.

> It sounds to me as though the controls aren't getting painted at all. So
> I'm not sure what it means for it to be "incredibly slow".

The control *does* get repainted, but it's laggy. If I drag calculator over
my form, the calculator window sort of "streaks" across it, leaving ghostly
images behind on the controls. Eventually they repaint themselves but it
can take a few seconds. It's as though there's a long delay between the
Invalidate and Paint calls, but I don't have any specific code in there that
would cause that. It's just taking a long time to refresh itself, it seems.

> That said, you mention that your application is "covered with these custom
> controls". How many? The fact is, there are practical limits to just how
> many controls you can put in a window and still expect reasonable drawing
> performance. If you have a large number, performance will suffer.

In all honesty it doesn't matter if it's covered in them or if it's just
one, the problem happens regardless. The most I'd fit on screen would be
roughly 10 - we're talking full width, and about 64px height (sort of like
list view items stacked one atop the other).

> It's also impossible to evaluate any sort of performance issue with just a
> code snippet. You should post a concise-but-complete code sample that
> reliably demonstrates the problem.

I know, sorry, should've posted a snippet but the project is complex. I'll
try to extract the relevant stuff into a sample repro project.

> Also, because this is a performance issue, you should be as specific about
> the details as you can. What methods are being called? How long do they
> take to execute? How often are they called? How sensitive to the number
> of controls being used is the issue? That sort of thing. Finally, it's
> important to be clear about what the system configuration is, especially
> the video card and CPU.

Methods: barely any. Once I've created an appropriate bitmap (that's done
early on) it's pretty much as simple as what I posted in the OP. I override
OnPaint, and call e.Graphics.DrawImage with my "here's one I made earlier"
bitmap. I clip it so that it should only repaint the invalidated area as
well. That is literally all it's doing, but between it getting invalidated
(me dragging an app over the top of it) and it actually refreshing, there
can be as much as a 3s delay (if I'm still rapidly dragging the other app
around the screen). CPU usage is high at the time as well.

Not really sensitive to the number of controls, it's slow with one, it's
slow with 12.

System is moderate spec, XP Pro x64, 2GB DDR800, Pentium D 3.4, PCI Express
128mb Radeon card.

> As a test, you could try using some built-in .NET control instead of your
> own in an otherwise-identical program and see how performance compares.
> That would at least give some information as to whether the performance
> issue is typical, or specific only to your custom control.

No contest, if I use a dozen large Buttons, ComboBoxen, or even ListViews
the repainting is snappy. Ironically I did a test a while back with a .NET
Panel control where I was painting a complex gradient bitmap with bubbles on
the background (just handling the PaintBackground event). With clipping and
some smart caching of the bitmap, performance was beautiful and snappy, even
in Debug mode.

Maybe it's because I'm inheriting from Control rather than UserControl or
Panel?


Peter Duniho

8/19/2008 10:24:00 PM

0

On Tue, 19 Aug 2008 13:15:28 -0700, Alex Clark <quanta@noemail.noemail>
wrote:

> [...]
>> It sounds to me as though the controls aren't getting painted at all.
>> So
>> I'm not sure what it means for it to be "incredibly slow".
>
> The control *does* get repainted, but it's laggy. If I drag calculator
> over
> my form, the calculator window sort of "streaks" across it, leaving
> ghostly
> images behind on the controls. Eventually they repaint themselves but it
> can take a few seconds. It's as though there's a long delay between the
> Invalidate and Paint calls, but I don't have any specific code in there
> that
> would cause that. It's just taking a long time to refresh itself, it
> seems.

I think I know what you mean, but you should try to be more precise. I
don't think you're calling Invalidate() at all (Windows is doing the
invalidation internally as the other window moves over your control), and
no painting is actually done until your OnPaint() method is called.

So, according to your description, it's not that the _painting_ is slow,
but that the OnPaint() method is not even called until much later than you
expect it to.

>> That said, you mention that your application is "covered with these
>> custom
>> controls". How many? The fact is, there are practical limits to just
>> how
>> many controls you can put in a window and still expect reasonable
>> drawing
>> performance. If you have a large number, performance will suffer.
>
> In all honesty it doesn't matter if it's covered in them or if it's just
> one, the problem happens regardless.

Then you definitely have something wrong with your own program.

> [...]
> I know, sorry, should've posted a snippet but the project is complex.
> I'll
> try to extract the relevant stuff into a sample repro project.

If you don't solve it yourself, you should definitely do that.

I'd guess there's at least a 50/50 chance of you finding the problem
yourself as you whittle your existing program down to something more
suitable as a sample.

> [...] I clip it so that it should only repaint the invalidated area as
> well. That is literally all it's doing, but between it getting
> invalidated
> (me dragging an app over the top of it) and it actually refreshing, there
> can be as much as a 3s delay (if I'm still rapidly dragging the other app
> around the screen). CPU usage is high at the time as well.

For what it's worth, using the ClipRectangle is probably pointless. It's
such a slight optimization and complicates the drawing. If that code
should ever have to change in the future, it could wind up broken because
of the explicit use of ClipRectangle (i.e. something could change to make
that rectangle unsuitable as a source or destination rectangle).

> [...]
> Maybe it's because I'm inheriting from Control rather than UserControl or
> Panel?

No, that's not the problem.

I wrote a simple test program that has a custom control (derived from
Control) that just generates a Bitmap for use in an OnPaint() method. I
then ran the program and the Calculator accessory at the same time and had
no trouble at all with performance, even with 20 instances of the control
on my form.

Granted, I'm using 32-bit Windows. I suppose you could be running into
some sort of driver bug related to the 64-bit Windows stuff. You should
try the same code on a different computer, preferably a 32-bit XP system.

Barring that, without any sample code, I have to guess. And my guess is
that you've got something else going on in your program that is somehow
tying up the GUI thread when the UI is invalidated by another window
moving across it. What that could be I have no idea. But for sure, this
is not a general issue that exists in .NET. Assuming it's not a driver
problem, this is fixable. We just need to see a complete code sample that
reproduces the issue.

Pete

Alex Clark

8/21/2008 7:44:00 PM

0

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


Peter Duniho

8/21/2008 7:56:00 PM

0

On Thu, 21 Aug 2008 12:43:39 -0700, Alex Clark <quanta@noemail.noemail>
wrote:

> [...] 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

Alex Clark

8/21/2008 8:05:00 PM

0

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" <NpOeStPeAdM@nnowslpianmk.com> wrote in message
news:op.uf85oah98jd0ej@petes-computer.local...
> On Thu, 21 Aug 2008 12:43:39 -0700, Alex Clark <quanta@noemail.noemail>
> wrote:
>
>> [...] 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


Peter Duniho

8/21/2008 11:32:00 PM

0

On Thu, 21 Aug 2008 13:05:16 -0700, Alex Clark <quanta@noemail.noemail>
wrote:

> 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.

But that wasn't useful. I can write that kind of code myself in five
minutes, had already done so, and had already pointed out that it worked
fine, without any performance issues at all. Posting another "proof of
concept" code snippet doesn't add anything to the discussion.

Unless you are posting a _complete_ code sample that you have tested
yourself and verified that it reproduces the problem you're trying to
illustrate, any code sample you post isn't going to be helpful in
understanding the problem you're actually having.

> 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. [...]

I hope that sufficiently illustrates the importance of posting an _actual_
concise-but-complete code sample that reliably demonstrates the problem.
:)

Pete

Alex Clark

8/22/2008 6:55:00 PM

0

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


Peter Duniho

8/22/2008 8:06:00 PM

0

On Fri, 22 Aug 2008 11:55:16 -0700, Alex Clark <quanta@noemail.noemail>
wrote:

> Hi Peter,
>
> I wasn't trying to insult or "dumb down" with the example,

I'm not saying you were.

> I just wanted to
> create something as concise as possible.

But what you posted didn't reproduce the problem.

It's well and good to keep the example short. But the highest priorities
are "no code missing" and "reproduces the problem".

Without meeting those goals, especially the latter, a code example isn't
likely to help others answer your question.

That's my point.

Pete

v-zhye

8/27/2008 7:14:00 AM

0

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:
msdnmg@microsoft.com.

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

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&am....
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.