How to use MFC to draw images efficiently
TouchMe
There are many questions about how to avoid flickering and how to improve the display efficiency.
Most people think that the efficiency of the MFC plot function is very low, and they always want to seek other solutions.
The drawing efficiency of MFC is indeed not high, but it is not bad, and its drawing function is very simple to use,
As long as you use the appropriate method and add some tips, you can use MFC to get a very efficient drawing program.
I 'd like to talk about my long-term experience in using MFC plotting (Oh, of course, I only have more than two years ).
Some of my points.
1. Why does the displayed image flash?
Most of our drawing processes are stored in the OnDraw or OnPaint functions, while OnDraw is performing screen operations.
OnPaint calls the screen display. When the window needs to be re-painted for any reason,
Always clear the display area with the background color before calling OnPaint.
In this way, the background color and the display image appear alternately in a short time, making the display window look like
In the flash. If you set the background to NULL, the duplicate drawing will not flash.
Of course, this will make the display of the window messy, because there is no background color for the original
The drawn image is cleared, and the new image is superimposed.
Some people may say that flashing is caused by the drawing speed being too slow or the graphics displayed are too complex,
In fact, this is not true. The influence of the display speed of the drawing on the flicker is not fundamental.
For example, in OnDraw (CDC * pDC), write as follows:
PDC-> MoveTo (0, 0 );
PDC-> LineTo (100,100 );
This drawing process should be very simple and fast, but we will still see it when pulling the window changes.
Flashing. In fact, in principle, the more complex the drawing process, the slower the drawing process, the less flickering, because
The larger the ratio of time spent on clearing the screen with the background, the less obvious a person will feel.
For example, if the screen time is 1 s, the drawing time is also 1 s, so that the continuous re-painting within 10 s will flash.
5; if the screen time is 1 s, and the drawing time is 9 s, the continuous re-painting within 10 s
It will only Flash once. This can also be tested by writing in OnDraw (CDC * pDC) as follows:
For (int I = 0; I <100000; I ++)
{
PDC-> MoveTo (0, I );
PDC-> LineTo (1000, I );
}
The program is abnormal, but the problem can be explained.
Some people may want to explain why a simple image does not look as complex.
Why? This is because the complex image occupies a large area and the contrast caused by the re-painting is relatively large.
But the flash frequency is low.
So why does the animation appear non-flashing when its re-painting frequency is high? Here, I will emphasize again,
What is flashing? Flashing means contrast. The larger the contrast, the more powerful the blinking. Because the animation has two consecutive frames.
The difference is very small, so it does not seem to flash. If you do not believe it, you can add a pure white frame between each frame of the animation,
It's not surprising that we don't have a flash.
2. How to Avoid blinking
After you know why the image flash, you can do the right thing. First of all, remove the MFC
The background rendering process is provided. There are many implementation methods,
* When the window is formed, you can pay NULL for the registration background of the window.
* You can also modify the background after the formation.
Static CBrush brush (RGB (255, 0, 0 ));
SetClassLong (this-> m_hWnd, GCL_HBRBACKGROUND, (LONG) (HBRUSH) brush );
* You can also load OnEraseBkgnd (CDC * pDC) to return TRUE directly.
In this way, the background is gone, and the result graphic display is indeed not flashing, but the display is also as mentioned above,
A mess. What should I do? This requires the dual-Cache method. The dual buffer is not only available on the screen
In addition to displaying images, there are also images in the memory. We can first display the image in the memory
Draw the image, and then overwrite the image in the memory to the screen one by one point at a time (this
The process is very fast, because it is a very regular copy of memory ). In this way, what contrast can be used for drawing in the memory?
The large background color will not flash when it is cleared, because it cannot be seen. When pasted to the screen, because the final image in the memory
The differences between the image and the screen display are very small (if there is no motion, of course there is no difference), so that it does not seem to flash.
3. How to implement dual Buffering
The implementation program is provided first, and then explained again, also in OnDraw (CDC * pDC:
CDC MemDC; // define a display device object
CBitmap MemBitmap; // defines a bitmap object.
// Create a memory display device compatible with Screen Display
Memdc. createcompatibledc (null );
// No drawing yet, because there is no place to draw ^_^
// A bitmap compatible with screen display is created below. The size of the bitmap can be set to the size of the window.
Membitmap. createcompatiblebitmap (PDC, nwidth, nheight );
// Select the bitmap to the memory display device.
// Only the memory display device with the bitmap selected can draw a local image and draw it to the specified bitmap.
Cbitmap * poldbit = memdc. SelectObject (& membitmap );
// Use the background color to clear the bitmap. Here, I use white as the background color.
// You can also use your own color
Memdc. fillsolidrect (255,255,255, nwidth, nheight, RGB ));
// Drawing
Memdc. moveTo (......);
Memdc. lineto (......);
// Copy the image in the memory to the screen for display
PDC-> bitblt (0, 0, nwidth, nheight, & memdc, 0, srccopy );
// Cleanup after drawing is complete
Membitmap. deleteobject ();
Memdc. deletedc ();
The above comment should be very detailed, so I will not talk much about it.
4. How to Improve the drawing efficiency
I am mainly working on the CAD software for the Network Graphics of the power system. In a window, thousands of power components are usually displayed, and each component is composed of basic figures such as points, lines, and circles. If you really want to re-draw so many elements in a re-painting process, it can be imagined that this process is very long. If you have added the Image Browsing function, you need to re-paint a lot when you move the mouse to scroll the image. The speed will be too slow for users to endure. What should I do? We only need to study the Drawing Process of MFC.
In fact, not all charts drawn in OnDraw (CDC * pDC) are displayed. For example, you
Two rectangles are drawn in OnDraw. In one re-painting, although the drawing functions of both rectangles are executed, there may be only one display, this is because MFC sets the cropping area to improve the repainting efficiency. The purpose of the cropping area is to ensure that only the plotting process in this area is valid and that the plotting function is not displayed even if it is executed outside the area. In most cases, the re-painting of a window is mostly due to partial occlusion of the window or scrolling of the window. The changed area is not the whole graph but only a small part, this part needs to be changed in the cutting area of the pDC. Because the display (display to memory or video memory) is much more time-consuming than the calculation of the drawing process, only the display part should be displayed after the cropping area, greatly improving the display efficiency. However, this cropping area is set by MFC, which has improved the display efficiency. How can we further improve the efficiency when drawing complex images? Then, only the plotting process outside the cropping area is removed. You can first use pDC-> GetClipBox () to obtain the cropping area, and then determine whether your image is in this area during the drawing. If you are painting it, you will not draw it if you are not.
If your drawing process is not complex, this may not improve your drawing efficiency.
From
Http://www.host01.com/article/software/VisualC/20060917184432231.htm
Here is an example to illustrate how to solve the problem.
From: http://www.programbbs.com/doc/4882.htm
I want to move a region,
How to solve the problem of area flickering when the window is refreshed.
- Void cjhkljklview: ondraw (CDC * PDC)
- {
- Cjhkljkldoc * pdoc = getdocument ();
- Assert_valid (pdoc );
- // Todo: Add draw code for native data here
- Int I;
- Int X [20], Y [20];
- Cpen Hpen;
- Point W [5];
- X [0] = A/100 + 10;
- X [1] = A/100 + 30;
- X [2] = A/100 + 80;
- X [3] = A/100 + 30;
- X [4] = A/100 + 10;
- Y [0] = 10;
- Y [1] = 10;
- Y [2] = 25;
- Y [3] = 40;
- Y [4] = 40;
- For (I = 0; I <5; I ++)
- {W [I]. x = x [I];
- W [I]. y = y [I];
- }
- // CClientDC (this );
- // Hpen = createpen (ps_solid, 1, RGB (255, 0 ));
- Crgn argn, brgn;
- Cbrush abrush (RGB (40, 30, 20 ));
- Argn. createpolygonrgn (W, 5, 1); // point is a cpoint array,
- PDC-> fillrgn (& argn, & abrush );
- Abrush. deleteobject ();
- }
- Void cjhkljklview: ontimer (uint nidevent)
- {
- // Todo: add your message handler code here and/or call default
- InvalidateRect (NULL, true );
- UpdateWindow ();
- A ++ = 100;
- CView: OnTimer (nIDEvent );
- }
- Int CJhkljklView: OnCreate (maid)
- {
- If (CView: OnCreate (lpCreateStruct) =-1)
- Return-1;
- // TODO: Add your specialized creation code here
- SetTimer (1, 10, NULL );
- Return 0;
- }
- The timer is used to directly refresh the screen within 10 milliseconds, so that the effect will flash continuously.
- The solution is to trigger WM_ERASEBKGND with double buffering, and then modify to return TRUE;
- Define variables:
- CBitmap * m_pBitmapOldBackground;
- CBitmap m_bitmapBackground;
- CDC m_dcBackground;
- // Draw the background
- If (m_dcBackground.GetSafeHdc () = NULL | (m_bitmapBackground.m_hObject = NULL ))
- {
- M_dcBackground.CreateCompatibleDC (& dc );
- M_bitmapBackground.CreateCompatibleBitmap (& dc, rect. Width (), rect. Height ());
- M_pBitmapOldBackground = m_dcBackground.SelectObject (& m_bitmapBackground );
- // DrawMeterBackground (& m_dcBackground, rect );
- CBrush brushFill, * pBrushOld;
- // Black background color
- BrushFill. DeleteObject ();
- BrushFill. CreateSolidBrush (RGB (255,255,255 ));
- PBrushOld = m_dcBackground.SelectObject (& brushFill );
- M_dcBackground.Rectangle (rect );
- M_dcBackground.SelectObject (pBrushOld );
- }
- Memdc. bitblt (0, 0, rect. Width (), rect. Height (),
- & M_dcbackground, 0, 0, srccopy );
- // Draw the image
- Int I;
- Int X [20], Y [20];
- Cpen Hpen;
- Point W [5];
- X [0] = A/100 + 10;
- X [1] = A/100 + 30;
- X [2] = A/100 + 80;
- X [3] = A/100 + 30;
- X [4] = A/100 + 10;
- Y [0] = 10;
- Y [1] = 10;
- Y [2] = 25;
- Y [3] = 40;
- Y [4] = 40;
- For (I = 0; I <5; I ++)
- {W [I]. x = x [I];
- W [I]. y = y [I];
- }
- // CClientDC (this );
- // HPen = CreatePen (PS_SOLID, 1, RGB (255, 0 ));
- CRgn argn, Brgn;
- CBrush abrush (RGB (40, 30, 20 ));
- Argn. CreatePolygonRgn (w, 5, 1); // point is a CPoint array,
- MemDC. FillRgn (& argn, & abrush );
- Abrush. DeleteObject ();
- }
Compile and runProgramThe screen will not flash.