英文:
winapi: GetUpdateRect() with bRepaint TRUE inside WM_PAINT doesn't clear the paint region, but InvalidateRect() outside WM_PAINT does?
问题
我的自定义绘图区域的WM_PAINT代码大致如下:
// TRUE表示清除背景
if (GetUpdateRect(hwnd, &r, TRUE) == 0)
return; // 没有更新区域,不做任何操作
dc = BeginPaint(hwnd, &ps);
// 检查返回值
// 使用GDI+绘制一些RGBA图像数据
EndPaint(hwnd, &ps);
(暂时忽略将HDC发送给GDI+的部分。)
我在这里使用GetUpdateRect()
而不是从BeginPaint()
获取更新区域,因为我总是希望在一个空白画布上绘制,特别是因为我在这里进行了Alpha混合绘制。此外,WM_PAINT的文档说如果GetUpdateRect()
失败,就不要调用BeginPaint()
。
然而,当我的自定义绘图区域实际执行这个WM_PAINT时,它并没有清除绘图区域。我可以看出来,因为启用了Alpha通道的图像会在自身上绘制,导致颜色变暗。
在我的代码的另一个部分,我通过调用InvalidateRect()
来响应键盘事件,以强制更新整个区域。如果我将等效的
InvalidateRect(hwnd, NULL, FALSE);
改为
InvalidateRect(hwnd, NULL, TRUE);
区域确实被正确清除。
那么我在这里到底做错了什么?谢谢。
(实际的代码在this;它是用Go语言编写的。这也是为什么我直接使用GDI+的原因。(我还没有弄清楚如何使用常规的GDI进行Alpha混合绘制。))
英文:
My custom drawing area's WM_PAINT looks something like this:
// TRUE to clear the background
if (GetUpdateRect(hwnd, &r, TRUE) == 0)
return; // no update rect; do nothing
dc = BeginPaint(hwnd, &ps);
// check return
// paint some RGBA image data with GDI+
EndPaint(hwnd, &ps);
(Disregard the sending a HDC to GDI+ for now.)
I use GetUpdateRect()
here instead of getting the update rect from BeginPaint()
because I always want to draw over a blank canvas, especially since I'm doing alpha-mixed drawing here. Furthermore, the docs for WM_PAINT
say not to call BeginPaint()
if GetUpdateRect()
fails.
However, when my custom drawing area actually does this WM_PAINT, it isn't clearing the drawing area. I can tell because the alpha-enabled image is drawn on top of itself, resulting in darker colors.
In another part of my code, I respond to a keyboard event by calling InvalidateRect()
to force an update of the whole area. If I change the equivalent of
InvalidateRect(hwnd, NULL, FALSE);
to
InvalidateRect(hwnd, NULL, TRUE);
the area does get cleared properly.
So what exactly AM I doing wrong here? Thanks.
(The actual code is this; it's written in Go. This is also why I am working with GDI+ directly. (I have yet to figure out how to do alpha-mixed stuff with regular GDI.))
答案1
得分: 2
InvalidateRect
使区域无效,这意味着接下来会有擦除/重绘的请求。你将其与不进行验证或无效化的GetUpdateRect
进行了比较,并且使用TRUE
参数擦除了区域,就像使用标准绘图方式擦除一样。此外,根据MSDN的说明:
> 由BeginPaint
函数检索到的更新矩形与由GetUpdateRect
检索到的更新矩形相同。
>
> BeginPaint
会自动验证更新区域,因此在调用BeginPaint
后立即调用GetUpdateRect
将检索到一个空的更新区域。
综上所述,我不认为在绘图处理程序中以你所做的方式调用GetUpdateRect
有任何理由。BeginPaint
以相同的方式进行验证,而且无论如何,你都需要在绘图处理程序中进行验证。一旦你在那里,擦除处理程序已经被调用,没有必要通过GetUpdateRect
再次调用它。
BeginPaint
的PAINTSTRUCT::rcPaint
以相同的方式获取更新区域,只是在调用EndPaint
之前,如果更新区域为空,不要进行任何操作。
英文:
InvalidateRect
invalidates the region, which means it is followed by erase/repaint requests. You compare it to GetUpdateRect
which does not do validation or invalidation and with TRUE
parameter erases the region the same way you would have it erased with standard painting. Furthermore as MSDN states:
> The update rectangle retrieved by the BeginPaint function is identical to that retrieved by GetUpdateRect.
>
> BeginPaint automatically validates the update region, so any call to GetUpdateRect made immediately after the call to BeginPaint retrieves an empty update region.
All together I don't see any reason to call GetUpdateRect
the way you do it in paint handler. BeginPaint
does it the same way, with validation, and you need to do it as soon as you are in paint handler anyway. Once you're there, erase handler is already called with no need to call it once again through GetUpdateRect
.
BeginPaint
's PAINTSTRUCT::rcPaint
gets you the update region the same way, just don't do anything before calling EndPaint
if the update region is empty.
答案2
得分: 0
然而,当我的自定义绘图区实际执行WM_PAINT时,它并没有清除绘图区域。我可以看出来,因为启用了alpha通道的图像会在自身上绘制,导致颜色变暗。
关于WM_PAINT或你调用的函数,并没有任何关于清除区域的内容。如果你想要清除区域,你必须自己做。大多数程序不会这样做,因为先清除再绘制会导致闪烁。如果你不清除区域并且绘制了一个透明对象,就会出现问题。解决方案可能是使用离屏绘制。
InvalidateRegion(,,true)是不同的。它会发送WM_ERASEBKGND和WM_NCPAINT消息来擦除和绘制区域。
GDI不支持透明度。可以使用掩码操作来模拟某些类型的透明度。
透明度支持是在GDI+中引入的,它相当全面,尽管使用得不多。将GDI+效果应用于旧代码,包括使用alpha通道的PNG图像等,不难。
Vista中的高级效果依赖于DirectX,这是一个相当不同的绘图系统。WPF中的可视化与旧的DC非常不同。你可能想考虑使用该系统编写新代码。
英文:
Re:
>However, when my custom drawing area actually does this WM_PAINT, it isn't clearing the drawing area. I can tell because the alpha-enabled image is drawn on top of itself, resulting in darker colors.
There is nothing about WM_PAINT or the functions you call that will clear the region. If you want it cleared, you must do that yourself. Most programs don't, because clearing followed by painting causes flicker. If you don't clear and you do paint a transparent object, you have a problem. The solution may be to use off-screen drawing.
InvalidateRegion(,,true) is different. It sends WM_ERASEBKGND and WM_NCPAINT messages to the region is erased and painted.
There is no support for transparency in GDI. Some kinds of transparency can be simulated using masking operations.
Transparency support was introduced with GDI+, and is reasonably extensive although not much used. It's not hard to retrofit GDI+ effects into legacy code, including alpha-channel PNGs and the like.
The fancy effects from Vista depend on DirectX, which is a rather different drawing system. The Visual in WPF is very different from the old DC. You might want to consider writing new code using that system.
答案3
得分: 0
感谢你们两位。不过,Stack Overflow只允许我一次性选择一个答案...是的,在正确清除后,我遇到了被选中答案中提到的闪烁问题。
最终,我决定放弃我目前的做法,特别是在Treeki/Ninjifox的建议下,他建议我使用AlphaBlend()
函数。不过,现在我又遇到了更多问题...但这将是另一个问题,需要一个独立的示例程序来解决。
英文:
Thanks to both of you. Stack Overflow will only let me award one check at a time though... And yep, after doing the clearing properly I got the flicker mentioned in the checked answer.
Ultimately I decided to throw out what I have so far, especially at the advice of Treeki/Ninjifox, who suggested I use the AlphaBlend()
function instead. Now, though, I have more problems... but that'll be for another question, with a standalone example program.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论