Bitmap drawing advice

  • Thread starter Richard Lewis Haggard
  • Start date
R

Richard Lewis Haggard

I'm having drawing performance issues in an application. What's the best way
to draw a large number of small bitmaps onto the client area?

The application with the drawing issues can be downloaded from
http://www.haggard-and-associates.com/Products/h&a_pocket_spider_solitaire.htm

Here's the situation: the application has 104 cards. Each card has a single
graphic (heart, spade, club, diamond) and a number (1-K).

Here's how it's working now: I'm using the standard double buffering
approach to get around the flashing problems. The erase background event has
been intercepted and does nothing. Each card maintains a copy of its image
so that it was not necessary to construct it each time. At Paint event time,
a bitmap of the clip rectangle size is created. A graphics object is created
to contain the bitmap. Those cards that intersect the clip rectangle are
drawn into the bitmap. Once all of the cards have been drawn, the bitmap is
copied to the device's display surface. At the end of the Paint, the clip
rectangle bitmap and its graphics object are disposed.

The performance is acceptable but there is significant delay. In a non.NET
application, I know that this could be done so that the delay is
imperceptable. Can .NET operate as efficiently or is the .NET overhead so
much greater that this performance hit is just something that we're just
going to have to live with? Or is there a better way to draw graphics?

There are two areas that are potential time sinks. First of all, during the
paint, a new bitmap is created and disposed of with each Paint event. How
much time is eaten up by creation of the bitmap? Would it make a sifnificant
difference to create a memory bitmap at the start of time and just hang onto
it until end of program or would that be a resource hog?
Secondly, this code has a lot small object creations. Is this important? How
many CPU cycles get chewed up with a "new Rectangle(xxxx)" call? Is this
significant?

Here's the code from the main form that receives the Paint event-
private void frmMain_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
Graphics dc;
Bitmap bm = null;

// Create and initialize a drawing surface that is the same size as the
refresh area.

Rectangle rc = e.ClipRectangle;
bm = new Bitmap( rc.Width, rc.Height );
dc = Graphics.FromImage( bm );
dc.FillRectangle( new SolidBrush( Color.Green ), 0, 0, rc.Width,
rc.Height );

// Let all of the various stacks draw their information onto this
surface.
// These routines pass the graphics object that contains the
// clip rectangle bitmap down to those card objects that intersect the
clip rectangle

m_topStacks.Paint( dc, e.ClipRectangle );
m_doneStacks.Paint( dc, e.ClipRectangle );
m_dealStacks.Paint( dc, e.ClipRectangle );
if ( m_bDrag )
m_dragStack.Paint( dc, rc, m_dragStack.Rect, true );

// Transfer the image from the drawing surface to the screen.

e.Graphics.DrawImage( bm, rc, 0, 0, rc.Width, rc.Height,
System.Drawing.GraphicsUnit.Pixel,
new System.Drawing.Imaging.ImageAttributes() );

// Graphics are a special case. The garbage collector doesn't react fast
// enough to handle it so we have to push the resource release up in
priority.

bm.Dispose();
dc.Dispose();
}

Here's the code down at the card leve that is responsible for doing the card
drawing -

public void Paint( Graphics e, Rectangle ClipRectangle )
{
// <Clipped code>
// The code here checks to see if the back bitmap exists.
// If not, creates the back bitmap.
// All cards share the same back bitmap.

// <Clipped code>
// The code clipped from here checks to see if the front card
// bitmap exists. If not, a front bitmap is created.

// m_rcRect is the card's Rectangle member that describe's the card's
screen location.
// Since the card is being drawn into the cliprectangle's bitmap, not
the screen, the coordinates
// must be adjusted to be relative to the clip rectangle.

Rectangle rcDst = new Rectangle( m_rcRect.X-ClipRectangle.X,
m_rcRect.Y-ClipRectangle.Y, m_rcRect.Width, m_rcRect.Height );
if ( m_bUp )
e.DrawImage( m_bmFace, rcDst.Left, rcDst.Top );
else
e.DrawImage( m_bmBack, rcDst.Left, rcDst.Top );
}
 
A

Alex Yakhnin [MVP]

The performance is acceptable but there is significant delay.

At which point do you see this delay?

Could you try to use the OnPaint override instead of the event?
Don't forget to override the OnPaintBackground also with no code in it.
 
A

Alex Feinman [MVP]

Creating strucutres (Rectangle) is cheap. As for bitmap - a siginificantly
better approach is to allocate a screen-sized bitmap for double-buffering
and then use the same source and target rectangle on both bitmaps for blting
 
R

Richard Lewis Haggard

At which point do you see this delay?During the paint. It takes about a second to draw all 104 cards into the
memory bitmap and then bitblt the memory bitmap back to the display surface.
Could you try to use the OnPaint override instead of the event?
No I did not try OnPaint instead of the paint event. Is there a significant
difference?
Don't forget to override the OnPaintBackground also with no code in it.
Did that in order to eliminate the erase/paint flash.
====
Richard Lewis Haggard
 
R

Richard Lewis Haggard

Yes, I came to that conclusion as well. I've changed the paint logic so that
the application creates a screen sized bitmap at app start time. Thereafter,
the cliprectangle area of memory bitmap is refreshed and that area gets
copied to the screen. This was a significant improvement over the 'create a
bitmap of the minimum size upon need and then release it' method but the
results are still drastically slower than what one would get with the
non-.NET environment.

So, the question remains, is the slow performance the result of my bad
coding or is it because of .NET over head? Is there some other technique
that I could use instead?
========
Richard Lewis Haggard
 
A

Alex Feinman [MVP]

Richard,

I've tried the game on an iPaq 3650 (WM2003) and found that it runs
reasonably fast. Are you by any chance testing it on a PXA250 device with
PocketPC 2002? That *would* be slow indeed
 
R

Richard Lewis Haggard

I'm using a 36xx, too. My goal was to have instant response but., obviously,
that didn't happen. In order to get it to work as well as it does I had to
do way too many special things. The one area where I could not get it to an
acceptable speed is in the final game phase. When a game is won, all 104
cards are set in random movement. The moving logic is running as fast as it
can go and a screen refresh takes the better part of a second. Given that a
36xx was the fastest Pocket PC of its time, that means that the contemporary
Cassios, etc, are going to play very sluggishly. The newer PPC's are, I
would expect, much faster.

Anyway, I guess that this is as good as it is going to get. Thanks.
======
Richard Lewis Haggard
 
N

Nick Randolph

Richard

Have you tried inserting some performance monitoring code into the paint
method to see what is causing the time lag. I would imagine that painting
104 images is not particularly quick - is there a way that you can use lines
and text to improve performance?

Nick
 

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