使用缓冲区的 Windows AlphaBlend

Posted

技术标签:

【中文标题】使用缓冲区的 Windows AlphaBlend【英文标题】:Windows AlphaBlend with buffer usage 【发布时间】:2016-10-05 07:52:50 【问题描述】:

我想在给定窗口内绘制一个缓冲区(带有 alpha 信息!)。绘图是在 WM_PAINT 之外完成的(它是在从 Chromium-Embedded-Framework 调用的 CefRenderHandler::OnPaint 方法中完成的)。

我遇到的问题是:

未清除窗口的旧内容(如果更改缓冲区,我会绘制旧内容并绘制新内容)。 Alpha 通道被错误解释 - 我认为,即使像素具有 Alpha 信息,它也会被绘制,因为它没有 Alpha 信息

这是我目前所拥有的:

OnPaint(...):

HDC screen_dc = GetDC(windowHandle);
RECT rcWin;
GetClientRect(windowHandle, &rcWin);

BITMAPINFO info;
ZeroMemory(&info, sizeof(BITMAPINFO));
info.bmiHeader.biBitCount = 32;
info.bmiHeader.biWidth = width;
info.bmiHeader.biHeight = -height;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biSizeImage = width*height * 4;
info.bmiHeader.biCompression = BI_RGB;

void *buf;
HBITMAP hDib = CreateDIBSection(screen_dc, &info, DIB_RGB_COLORS, (void **)&buf, 0, 0);
memcpy(buf, buffer, width * height * 4); //buffer contains bitmap to draw
HDC hDibDC = CreateCompatibleDC(screen_dc);
HGDIOBJ hOldObj = SelectObject(hDibDC, hDib);
BLENDFUNCTION blendFunction_;
blendFunction.BlendOp = AC_SRC_OVER;
blendFunction.BlendFlags = 0;
blendFunction.SourceConstantAlpha = 255;
blendFunction.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(screen_dc, 0, 0, width, height, hDibDC, 0, 0, width, height, blendFunction);

SelectObject(hDibDC, hOldObj);
ReleaseDC(windowHandle, screen_dc);
DeleteObject(hDib);
DeleteDC(hDibDC);

窗口创建:

WNDCLASSEX wcex = 0;
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = BrowserWindowWndProc;
wcex.hInstance = hinstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = WHITE_BRUSH;
wcex.lpszClassName = BROWSER_WINDOW_CLASS;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
RegisterClassEx(&wcex);

DWORD exStyle0;
exStyle |= WS_EX_TOOLWINDOW;
exStyle |= WS_EX_LAYERED;

DWORD style 0;
style |= WS_SYSMENU;
style |= WS_VISIBLE;

HWND hWnd = CreateWindowEx(
    exStyle,
    BROWSER_WINDOW_CLASS, 
    BROWSER_WINDOW_CLASS,
    style,
    100,
    100,
    300,
    300,
    nullptr,
    nullptr, 
    hinstance,
    nullptr
);
...
SetLayeredWindowAttributes(hWnd, RGB(255, 255, 255), 255, LWA_COLORKEY);

你能帮我解决这些问题吗?

提前谢谢你。

【问题讨论】:

在 OnPaint 中,您应该使用从 BeginPaint 函数获取的 hdc 而不是使用 GetDC。这可能会解决背景内容的问题。至于错误的 alpha 值,您可能需要预乘位图中的 alpha 值,如下所示:fengyuan.com/article/alphablend.html 【参考方案1】:

不必同时使用AlphaBlend 和分层窗口。仅使用分层窗口:

void OnPaint(HDC hdc, int width, int height, HBITMAP hbitmap)

    HDC memdc = CreateCompatibleDC(hdc);
    auto oldbmp = SelectObject(memdc, hbitmap);

    BITMAP bm;
    GetObject(hbitmap, sizeof(bm), &bm);
    BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, memdc, 0, 0, SRCCOPY);

    SelectObject(memdc, oldbmp);
    DeleteDC(memdc);

其中hbitmap 是之前创建的位图的句柄。使用SetLayeredWindowAttributes(hwnd, RGB(255,255,255), 255, LWA_COLORKEY);时,位图的白色区域应该显示为透明的

或使用LWA_COLORKEY | LWA_ALPHA 调整透明度和Alpha 级别。

假设OnPaint 是对WM_PAINT 的响应,请使用BeginPaint/EndPaint 而不是GetDC/ReleaseDC

注意WHITE_BRUSH 为零,所以wcex.hbrBackground = WHITE_BRUSH; 将背景画笔设置为零。改为指定画笔手柄。

您也可以在同一窗口中使用TransparentBlt

HDC memdc = CreateCompatibleDC(hdc);
auto oldbmp = SelectObject(memdc, hbitmap);

BITMAP bm;
GetObject(hbitmap, sizeof(bm), &bm);
TransparentBlt(hdc, 0, 0, width, height, 
    memdc, 0, 0, bm.bmWidth, bm.bmHeight, RGB(255, 255, 255));

SelectObject(memdc, oldbmp);
DeleteDC(memdc);

【讨论】:

是的,它有效。唯一不起作用的是半透明像素(在 hbitmap 内)。但我现在可以离开了。我用过TransparentBlt。我不使用 LWA_ALPHA,因为它对所有像素都透明(我不希望这样)。我在 WM_PAINT 之外进行绘制,所以我需要使用 GetDC/ReleaseDC 这是来自 *.png 文件吗?在有关图像的问题中提供更多信息,如果可能,请上传示例(默认上传会将 *.bmp 转换为 *.png) 不,它不是静态图像,我已经提到,缓冲区来自 Chromium-Embedded-Framework。所以换句话说,它是 chromium 从 html 生成的缓冲区。 如果位图图像具有预乘 alpha,那么您的方法应该可以正常工作。尝试做同样的事情,但不要在分层窗口中。我不认为它确实有预乘 alpha。您可以发布一些关于它的外观和您认为它应该是什么样子的图像,或者保持原样......如果您拥有该窗口,那么您可以在WM_PAINT 中进行绘制,然后发送InvalidateRect 消息以强制重新绘制. 不使用分层窗口会破坏它 - 透明度不起作用。但是,也许这是 CEF/chromium 的问题 - 生成具有这种背景的位图。注意:我不使用 WM_PAINT 或 WM_ERASEBKGND。不管怎样,谢谢大家的帮助

以上是关于使用缓冲区的 Windows AlphaBlend的主要内容,如果未能解决你的问题,请参考以下文章

图像处理:AlphaBlend混合两张图片

如何判断打印机驱动程序是不是支持 GDI AlphaBlend?

MFC-AlphaBlend显示具有指定透明度的图像

在 Windows 中使用 MinGW 的 Google 协议缓冲区

Windows 媒体基础 2D 缓冲区

WASAPI 在 Windows 上捕获的缓冲区大小