windows 绘制窗体时防止闪烁

如果一个程序出现闪烁现象,会让人觉得程序编写人员很马虎,缺乏对细节的足够重视。Windows程序的任何部分都没有任何理由出现闪烁现象。这篇文章的目的是告诉读者如何使用相关的技术防止窗口出现闪烁效果。

 

什么是闪烁

闪烁可以这样定义:当后面一幅图像以很快的速度画在前面一幅图像上时,在后面图像显示前,你可以很快看到前面那一个图像,这样的现象就是闪烁。我认为,闪烁会让使用者对程序很不满,原因是:如果用户接口编码如此糟糕,那么程序的其他部分呢,如何能相信数据的正确性呢?一个具有平滑,快速相应的程序会给用户带来信心,这个道理很简单。

程序出现闪烁可以由多种形式造成,最常见的原因是窗口大小发生改变时,其内容重画造成闪烁。

仅仅画一次

这是一个黄金法则,在任何计算机(Windows或者你使用的任何操作系统)上处理画法逻辑都需要遵循,即永远不要将同一像素画两次。一个懒惰的程序员常常不愿意在画法逻辑上投入过多精力,而是采用简单的处理逻辑。要避免闪烁,就需要确保不会出现重复绘制的情况发生。现在,WIndows和计算机还是很笨的,除非你给他们指令,否则他们不会做任何事情。如果闪烁的现象发生,那是因为你的程序刻意地多绘制了屏幕的某些区域造成的. 这个现象可能是因为一些明确的命令,或者一些被你忽视了的地方。如果程序有闪烁的现象出现,你需要你知道如何找到好的方案去解决这个问题。

WM_ERASEBKGND

通常,首先需要怀疑的是WM_ERASEBKGND消息。当一个窗口的背景需要被擦除时,这个消息会被发送。这是因为窗口的绘画通常经历了两个过程

  • WM_ERASEBKGND: 清除背景
  • WM_PAINT: 在上面绘制内容

这两个过程让窗体在绘制内容时变得很简单,即:每次当收到WM_PAINT消息时,你知道已经有了一个新画布等待去绘制。然而,画窗口两次(一次是通过WM_ERASEBKGND画背景,另外一次是WM_PAINT)将会导致窗口出现比较糟糕的闪烁现象。只要看看标准的编辑框-打开Windows的写字板并改变窗口大小,就可以看到那种闪烁的效果。

那么,如何避免窗口背景的重刷呢?有如下两种方法:

  • 设置窗口背景刷子为NULL(当注册Windows类时,设置WNDCLASS结构中的hbrBackground成员为零)
  • 在WM_ERASEBKGND消息处理时 返回非零值

以上任何一种方法都可以阻止WM_ERASEBKGND 消息去清除窗口。其中,第二个方案的通常可以以如下代码实现:

case WM_ERASEBKGND:
    return 1;

当你标记窗口内容无效并试图更新时,还有如下办法可以防止WM_ERASEBKGND消息:InvalidateRect函数的最后一个参数可以指明在下一次窗口重画时,是否窗口的部分背景会被重刷。将该参数置为False可以防止当窗口需要重画时系统发出WM_ERASEBKGND消息。

InvalidateRect(hwnd, &rect, FALSE);

不该画的时候一定不要画

有一个比较普遍的现象:即使窗口中只有一个小的部分发生了改变,往往所有的部分都会被重画。比如,经常地,当窗口大小被改变时,一些(不是所有)的程序会重画所有的窗口。通常,这是个是不必要的,这是因为当窗口大小被改变时,经常是之前窗口的内容是不变的,仅仅是改变大小造成的一个小的边界区域需要重画。此时,没有必要重画所有区域。如果在这里多注意,多考虑,就可以使用好的算法以使得一次只有最小的部分被画。

系统中每个窗口都有更新区域。这个区域描述了窗口中变得无效需要重画的地方。如果一个窗口仅仅其需要更新的区域,不多绘制其他地方,那么窗口的绘制效果将会非常快。

有几种方法可以获得窗口的更新区域。通过GetUpdateRgn 函数可以获得准确的更新区域,这个函数返回的结果可以使矩形的区域也可以是非矩形的区域。通过GetUpdateRect 函数可以获得需要更新的最小矩形区域。通常使用矩形的更新区域比较容易。第三个方法是在BeginPaint/EndPaint中得到PAINTSTRUCT 结构,从而得到准确的更新区域信息。

一个常规的画法函数是这样的:

PAINTSTRUCT  ps;
HDC          hdc;
case WM_PAINT:
    hdc = BeginPaint(hwnd, &ps);
    // do painting
    EndPaint(hwnd, &ps);
    return 0;

BeginPaint函数初始化PS(PAINTSTRUCT)结构,其中,成员rcPaint是一个RECT结构,描述了包含了需要更新的最小矩形区域(就像GetWindowRect函数)。

如果仅仅在这个矩形区域上绘制窗口,速度上绘有很好地提高。

现在,当使用BeginPaint/EndPaint时Windows会自动剪切掉画在更新区域外面的部分。这意味着,你没有机会画到更新区域以外的地方。可能你会认为,如果是这样的话,花功夫确保代码不试图画到更新区域外是没有意义的,反正没有画出任何东西来。然而,你仍然可以避免不必要的API调用和相关计算,所以,我认为放一些精力在如何工作地更快上是绝对值得的。

如果还是不能解决

 有些时候,当你花了很多努力去考虑非常好的画法时,发现窗口还是会被全部刷新。这通常是由两个Window 类的属性造成的:CS_VREDRAW 和 CS_HREDRAW。如果有其中一个标志被设置时,那么当窗口水平或者竖直方向有大小被改变时,其内容每次都会被重新刷新。所有,你需要关掉这两个标志,解决的唯一的方式是在创建窗体和窗体类被注册时,确保这两个属性不被设置。

WNDCLASSEX wc;
wc.cbSize  = sizeof(wc);
wc.style   = 0; /* CS_VREDRAW | CS_HREDRAW; */ 
...
RegisterClassEx(&wc);

上面的例子描述了当窗体类被注册时,这两个属性不被设置的实现方法。

有一点需要注意:如果主窗体有了这两个属性,即使子窗体没有重画标志,会导致所有子窗体在其大小被改变时会被重绘。可以通过以下方式避免这个情况发生:

剪切子窗体
有时,闪烁的原因是因为当重画时,父窗体没有剪切其子窗体区域。这样的结果导致,整个父窗口内容被重画,而子窗体又被显示在了上面(造成闪烁)。这个可以通过在父窗体上设置WS_CLIPCHILDREN 来解决。当这个标志被设置时,被子窗体占据的任何区域将会被排除在更新区域外。因此,即使你尝试在子窗体所在的位置上绘制(父窗口的内容),BeginPaint中的剪切区域也会阻止其绘制效果。
双缓冲和内存设备描述表(Memory Device Context, 简称Memory-DC)

常见的彻底避免闪烁的方法是使用双缓冲。其基本的思路是:将窗体的内容画在屏幕外的一个缓冲区内,然后,将该缓冲区的内容再传递到屏幕上(使用BilBlt函数)。这是一个非常好的减少闪烁的方法,但是经常被滥用,特别是当程序员并不真正地理解如何有效地绘制窗口时。

典型的双缓冲代码如下:

HDC          hdcMem;HBITMAP      hbmMem;HANDLE       hOld;PAINTSTRUCT  ps;HDC          hdc;....case WM_PAINT:// Get DC for windowhdc = BeginPaint(hwnd, &ps);// Create an off-screen DC for double-bufferinghdcMem = CreateCompatibleDC(hdc);hbmMem = CreateCompatibleBitmap(hdc, win_width, win_height);hOld   = SelectObject(hdcMem, hbmMem);// Draw into hdcMem// Transfer the off-screen DC to the screenBitBlt(hdc, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY);// Free-up the off-screen DCSelectObject(hdcMem, hOld);DeleteObject(hbmMem);DeleteDC    (hdcMem);EndPaint(hwnd, &ps);return 0; 

这个方法比较慢,因为在每次窗体需要重画的时候内存设备描述表(Memory-DC)都需要被重新创建。更有效的方法是,仅仅创建内存设备描述表(Memory-DC)一次,并使其足够大到能满足任何时候的整个窗体刷新。当程序结束时,再销毁这个内存设备描述表(Memory-DC)。这两种方法都存在对内存开销的问题,特别是如果内存设备描述表(Memory-DC)是针对真个屏幕的大小。双缓冲也需要两倍的时间去画。这是因为其第一次是在内存设备描述表(Memory-DC)上画,然后再使用BitBlt画回到屏幕上。当然,好的显卡会使BitBlt更快,但是仍然会耗CPU 时间。

如果程序需要显示相当复杂的信息,比如像网页,那么你应该使用内存设备描述表(Memory-DC)。比如IE,如果不使用双缓冲,是没有办法在绘制网页时不闪烁的。

没有必要将双缓冲技术用于整个窗体的绘制中。可以这样设想,窗口中仅仅有一个小部分包含了复杂的图形对象(比如半透明的位图或者其他)。你应该将内存设备描述表(Memory-DC)仅仅用于着一个小区域,其他区域使用常规的方法。 有时,通过仔细的思考,经常可以避免使用双缓冲而直接将结果画到屏幕上。只要你不破坏黄金法则,即“永远不要将一个像素画两次”,就可以防止闪烁的出现。

避免过度绘制

我想说的关于这个话题是这样的:有一个需要自己定义画法的窗体的标题栏。首先,你画了标题,接着在上面画一些其他的图形。现在,只要标题需要被重画,就会出现闪烁现象。这是因为你没有合乎黄金法则。这里,标题被很快地显示在其他图形在上面绘制时,导致了闪烁。

有两种技术可以组织这种类型的闪烁。第一个是使用剪切,第二个是使用你的大脑。

使用剪切时,你可以使用ExcludeClipRect 函数在设备描述表中去标记一个特定的区域。当一个区域被标记上时,即使在该区域上面重画也不会产生效果。一旦背景已经被绘制了,可以通过SelectClipRgn移掉该标记的区域,其他图形能被画到前面标记的区域上。通过准确的标记(剪切),可以在很多时候被避免过度绘制。

另外一个方案就是找更聪明的解决办法。比如,当你需要画一个表格,通常应该先画空的背景,再画网格线从而产生表格。但是,这个方法会使网格线产生闪烁,这是因为在网格线被画之前,下面背景被很快地显示了一下。然而可以使用不同的做法达到想要的结果。即,不是一次画一个大的空背景,而是画一系列的空方块,每一个方块边是被一个像素的宽度分开。这样,当画网格线时,他们刚好能被画到一个之前没有画过的地方。其结果是不会有闪烁现象,因为没有像素被画了超过两次。

使用你的头脑去想一个好的算法可能需要长一点的时间,但是却是值得的,因为这能让结果更好。

结论

希望你再也不会问:“为什么我的窗体会闪烁”这样的问题。我已经讲解了闪烁的主要原因和解决办法。如果你遇到了闪烁的问题,你应该能找到原因并且使用这里提到的技术来解决了。

-----------------------------------------------------------------------------

------------------------------------------------------------------------------

英:

 

Flicker is the sign of sloppy programming and a lack of attention to detail. There is no reason why any part of a Windows program should flicker. The aim of this article is to present the reader (that's you) with the techniques used to prevent their windows applications from flickering.

What is flickering?

Flicker is simply this: the display of one image over the top of another in rapid succession. The result of this is screen flicker, where you can see one image briefly before another one is shown on top. Personally I find applications that "flicker" annoying to use, for this one reason: If the user-interface has been badly coded, then what does this say about the rest of the application, the part that you trust your data with? An application that has a smooth, fast user interface inspires confidence in it's users - it's as simple as that.

An application can flicker in many ways. The most common cause is when a window is resized, causing the contents to flicker badly as it is redrawn.

Only draw things once

This is the golden rule when doing any kind of painting on a computer, be it Windows or whatever OS you are using. You must never draw over the same pixel twice. A lazy programmer will often avoid putting any thought into the painting process, instead opting to take the easy route.

With the case of flickering, it is your responsiblity to ensure that no "overdraw" occurs. Now, Windows and your computer are fundamentally stupid; they won't do anything unless you instruct them explicitly. If any flickering is occuring, it is because some part of your program has deliberately overdrawn some area of the screen.

This may be because of some explicit command, or something which you have neglected to do. In either case, if your Windows program has a flickering problem, you need to understand how best to remove the problem.

WM_ERASEBKGND

The prime suspect is usually the WM_ERASEBKGND message. This message is sent to a window when it's background needs to be erased. This happens because windows are usually painted using a 2-stage process:

  • WM_ERASEBKGND: Clear the background
  • WM_PAINT: Draw the contents on top

This makes it easy to draw a window's contents: Every time you receive a WM_PAINT message, you know that you have a nice fresh canvas to draw on. However, drawing a window twice (once with WM_ERASEBKGND, once again with WM_PAINT) will cause the window to badly flicker. Just take a look at the standard Edit control in Windows - open up Notepad.exe and resize the window, and see how the contents flicker as it is redrawn.

Right then, how do we avoid erasing the background of a window? There are two methods.

  • Set the window's background brush to NULL. (Set the hbrBackground member of the WNDCLASS structure to zero when you register the window class).
  • Return non-zero in the WM_ERASEBKGND message handler.

Any one of these will steps will prevent the WM_ERASEBKGND message from clearing the window. The last option is usually easiest to implement:

case WM_ERASEBKGND:return 1;

It is also possible to prevent WM_ERASEBKGND when you invalidate and update a window. The InvalidateRect API call's last parameter specifies whether or not a portion of a window is to have it's background erased when it is next redrawn. Specifying FALSE for this paramter prevents WM_ERASEBKGND from being sent when the window is redrawn.

InvalidateRect(hwnd, &rect, FALSE);
Don't draw things when you don't have to

It is quite common for a Windows application to redraw it's entire window contents, even if only a small part of it changed. This is most usually the case when a window is resized - some (but not all) programs redraw the whole window. This is normally not necessary, because when a window is resized, more often than not the previous window contents is left unchanged, and the resize has just uncovered a small border which needs painting. It is not necessary to redraw the entire contents in this case. If a little thought and care is used, the painting algorithms can be written so that only the bare minimum is painted at any one time.

Every window in the system keeps an update region. This region describes the area of a window that has become invalidated and needs repainting. If a windows only updates the required area, and no more, then the window will draw much quicker as a result.

There are several ways to retrieve the update region for a window. The GetUpdateRgn API call retrieves the exact region, be it rectangular, or a more irregular shape. The GetUpdateRect API call retrieves the smallest bounding rectangle that encloses the update region. It is usually easier to just work with a rectangular area like this. The third method is to use the PAINTSTRUCT structure in conjunction with the BeginPaint/EndPaint API calls.

A normal painting procedure looks like this:

PAINTSTRUCT  ps;HDC          hdc;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);// do paintingEndPaint(hwnd, &ps);return 0;

BeginPaint initializes the ps (PAINTSTRUCT) structure. One member, rcPaint, is a RECT structure which describes the smallest bounding rectangle that encloses the update region (Just like the GetWindowRect API call). By only limiting drawing to just this rectangular region, painting can be dramatically sped up.

Now, Windows automatically clips any drawing you perform outside the update region when you use BeginPaint/EndPaint. This means that there is no way you can draw outside the update region even if you tried. You might think that it is pointless to make sure your code doesn't try to draw outside the update region, even when nothing will be drawn anyway. However, you are still avoiding unnecessary API calls and calculations, so I think it is always worth putting in a little more effort to get things working as fast as possible.

When you just can't help it

There are occasions when you spend alot of time and effort getting your super-duper drawing code working, only to find that your window is still getting redrawn in it's entirety. This is usually the cause of two window class styles - CS_VREDRAW and CS_HREDRAW. When a window class has either of these two styles set, the window contents will be completely redrawn every time it is resized either vertically or horizontally (or both). So, you need to turn off these two class styles. The only way to do this is to make sure your window isn't created with them in the first place, and to prevent this from happening, you have to make sure that CS_HREDRAW and CS_VREDRAW aren't included when the window class is registered.

WNDCLASSEX wc;wc.cbSize  = sizeof(wc);wc.style   = 0; /* CS_VREDRAW | CS_HREDRAW; */...RegisterClassEx(&wc);

The above example is just to help illustrate the point that these two styles must not be included when the window class is registered.

Just a word of warning here: If the main window in an application has these two class styles set, then this will cause all child windows to be redrawn during a resize, even if those children don't have the redraw flags set. This can be avoided by following the next step:

Clipping child windows

Sometimes flickering occurs because a parent window doesn't clip it's children when it paints itself. This results in the entire parent window contents being shown, and the the child windows being displayed on top (causing flicker). This can be easily solved by setting the WS_CLIPCHILDREN style on the parent window.

When a window has this style set, any areas that its child windows occupy are excluded from the update region. So, even if you try to draw over a child control, the clipping region that BeginPaint assigns will prevent you from doing so.

Double-buffing and memory-DC's

A common method to completely eliminate flickering windows is to use a technique called double-buffering. This basic idea is to draw a window's contents into an off-screen buffer, and then transfer this buffer to the screen in one fell-swoop (using BitBlt). This is a pretty good way to reduce flicker, but is often overused, especially by programmers who don't really understand how get efficient drawing working.

The basic way double-buffering works is like this:

HDC          hdcMem;HBITMAP      hbmMem;HANDLE       hOld;PAINTSTRUCT  ps;HDC          hdc;....case WM_PAINT:// Get DC for windowhdc = BeginPaint(hwnd, &ps);// Create an off-screen DC for double-bufferinghdcMem = CreateCompatibleDC(hdc);hbmMem = CreateCompatibleBitmap(hdc, win_width, win_height);hOld   = SelectObject(hdcMem, hbmMem);// Draw into hdcMem// Transfer the off-screen DC to the screenBitBlt(hdc, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY);// Free-up the off-screen DCSelectObject(hdcMem, hOld);DeleteObject(hbmMem);DeleteDC    (hdcMem);EndPaint(hwnd, &ps);return 0; 

This method is a little slow, because the offscreen memory-DC is created from scratch every time the window needs to be drawn. A more efficient method would be to create the memory DC only once, big enough so that the entire window can be painted at any time. When the application terminates, the memory DC would then be destroyed. Both these methods are potentially quite memory-intensive, especially if the memory DC needs to be the size of a screen (1024 * 768 * 32 bytes=2.5 Mb).

Double-buffering will also be twice as slow as it needs to be. Because you are drawing once to the memory-DC, then again during the "blit", you are using up clock cycles when you don't need to. Granted, a fast graphics card will perform a BitBlt very quickly, but it's still wasted CPU.

If your application needs to display quite complicated information (say, like a web-page), then you would need to use the memory-DC method. Take Internet Explorer, for instance. There is no way it would be able to render a web-page with no flickering without using double-buffering.

Double-buffering doesn't have to be used to paint a whole window. Imagine that you had just a small portion of a window that contained a complex graphic object (maybe a semi-transparent bitmap or something). You could use an off-screen DC to draw just this one region, and BitBlt that to the screen, whilst drawing the rest of the window normally.

Sometimes though, with a little careful thinking, it is often possible to avoid double-buffering and draw straight to the screen. As long as you don't break the golden rule, "Never draw over the same pixel twice", you will achieve flicker-free drawing.

Avoiding deliberate overdraw

What I mean by this is the following type of situation. Say, you are custom-drawing the titlebar of a window. You draw the caption first, then draw some additional graphics over the top. Now, whenever the caption needs to be painted, it will flicker. This is because you haven't followed the "golden rule". In this case, the caption is being shown briefly before additional graphics are painted on top, which appear to flicker.

There are two techniques you can use to prevent this type of flickering. The first is to use clipping, the second is to use your brain.

 

 

 

In the case of clipping, you can use the ExcludeClipRect API call to mask out certain areas of a device context. When an area is masked, it is not affected when painted over. Once a background has been drawn, the clipping area can be removed with SelectClipRgn, and another graphic can be painted in the previously masked-out area. By using appropriate masking (or clipping), overdraw can be eliminated in alot of cases.

The other option is to take a more intelligent approach. Imagine you had to draw a grid. A grid would normally be painted by first drawing a blank background, and then drawing a series of lines (horizontal and vertical) to create the grid effect. The problem with this type of approach is that the grid lines will appear to flicker, because the background is briefly appearing underneath each line before the lines are drawn. However, the same effect can be achieved with a different approach. Instead of drawing a single blank background, draw a series of blank squares, separated by a pixel-wide space on each side. When you come to draw the grid lines, they can be placed in the pixel-wide gaps which haven't been painted over yet. The result is the same, but this time there is no flickering because no pixel has been painted over twice.

Using your brain to think around a problem may take slightly longer than the direct "no-brainer" approach, but I think it is worth the extra effort, because the results can be so much better.

Conclusion

Hopefully you should never have to ask the question "Why does my window flicker?" ever again. I have presented the major causes of flickering in a windows program, and also the techniques you can use to remove this flickering. If you encounter flickering in a program you are developing, you should be able to identify the possible causes, and use the techniques described in this tutorial to completely eliminate flicker from your applications.

包含文件说明: 1. SolveFlashingAndRedrawv1.0.5 纯净版 无闪烁的MFC应用框架,实际使用把此工程改名成你要建立的项目名称,然后开始开发即可。你熟悉MFC的话研究这个框架的半个小应该就明白并熟练运用了。 2.SolveFlashingAndRedrawv1.0.5 demo版 利用SolveFlashingAndRedrawv1.0.4框架写的一个示例小程序,主要展示框架要实现的优点特性。 3.VCRn 修改vc工程名工具 ___作者 田彬.exe 用网上找到的一个MFC改工程名称的小工具,很实用。如果你想使用本框架就可以用它来改成你想要的工程名了。 4. 未使用本框架的类似功能简化程序 没有使用框架的程序,实现的功能和Demo类似。但是运行之后改变窗口大小等,会发现图形闪烁很厉害! 5. SolveFlashingAndRedrawv1.0.5 demo版 运行截图.jpg 6. ReadMe.txt 说明文件。 补充说明: 工程使用vc6.0开发,如果你用vc6.0双击.dsw文件无法打开,请先打开vc6.0然后把.dsw拖动到vc上面。 如果这种方法还是无法打开,你新建一个vc6.0 mfc sdi程序,把示例中框架拷贝到这个新工程中,运行即可,代码量不是太多。 框架说明: /****************************************************** SolveFlashingAndRedraw框架说明 ******************************************************/ /** 项目名称: demo框架 版本号: v1.0.5 第一作者: Jef 地址: 中国/江苏 日期: 20100724 电子邮箱: dungeonsnd@126.com 版权: 1.您可以修改及免费使用本程序。 2.修改之后附上您的个人信息发送到上面的作者邮箱,作者负责在全面测试后发布您修改后的新版本。 3.您使用本程序而导致任何伤害以及经济损失,由过错方依法承担所有责任,一概与第一作者及合作单位无关。 4.如果您使用本程序则表示您已经同意此版本协议!否则请勿使用! 项目功能: SolveFlashingAndRedraw框架是MFC解决窗口保存及重绘闪烁问题的一种比较好的方案(Win32解决方法类似)。 版本历史: v1.0.1 20091126 第一版本 v1.0.2 20091212 第二版本 1. 修改了部分变量的名字使其更符合其意义 2. 增加为两个工程,一是带demo例子的,另一是不带demo的纯净版. 3. 修改了其中一个错误. 如 CreateCompatibleDC之后没有调用DeleteDC等. v1.0.3 对v1.0.2进行了整理 v1.0.4 20100416 在v1.0.3的基础上进行整理,并增加了裁剪区,提高了绘图效率! v1.0.5 20100724 1. 添加了一个工具类CMemBmpDc,帮助产生一个内存DC,并把指定的内存位图选进去。方便绘图。 2. 演示了在适当机如何高效画图,见Demo版的DrawSinwave(bool bDrawOnScreen)函数。 演示了用两种方法来绘图, 方法1. 直接绘图到屏幕上, 同绘图到内存位图上,内存位图不会立即贴到屏幕上减少了内存拷贝的间,提高了效率, 将来窗口失效OnPait贴图到屏幕上. 这种方法的优点减小了不必要的内存拷贝,缺点当绘图内存复杂并且非常耗可能会导致闪烁。 故适用于像本Demo的这样绘图(本例函数只绘制一小段直线)。 方法2. 绘制到内存位图上后把应该重绘的这一小块设成裁剪区,然后立即OnPait重绘这个裁剪区。 运行步骤: 直接运行demo里面的程序,在窗口上任意拖拉鼠标画线,然后点击菜单栏的几个示范菜单项,然后移动窗口、 改变窗口大小、最大最小化窗口、用其它窗口覆盖此窗口、鼠标放到任务栏。。。 以上种种操作观察窗口内的图像变化。可以发现窗口内图像几乎看不到闪烁,而且窗口的元素已经保存下来重绘任然可以看到图像。 如何使用: 进行项目开发,可以先建立项目,然后把本解决方案框架拷贝到新建项目中即可。 也可以自己根据需要修改纯净版。 其它: 友情提示,小心 View类头文件及View类的实现文件中有说明,使用别把它弄到你实际项目里哦! 进行大量复杂的图形的输出,而且对效率要求特别高要考虑适当修改此框架(如增加裁剪区)后再使用哦。 关于如何在此框架的基础上提高绘图效率可以参阅下面的文章 如何提高绘图的效率 文章摘录 http://hi.baidu.com/new8sun/blog/item/68ccba8a80c3aadafc1f1079.html MFC双缓冲解决图象闪烁 2009-06-13 23:03 显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。 MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值