将 HBITMAP 绘制到分层窗口上。怎么了?
Posted
技术标签:
【中文标题】将 HBITMAP 绘制到分层窗口上。怎么了?【英文标题】:Draw HBITMAP onto layered window. What's wrong? 【发布时间】:2012-09-27 17:14:24 【问题描述】:大家好,大家好,
我的最终目标是在屏幕上绘制一个包含 alpha 的 PNG 文件 - 这意味着不是在自己的窗口中,而是在桌面上的某个地方。 将 PNG 加载到 HBITMAP 中的部分现在可以工作(以不同的方式进行测试),但我无法将它包括 alpha 来绘制。
据我所知,最好的方法是使用 alyered windows。所以我做了很多工作来重做几个例子和小教程。
以下代码编译没有问题,也没有提示任何消息(这意味着永远不会调用 showError("#") 函数)。
然而屏幕上没有任何东西可见:/
抱歉这么长...希望有人愿意至少快点看一下..
LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);
int main(HINSTANCE hInstance)
WNDCLASSEX WndClass;
char sClassName[] = "mainClass";
WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;//WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = NULL;
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = sClassName;
WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (RegisterClassEx(&WndClass) == 0) showError("-1");
HWND screen = CreateWindowEx(WS_EX_LAYERED,//WS_EX_LEFT
"mainClass",
"UpdateLayeredWind",
WS_DISABLED | WS_VISIBLE,
200,200,260,260,
NULL /*eventuelly, GM window*/,
NULL,
hInstance,
NULL);
if (screen == NULL) showError("0");
HBITMAP img = LoadImageResource("D://ThreadDraw/ThreadDraw-test/ThreadDraw/test.png");
if (img == NULL) showError("1");
BLENDFUNCTION blend = 0;
blend.AlphaFormat = AC_SRC_ALPHA;
blend.SourceConstantAlpha = 155;
POINT ptPos = 200,300;
SIZE sizeWnd = 260,260;
POINT ptPos2 = 200,300;
ShowWindow(screen, SW_SHOW);
while (1)
PAINTSTRUCT ps;
HDC hdc;
BITMAP bitmap;
HDC hdcMem;
HGDIOBJ oldBitmap;
hdc = BeginPaint(screen, &ps);
hdcMem = CreateCompatibleDC(hdc);
oldBitmap = SelectObject(hdcMem, img);
GetObject(img, sizeof(bitmap), &bitmap);
if (SetLayout(hdc,LAYOUT_RTL) == GDI_ERROR)
showError("5");
if (!BitBlt(hdc, 0, 0, 64, 64, hdcMem, 0, 0, SRCCOPY))
showError("4");
if (!UpdateLayeredWindow(screen,hdcMem,&ptPos,&sizeWnd,hdc,&ptPos2,RGB(255,255,255),&blend,ULW_ALPHA))//ULW_OPAQUE))
showError("2");
EndPaint(screen, &ps);
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
Sleep(10);
return 0;
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
switch(Message)
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
return 0;
顺便说一句,如果我在 UpdateLayeredWindow 中使用 ULW_OPAQUE 而不是 ULW_ALPHA,则会出现一个大小合适的黑色窗口,因此认为问题必须与 PAINTSTRUKT 或 BitBlt 函数相关。但我尝试了很多方法没有任何变化。
希望有人可以提供帮助。非常感谢您!
【问题讨论】:
【参考方案1】:这大多是错误的。您的代码应该:
使用CreateWindowEx
创建分层窗口。
使用UpdateLayeredWindow
附加位图。
显示带有ShowWindow
的窗口。 Windows 将负责绘制分层窗口,因此您无需处理 WM_PAINT
或调用 BeginPaint
。
进入消息循环。
就是这样。
如果您使用的是 Visual Studio,请创建一个新的 Win32 项目,它将为您创建一个带有消息循环的新项目。
更新
这是一个创建透明分层窗口的示例程序。它需要一个函数来将 PNG 作为透明位图加载。而且它没有错误检查。
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
LPCTSTR szWindowClass = _T("TransparentClass");
// Register class
WNDCLASSEX wcex = 0;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = DefWindowProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = szWindowClass;
RegisterClassEx(&wcex);
HWND hWnd = CreateWindowEx(WS_EX_LAYERED, szWindowClass, 0, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
int width;
int height;
HBITMAP hbmp = LoadPng(L"sample.png", &width, &height);
HDC hdcScreen = GetDC(0);
HDC hdc = CreateCompatibleDC(hdcScreen);
ReleaseDC(0, hdcScreen);
HBITMAP hbmpold = (HBITMAP)SelectObject(hdc, hbmp);
POINT dcOffset = 0, 0;
SIZE size = width, height;
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
UpdateLayeredWindow(hWnd, 0, 0, &size, hdc, &dcOffset, 0, &bf, ULW_ALPHA);
SelectObject(hdc, hbmpold);
DeleteDC(hdc);
DeleteObject(hbmp);
ShowWindow(hWnd, SW_SHOW);
MSG msg;
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
TranslateMessage(&msg);
DispatchMessage(&msg);
return (int)msg.wParam;
另一个更新
这里有一些代码将红色、绿色和蓝色值预乘以 alpha。它假定splash_image
指向大小为width*height
的32bpp ARGB 数据。
LPBYTE bits = (LPBYTE)splash_image;
int size = width * height;
for (int pixel = 0; pixel != size; ++pixel)
bits[0] = bits[0] * bits[3] / 255;
bits[1] = bits[1] * bits[3] / 255;
bits[2] = bits[2] * bits[3] / 255;
bits += 4;
【讨论】:
非常感谢您的耐心等待!我担心它并没有真正帮助我。我已经尝试了很多有和没有循环的方法以及不同的顺序..没有任何成功。尤其是附件部分对我来说很难得到。这是正确的方法吗? HDC hdcMem = CreateCompatibleDC(GetDC(screen)); HGDIOBJ oldBitmap = SelectObject(hdcMem, img); UpdateLayeredWindow(screen,NULL,&ptPos,&sizeWnd,hdcMem,&ptPos2,RGB(255,255,255),&blend,ULW_ALPHA);我在哪里可以更准确地了解这一点?另外,请注意我正在编写一个 DLL,因此我真的不需要消息循环.. 同意,如果您在 DLL 中,您可以省去消息循环,但您绝对不想要无限循环。我已经用一个完整的程序更新了我的答案,除了加载 PNG 的代码。 再次非常非常感谢您!我想我现在明白了,而且效果很好!嗯..几乎。有一个尴尬的问题。即使完全不透明似乎也显示为透明(bf.SourceConstantAlpha 为 255).. 或者更确切地说与背景混合。在黑暗的背景下,它看起来应该如此。在白色背景上,颜色几乎消失了。可能是什么问题?这不是加载脚本,因为我还尝试了内置函数,方法是编写 HBITMAP hbmp = (HBITMAP)LoadImage(...) 来加载一个简单的 BMP 文件——结果相同——>图像在明亮的背景下是透明的... GDI 大多不理解透明度。如果您使用 LoadImage 加载不透明位图,它不一定正确设置透明度(不透明),因为 GDI 假定您将使用不关心透明度的功能的图像。这段代码对我来说运行良好,表明问题出在您的源 PNG 或加载它的函数上。请注意,UpdateLayeredWindow 需要一个带有预乘 alpha 的位图。 我已经想到了这种可能性,现在我尝试了一个 rpemultiplied PNG_file(我有一个工具),现在颜色似乎更正确了。但仅限于 black 背景!背景越亮,图片越透明!是什么原因造成的?听起来 HBITMAP 本身不会被错误加载...这不会发生在您的计算机上吗?以上是关于将 HBITMAP 绘制到分层窗口上。怎么了?的主要内容,如果未能解决你的问题,请参考以下文章
用C++在Win32中用LoadImage()绘制HBITMAP的二维数组
如何通过 Bitmap::GetHBITMAP 将位图转换为带有 alpha 的 HBITMAP?