Reputation:
I am writing an MFC Windows App to plot the motion of a wheeled robot.
In CChildView::OnPaint()
I do a pdc->FillRect(ViewRect, &CBrush( RGB(128,128,128) ) );
Then I use pdc->MoveTo
and pdc->LineTo
to draw all the data points etc. refreshed every 250ms.
Problem is as the robot progresses on its travels the number of data points increases to a level where the OnPaint()
function refresh time becomes noticeable.
In a previous DOS app I did years ago I got around this sluggish refresh rate by removing the fill background paint and replaced it with a plot all the data points in the background colour, then painting them all again in there foreground item colour. Thus reducing the number of changed pixels.
Would this method be valid for what I am doing in MFC OnPaint()
or is there a better solution please?
Upvotes: 0
Views: 131
Reputation: 3401
Painting in a DOS applications is simple, because you have full control of the process and you don't get paint requests from the system - normally you have to introduce delays if you want to achieve a "motion" effect. In Windows though painting is very different, because you may (or may not) receive paint requests from the system, if the user minimizes/maximizes/restores, moves, unhides etc or programmatically invalidates a window. This causes an "invalid" area on the window surface. If the window contains an invalid area it will receive a WM_PAINT
message. It is a low-priority message, posted just before the application is about to enter the idle state, also it is in some way "asynchronous", posted when the system decides so.
First you need to fully understand the invalidating/painting mechanism in Windows. The documentation you must read is: Painting and Drawing. I have posted quite a bit about this, please check my answer here (and the links in the post).
As for your question, I would suggest using a combination of painting and drawing. Many suggest not painting outside the WM_PAINT
message (OnPaint()
/OnDraw()
in MFC), but this is not suitable for all cases, as the message is sent asynchronously, and it's also very hard to implement a really optimized operation, painting only the affected area; you would actually have to paint the invalidated area as well. It is best used to paint the whole client area of the window. You could definitely use it in your app (and it's logically "correct"), but as you have found it's really "wasteful", and has a sluggish and choppy feel. So my proposal is:
OnDraw()
, rather than OnPaint()
for a CView/CScrollView
-derived class. It should paint the background, if any, all additoinal objects (labels, axes etc) and finally all data points.GetDC()
/ReleaseDC()
) to paint only the section that changes, ie a single data point at a time. You will have to create a new worker thread (so as to not block the main/UI thread). It must do the calculations, add the new data point to some list-of-data-points (also used by the UI thread in OnDraw()
to paint the whole window) and draw the new data point only. You should use some sort of synchronization (eg Critical Section) to access the data points list, because it is used and modified by more than one threads. As all paint operations should best be performed in the context of the UI thread, you can define a custom message and have the worker thread sending this message (SendMessage()
) to the window. The routine handling the message should do the actual drawing, ie paint the new data point.If implemented correctly this should result in a smooth operation, allowing the window to be moved or resized while the paint operation is in progress, without blocking the window or the application.
Upvotes: 1