Drawing

Poor drawing performance not only slows down your program and gives it a sluggish appearance but also is very obvious to the user. The following techniques can help you improve drawing performance.

The first technique is to eliminate unnecessary redrawing. While there are many ways to accomplish this goal, the most important is to understand how Windows does painting and let it do its job. The designers of Windows intentionally gave paint messages a low priority. With the exception of timer messages, paint messages have the lowest priority in Windows. In fact, paint messages are not stored in the application message queue but are synthesized when the application message queue is empty. While painting windows is vital to a program, giving it a low priority allows other tasks, such as computation, to take place first. Since the window has to be painted eventually, it might seem like the trade-off doesn't accomplish much. However, a region in a window can become invalid many times before a task is completed. Because the paint messages are delayed, the window will be painted once instead of multiple times. This makes the results much more efficient and eliminates flashing. You should allow Windows to use this process to optimize painting. You should not call the UpdateWindow function to force an immediate paint unless it's necessary (which isn't unusual). If your program has some windows that are repainted over and over again, unnecessary calls to the UpdateWindow function are probably to blame.

TIP
Let Windows automatically optimize drawing by not calling the UpdateWindow function unless really necessary.

You indicate to Windows that a portion of a window needs to be redrawn with the InvalidateRect and InvalidateRgn functions. With InvalidateRect, you need to specify a pointer to a rectangle containing the area that needs to be updated. You can also pass NULL to update the entire region. To eliminate unnecessary drawing, you should avoid passing NULL unless you really cannot determine the size of the invalid area. When supplying an update rectangle, you should try to invalidate as little as possible. For example, a list view will redraw much more efficiently if several individual rows or columns are invalidated instead of an entire range of rows or columns.

TIP
Optimize drawing by passing the InvalidateRect function an invalid rectangle whenever possible. Try to invalidate the smallest area possible.

Let's look at a good example of how not to handle redrawing. If you have ever used the standard list box control, you know that before you add items to the list you have to call SetRedraw(FALSE) to disable drawing. If you don't, the list will redraw itself every time an item is added. When you have finished adding list items, you then have to call SetRedraw(TRUE) to enable drawing. The problem is that every call to the CListBox::AddString or CListBox::InsertString function invalidates and updates the list box, resulting in a flash. As you now know, this approach is completely wrong. The correct approach is to invalidate the individual rows that change when an item is added instead of calling the UpdateWindow function. This way, Windows will automatically repaint the list window when the message queue is empty or the programmer can force a repaint when needed by calling the UpdateWindow function.

When you invalidate a window with the InvalidateRect or InvalidateRgn function, be careful with the erase background parameter—erase the background only when necessary. Note that this parameter is TRUE by default in MFC. When is it necessary to erase the background? The background needs to be erased when it is both invalid and visible. For example, if you paint the same text in the same location, the background doesn't need to be erased because the background isn't invalid. If you paint different text, you need to erase the background; otherwise, the old text will show through. But painting transparent objects like text is the worst case. If you are drawing opaque objects like bitmaps, you never need to erase behind the bitmap because the background is never visible. If you draw the entire area, you never need to erase the background.

TIP
Avoid erasing the background unless really necessary.

Note that erasing the background is an all-or-nothing deal when the paint actually occurs. If you call the InvalidateRect function 10 times without indicating that the background should be erased and then call it once indicating that it should, the entire background will be erased when the paint message is processed. If you have a large region that doesn't need the background erased and a small region that does, it is more efficient to invalidate the large area, call the UpdateWindow function to force the window to be painted, and then invalidate the small area. A lot of flashing is a symptom of unnecessary background painting.

Another technique is to make sure that your windows are registered with the correct class styles. A window needs the CS_HREDRAW and CS_VREDRAW styles only if changing the size of the client area requires a complete redraw. This is true when something in the window is centered, but most windows do not center anything, so they don't need these class styles. If you are using MFC, note that MFC uses these styles by default, so you need register your own class in the window's constructor to remove these styles. You should consider doing this for all MFC views or other windows that can be resized.

TIP
Use the CS_HREDRAW and CS_VREDRAW styles only when necessary.

If you are displaying an object that takes a significant amount of time to render, you should consider rendering the object into a bitmap and blitting the bitmap to the screen in one step. Using a bitmap improves the perception of speed and eliminates flashing since you never see the background being erased. In fact, you don't need to erase the background when you use this technique, since the foreground and background are both contained in the bitmap. This technique isn't difficult—just create a memory device context and a bitmap, select the bitmap into the memory device context (DC), clear the background, call your normal painting function with the memory DC instead of the window DC, and then blit the resulting bitmap into the window DC. From this point on, you can repaint just by blitting the bitmap until the object or window changes.

TIP
Consider rendering to a bitmap instead of rendering directly to the screen.

Lastly, if your drawing code spends a relatively large amount of time creating and selecting Graphical Device Interface (GDI) objects—profiling will indicate this by showing a lot of time in functions like CreateSolidBrush, CreatePen, and SelectObject—you can improve drawing performance by keeping the GDI resources in memory and selecting the GDI objects only when necessary. If necessary (and it usually isn't), you can use the CS_OWNDC class style to help maintain the selected GDI objects across paint messages.



Developing User Interfaces for Microsoft Windows
Developing User Interfaces for Microsoft Windows
ISBN: 0735605866
EAN: 2147483647
Year: 2005
Pages: 334
Authors: Everett N McKay
BUY ON AMAZON

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net