[英]How to prevent color key flickering visibly when enabling LWA_COLORKEY
I have a window that should sometimes have a transparent hole in it, and sometimes not. 我有一扇窗户,有时应该在其中开一个透明的孔,有时没有。 Ideally, we would use SetWindowRgn, but that disables visual styles, which not only looks ugly but doesn't draw correctly with per-monitor DPI-awareness, so I am trying to use a layered window with a color key.
理想情况下,我们将使用SetWindowRgn,但会禁用视觉样式,这种样式不仅看起来难看,而且无法通过每个显示器的DPI感知正确绘制,因此我尝试使用带有颜色键的分层窗口。
When enabling the color key, I first call SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY)
, then invalidate the window so that it is redrawn. 启用颜色键时,我首先调用
SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY)
,然后使窗口无效,以便重绘该窗口。 At this point the window should not contain the key color. 此时,窗口不应包含键色。 Then the window receives
WM_PAINT
at some point later, and the key color is painted, but at this point the window should have LWA_COLORKEY
set, so again, I expect the key color not to be visible. 然后,该窗口稍后在某个位置接收
WM_PAINT
,并绘制了键颜色,但是此时该窗口应设置LWA_COLORKEY
,因此,我再次希望键颜色不可见。
When disabling the color key, I first repaint the window (synchronously) so that it does not contain the key color, and then disable WS_EX_LAYERED
, so again, I never expect to see the key color. 禁用颜色键时,我首先(同步)重新绘制窗口,使其不包含键颜色,然后禁用
WS_EX_LAYERED
,因此,我再也没有期望看到键颜色。
However, a window with the following window procedure constantly flickers between green, transparent and the background color as the mouse moves a across it. 但是,当鼠标在其上移动时,具有以下窗口步骤的窗口会在绿色,透明和背景颜色之间不断闪烁。
It seems that perhaps SetLayeredWindowAttributes
does not take effect immediately (and not even before the next WM_PAINT
). 似乎
SetLayeredWindowAttributes
可能不会立即生效(甚至不会在下一个WM_PAINT
之前生效)。 How can I make sure that this attribute has taken effect before repainting, or otherwise prevent the key color being visible? 如何确保此属性在重新粉刷之前已生效,否则会防止键颜色可见?
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static auto const colorkey = RGB(0,255,0);
static auto const hbrush = CreateSolidBrush(colorkey);
static auto transparent = false;
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
if (transparent) {
RECT rect{30,30,500,500};
FillRect(hdc, &rect, hbrush);
}
EndPaint(hWnd, &ps);
}
break;
case WM_MOUSEMOVE:
if (transparent) {
transparent = false;
RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
} else {
SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
transparent = true;
InvalidateRect(hWnd, nullptr, TRUE);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I don't think layered windows are designed to be turned on and off multiple times per second (Windows will allocate/destroy a 32 BPP image etc. each time you toggle). 我不认为分层的窗口设计为每秒打开和关闭多次(Windows会在每次切换时分配/销毁32 BPP图像等)。
SWP_FRAMECHANGED
and an extra erase does make it much better for me at least: SWP_FRAMECHANGED
和额外的擦除操作至少对我来说更好:
case WM_MOUSEMOVE:
if (transparent) {
transparent = false;
RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INTERNALPAINT|RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN);
SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
InvalidateRect(hWnd, nullptr, true);
} else {
SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
transparent = true;
InvalidateRect(hWnd, nullptr, true);
}
TCHAR b[99];wsprintf(b,TEXT("%d tick=%d"), transparent, GetTickCount()), SetWindowText(hWnd, b);
break;
It seems those layered window changes take some time to be reflected in the rendering of the window. 这些分层的窗口更改似乎需要一些时间才能反映在窗口的呈现中。 Add a sleep can make the green doesn't show.
添加睡眠可以使绿色不显示。
case WM_MOUSEMOVE:
if (transparent) {
transparent = false;
RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
}
else {
SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
Sleep(1); // Add sleep
transparent = true;
InvalidateRect(hWnd, nullptr, TRUE);
}
break;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.