InvalidateRect你懂的

菜鸟二号 2011-04-13 12:56:41
不用说这个函数的用途了,大家都应该比我还熟悉了..O(∩_∩)O哈哈~

但是本人有几点存在疑问:
该函数可控制使窗体的局部无效(就是重绘),可是同时该函数依然发送WM_PAINT消息,而BeginPaint所得到的DC与GetDC所
获取到的DC并不一样(本人猜测,如果你觉得这句话不对,可以指出)。

在WM_PAINT里,会执行全部的绘图部分,那这些代码的执行时间跟InvalidateRect后重绘的时间是否一样呢

如果你没看懂我的问题,我换一种简单的方式说:就是说InvalidateRect窗体是局部重绘的,可是这个局部绘制是要执行WM_PAINT消息里的全部代码,这个是否浪费时间和CPU,不管区域大小,都是基本类似的时间(电脑环境不变时)?

如果依然没看懂,我再换:就是说InvalidateRect局部重绘窗体的方法和GetDC后,自己绘制局部,那个更省时间和CPU(且不说那种方法的功能好)?

我自己想了一下,可以总结为之下的问题:
就是说InvalidateRect发送的WM_PAINT消息里的DC不一定是窗体的整个DC,可能是局部,那如果在DC的外面画图(例如BitBlt(hdc,-100,-100,20,20...),注意这个坐标是负的),那会不会耗时间和CPU?
如果你懂得这些问题,不妨提示一下,如果你也不懂,没事,我们一起研究...

...全文
955 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
dulvtianya 2011-05-31
  • 打赏
  • 举报
回复
楼主的钻研精神太强悍了。。
菜鸟二号 2011-04-14
  • 打赏
  • 举报
回复
看少一个,不好意思哈,mfcing
到时我会再开一个送分贴,你一定要来哈
CppCoder 2011-04-13
  • 打赏
  • 举报
回复
只知道InvalidateRect无效局部区域,引发重新绘制消息,一般我用来强制更新界面

学习,帮顶
j8daxue 2011-04-13
  • 打赏
  • 举报
回复
这个函数使得windows知道要刷新局部,会对DC做剪裁,只拷贝刷新的地方到屏幕。
但不管怎样,OnPaint里的函数还是会全部调用一次。除非OnPaint里判断好那些元素和这个无效区域相交。
无言猪 2011-04-13
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 hkf314 的回复:]

引用 13 楼 lostying 的回复:
做了实验,BeginPaint获取的DC只能画无效区域,GetDC获取的能画整个Client区域(至少,出去的部分没做实验)


呵呵,正是如此啊
[/Quote]
这样看的话就是用PaintDC的效率高了,这应该是问题根结了吧
菜鸟二号 2011-04-13
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 howtop2p 的回复:]
InvaidateRect将无效区加入BeginPaint返回的DC中,计算一个包含各个无效区的最小矩形作为需要绘制得最小区域,也就是GetClipBox返回的值,在下一个WM_PAINT时绘制,在这个区域外部的绘制无效,在区域外部执行的绘图语句的效果不会被显示(显示比执行这些语句费时得多),所以虽然都要执行全部的WM_PAINT,但InvalidateRect效率比Invalidate高
[/Quote]

言之有理
HowToP2p 2011-04-13
  • 打赏
  • 举报
回复
InvaidateRect将无效区加入BeginPaint返回的DC中,计算一个包含各个无效区的最小矩形作为需要绘制得最小区域,也就是GetClipBox返回的值,在下一个WM_PAINT时绘制,在这个区域外部的绘制无效,在区域外部执行的绘图语句的效果不会被显示(显示比执行这些语句费时得多),所以虽然都要执行全部的WM_PAINT,但InvalidateRect效率比Invalidate高
菜鸟二号 2011-04-13
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 lostying 的回复:]
做了实验,BeginPaint获取的DC只能画无效区域,GetDC获取的能画整个Client区域(至少,出去的部分没做实验)
[/Quote]

呵呵,正是如此啊
无言猪 2011-04-13
  • 打赏
  • 举报
回复
做了实验,BeginPaint获取的DC只能画无效区域,GetDC获取的能画整个Client区域(至少,出去的部分没做实验)
菜鸟二号 2011-04-13
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 visualeleven 的回复:]
引用 8 楼 hkf314 的回复:
致5楼:

你所说的,我也略知12,而我也清楚,当窗体有一部分被遮挡时,窗体并不会发送WM_PAINT消息(本人使用Win7,不知道XP的情况),如果【InvalidateRect】和 【GetDC后再吧被遮挡区绘制上去】 的方法一样的话,为何系统不使用前者呢?

窗口被遮挡当然没有必要去绘制,绘制了你也看不到,只有当重新显示出来的时候才去绘制
[/Quote]

不绘制的话,窗体不会更新,还是之前那个窗体的部分,可是系统肯定是先保存了,然后GetDC后再吧被遮挡区绘制上去,这样了吧,怎么能算没有绘制呢?
疯狂-的-蜗牛 2011-04-13
  • 打赏
  • 举报
回复
会一点点的菜鸟,接分来了,(*^__^*) 嘻嘻……
Eleven 2011-04-13
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 hkf314 的回复:]
致5楼:

你所说的,我也略知12,而我也清楚,当窗体有一部分被遮挡时,窗体并不会发送WM_PAINT消息(本人使用Win7,不知道XP的情况),如果【InvalidateRect】和 【GetDC后再吧被遮挡区绘制上去】 的方法一样的话,为何系统不使用前者呢?
[/Quote]
窗口被遮挡当然没有必要去绘制,绘制了你也看不到,只有当重新显示出来的时候才去绘制
菜鸟二号 2011-04-13
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 visualeleven 的回复:]
InvalidateRect只刷新局部区域当然比刷新整个区域效率要高了
[/Quote]

刷新局部不假,可是它始终会有WM_PAINT消息发出,即使和前面的WM_PAINT消息合并了,可是依然会执行WM_PAINT消息里的全部代码,关键是,如果单独在一个合适的DC上按照WM_PAINT消息里的绘图代码绘制时间和此时(指局部重绘时)所花费的时间,那个常那个短呢?
菜鸟二号 2011-04-13
  • 打赏
  • 举报
回复
致5楼:

你所说的,我也略知12,而我也清楚,当窗体有一部分被遮挡时,窗体并不会发送WM_PAINT消息(本人使用Win7,不知道XP的情况),如果【InvalidateRect】和 【GetDC后再吧被遮挡区绘制上去】 的方法一样的话,为何系统不使用前者呢?
Eleven 2011-04-13
  • 打赏
  • 举报
回复
InvalidateRect只刷新局部区域当然比刷新整个区域效率要高了
菜鸟二号 2011-04-13
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 huangcheng90 的回复:]
InvalidateRect只是增加重绘区域,在下次WM_PAINT的时候才生效
[/Quote]

The system sends a WM_PAINT message to a window whenever its update region is not empty and there are no other messages in the application queue for that window.

本函数已经发送WM_PAINT消息,,的确也是增加无效区,但是这些无非是用法,我想知道这个局部重绘到底会不会用很多时间和CPU,而至于WM_PAINT消息,由于优先级较低,因此系统会将多个WM_PAINT消息合而为一,组合一个新的无效区
Eleven 2011-04-13
  • 打赏
  • 举报
回复
The WM_PAINT message is generated by the system and should not be sent by an application.The system sends this message when there are no other messages in the application's message queue

  也就是说WM_PAINT消息是由系统产生,非要等应用程序的消息队列为空时才发送WM_PAINT消息,并且该消息不应该被程序(自己写代码用SendMessage)来发送。

  当调用UpdateWindow函数,或者是Window检测到窗口被覆盖的地方需要恢复的时候,比如,第一次创建窗口,改变了窗口的大小,最大化,最小化等等(其实这些事件发生时会调用UpdateWindow函数,由该函数发送WM_PAINT消息),它会向用户程序发送一个WM_PAINT消息。窗口过程收到WM_PAINT消息后,并不代表整个客户区都需要被刷新,有可能客户区被覆盖的区域只有一小块,这个区域叫做“无效区域”,程序只需要更新这个区域。与WM_TIMER消息类似,WM_PAINT消息也是一个低级别的消息,虽然它不会像WM_TIMER消息一样被丢弃,但Windows总是在消息循环空的时候才把WM_PAINT放入其中。

  无效区域的坐标并不附带在WM_PAINT消息的参数中,在程序中有其他方法可以获取。WM_PAINT消息只是通知程序有个区域需要更新而已,所以Windows也不会同时将两条WM_PAINT消息放入消息循环中。当Windows要放入一条WM_PAINT消息的时候,如果发现已经存在一个无效区域了,那么它只需要把新旧两个无效区域合并计算出一个无效区域就可以了,消息循环中还是只需要一条WM_PAINT消息。

  实际上,Windows为每个窗口维护一个“绘图信息结构”,无效区域的坐标就在其中,每当消息循环空的时候,如果Windows发现存在一个无效区域,就会放入一个WM_PAINT消息。那么“绘图信息结构”怎么获取呢?BeginPaint函数的第二个参数就是一个绘图信息结构的缓冲区地址,windows会在这里返回绘图信息结构,结构中包含了无效区域的位置和大小,绘图信息结构的定义如下:

typedef struct tagPAINTSTRUCT { // ps 
    HDC   hdc; 
    BOOL fErase; 
    RECT rcPaint; 
    BOOL fRestore; 
    BOOL fIncUpdate; 
    BYTE rgbReserved[32]; 
} PAINTSTRUCT; 

  其中hdc字段是窗口的设备环境句柄,rcPaint字段是一个RECT结构,它指定了无效区域矩形的对角顶点(如果开始有一个((0,0),(40,40)),现在又来一个((20,20),(60,30)),那么拼接后就是((0,0),(60,40))),fErase字段如果为非零值,表示Windows在发送WM_PAINT消息前已经使用背景色擦除了无效区域,后面3个字段是Windows内部使用的,应用程序不必去理会他们。

  在某些情况下,显示区域的一部分被临时覆盖,Windows试图保存一个显示区域,并在以后恢复它,但这不一定能成功。Windows可能发送WM_PAINT消息:Windows擦除覆盖了部分窗口的对话框或消息框;菜单下拉出来,然后被释放;显示工具提示消息。

  在某些情况下,Windows总是一定保存它所覆盖的显示区域,然后恢复它。这些情况是:鼠标光标穿越显示区域;图标拖过显示区域。

  有时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,这一般是通过InvalidateRect和 InvalidateRgn函数来完成的。InvalidateRect和InvalidateRgn把指定的区域加到窗口的Update Region中,当应用的消息队列没有其他消息时,如果窗口的Update Region不为空时,系统就会自动产生WM_PAINT消息。

  系统为什么不在调用Invalidate时发送WM_PAINT消息呢?又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢?这是因为系统把在窗口中的绘制操作当作一种低优先级的操作,于是尽 可能地推后做。不过这样也有利于提高绘制的效率:两个WM_PAINT消息之间通过InvalidateRect和InvaliateRgn使之失效的区域就会被累加起来,然后在一个WM_PAINT消息中一次得到 更新,不仅能避免多次重复地更新同一区域,也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效,依赖于系统在合适的时机发送WM_PAINT消息的机 制实际上是一种异步工作方式,也就是说,在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这种延迟并不是我们希望的,这时我们当然可以在无效化窗口区域后利用SendMessage 发送一条WM_PAINT消息来强制立即重画,但不如使用Windows GDI为我们提供的更方便和强大的函数:UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region,当其不为空时才发送WM_PAINT消息;RedrawWindow则给我们更多的控制:是否重画非客户区和背景,是否总是发送WM_PAINT消息而不管Update Region是否为空等。

  BeginPaint

  在处理消息时,没有使用BeginPaint和EndPaint这对函数,结果导致其余的消息弹不出来,窗口拖动时,不停闪烁(其实那就是重绘)。后来还是在MSDN上找到了答案,现将原话贴出来。(在MSDN的The WM_PAINT Message标题中)

  BeginPaint sets the update region of a window to NULL. This clears the region, preventing it fromgenerating subsequent WM_PAINT messages. If an application processes a WM_PAINT message but does not call BeginPaint or otherwise clear the update region, the application continues to receive WM_PAINT messages as long as the region is not empty. In all cases, an application must clear the update region before returning from the WM_PAINT message. 
  BeginPaint函数的作用就是将窗口需要重绘的区域设置为空(也就是Update Region置空)。在正常情况下,我们接收到了WM_PAINT消息后,窗口的Update Region都是非空的(如果为空就不需要发送WM_PAINT消息了)。而当你响应这个消息的时候又不调用BeginPaint来清空,窗口的Update Region就一直是非空的,系统就会一直发送WM_PAINT消息。这样就形成了一个处理WM_PAINT消息的死循环。

  BeginPaint和WM_PAINT消息紧密相关。试一试在WM_PAINT处理函数中不写BeginPaint会怎样?程序会像进入了一个死循环一样达到惊人的CPU占用率,你会发现程序总在处理一个接 一个的WM_PAINT消息。这是因为在通常情况下,当应用收到WM_PAINT消息时,窗口的Update Region都是非空的(如果为空就不需要发送WM_PAINT消息了),BeginPaint的一个作用就是把该Update Region置为空,这样如果不调用BeginPaint,窗口的Update Region就一直不为空,如前所述,系统就会一直发送WM_PAINT消息。

  BeginPaint和WM_ERASEBKGND消息也有关系。当窗口的Update Region被标志为需要擦除背景时,BeginPaint会发送WM_ERASEBKGND消息来重画背景,同时在其返回信息里有一个标志表明窗口背景是否被重画过。当我们用InvalidateRect和InvalidateRgn来把指定区域加到Update Region中时,可以设置该区域是否需要被擦除背景,这样下一个BeginPaint就知道是否需要发送WM_ERASEBKGND消息了。

  当然关于 WM_PAINT消息还有很多的知识需要学习。另外要注意的一点是,BeginPaint只能在WM_PAINT处理函数中使用,并且在调用了BeginPaint函数后,不要忘记了调用EndPaint函数,他们可是一对的。

  重画函数 InvalidateRect,Invalidate,UpdateWindow, RedrawWindow

  InvalidateRect(部分区域) 和Invalidate(整个窗口) 仅仅是用来设置无效区域,但是并不重绘窗口。

  UpdateWindow 检查窗口有无无效区域,如果有,则立即发送一个WM_PAINT 消息给窗口并立即重画。 

  RedrawWindow相当于先调用InvalidateRect,紧接着又调用UpdateWindow,此外RedrawWindow还提供了一些前两者没法做到的功能。

如果不调用 InvalidateRect就调用 UpdateWindow,那么UpdateWindow什么都不做,因为没有无效区域。如果调用 InvalidateRect 后不调用UpdateWindow,则系统会自动在窗口消息队列为空的时候,系统自动发送一WM_PAINT消息。

Eleven 2011-04-13
  • 打赏
  • 举报
回复
Dreadnought 2011-04-13
  • 打赏
  • 举报
回复
InvalidateRect只是增加重绘区域,在下次WM_PAINT的时候才生效
Eleven 2011-04-13
  • 打赏
  • 举报
回复
The invalidated areas accumulate in the update region until the region is processed when the next WM_PAINT message occurs or until the region is validated by using the ValidateRect or ValidateRgn function.

The system sends a WM_PAINT message to a window whenever its update region is not empty and there are no other messages in the application queue for that window.
加载更多回复(1)

15,981

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧