2408gui,分层窗口1
原文
前言
分层窗口,功能非常强大,可轻易实现一些比较漂亮的UI接口.
UpdateLayeredWindow函数
UpdateLayeredWindow函数作用是更新一个分层窗口,是分层窗口的核心,定义如下:
//更新分层窗口的位置,大小,形状,内容和透明度
BOOL UpdateLayeredWindow([in] HWND hWnd,[in, optional] HDC hdcDst,[in, optional] POINT *pptDst,[in, optional] SIZE *psize,[in, optional] HDC hdcSrc,[in, optional] POINT *pptSrc,[in] COLORREF crKey,[in, optional] BLENDFUNCTION *pblend,[in] DWORD dwFlags
);
1,hWnd:分层窗口的句柄
2,hdcDst:屏幕DC的句柄
3,pptDst:分层窗口的位置
4,psize:分层窗口的新大小
5,hdcSrc:分层窗口的图面DC的句柄
6,pptSrc:指定层在设环中的位置
7,crKey:指定要在组合分层窗口时使用的颜色键
8,pblend:指定要在组合分层窗口时使用的透明度值
dwFlags:此参数的取值可为下列值之一:
值 | 含义 |
|---|---|
ULW_ALPHA,0x00000002 | 按混合函数使用pblend.如果显示模式为256种或更少颜色,则此值的效果与ULW_OPAQUE的效果相同. |
ULW_COLORKEY,0x00000001 | 按透明度颜色使用crKey. |
ULW_OPAQUE,0x00000004 | 绘画不透明的分层窗口. |
ULW_EX_NORESIZE,0x00000008 | 如果当前窗口大小与psize中指定的大小不匹配,则强制UpdateLayeredWindowIndirect函数失败. |
| 这里 |
首先需要一个分层窗口,为了方便可以先创建一个简单的win32窗口
//注册窗口
{wnd.cbClsExtra = 0;wnd.cbWndExtra = 0;wnd.hbrBackground = (HBRUSH)(GetStockObject(dwColor));wnd.hCursor = LoadCursor(NULL, IDC_ARROW);wnd.hIcon = LoadCursor(NULL, IDI_APPLICATION);wnd.lpfnWndProc = wndProc;wnd.lpszClassName = lpClassName;wnd.lpszMenuName = NULL;wnd.style = CS_HREDRAW;wnd.hInstance = hInstance;RegisterClass(&wnd);
}
//窗口过程函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg,WPARAM wParam, LPARAM lParam)
{switch (Msg){case WM_DESTROY:PostQuitMessage(0);return 0;default:return DefWindowProc(hWnd, Msg, wParam, lParam);}return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE PrevhInstance, LPSTR lpCmdLine, int nCmdShow)
{//按白色背景指定注册窗口RegWindow(hInstance, L"WINDOW", WindowProc, WHITE_BRUSH);//创建窗口HWND hWnd = CreateWindow(L"WINDOW", 0,WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, NULL, NULL, hInstance, NULL);//宽高,显示窗口ShowWindow(hWnd, SW_SHOW);//更新窗口UpdateWindow(hWnd);//消息循环MSG msg;while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}
这样就创建了一个宽和高为300,白色背景的普通窗口.
接着就是创建一个分层窗口,为了方便,可直接在消息循环前,创建.
创建分层窗口要用CreateWindowExW函数,比较简单,直接上代码,这里.
...
//更新窗口
UpdateWindow(hWnd);
//创建分层窗口
//注册分层窗口
RegWindow(hInstance, L"LayeredWindow", WindowProc, BLACK_BRUSH);
//创建分层窗口
HWND hLayeredWindow = CreateWindowEx(WS_EX_LAYERED, L"LayeredWindow", 0,WS_POPUP | WS_BORDER, 0, 0, 100, 100, NULL, NULL, hInstance, NULL);
//将分层窗口设为本窗口的子窗口
SetParent(hLayeredWindow, hWnd);
//消息循环
MSG msg;
...
这样就创建了一个宽和高为100,黑色背景的普通窗口.
但运行程序后会发现并无黑色窗口,这是因为更新分层窗口需要用UpdateLayeredWindow函数,现在应该考虑怎么使用该函数了.
...
//将分层窗口设为本窗口的子窗口
SetParent(hLayeredWindow, hWnd);
//更新分层窗口
//创建分层窗口的`DC`
HDC hLayeredWindowDC = GetDC(hLayeredWindow);
HDC hCompatibleDC = CreateCompatibleDC(hLayeredWindowDC);
//填充`BLENDFUNCTION`结构
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
//控制显示位置
POINT ptDst = { 0, 0 };
//控制窗口大小
SIZE sizeWnd = { 100, 100 };
//为0就行
POINT pSrc = { 0, 0 };
//更新分层窗口
UpdateLayeredWindow(hLayeredWindow, hLayeredWindowDC, &ptDst, &sizeWnd, hCompatibleDC, &pSrc, NULL, &blend, ULW_ALPHA);
//释放`DC`
DeleteDC(hLayeredWindowDC);
DeleteDC(hCompatibleDC);
//消息循环
MSG msg;
...
它的第一个和第二个参数很简单,只需要填分层窗口的句柄和对应的DC就行了,对应的DC可用GetDC函数取.
第三个参数控制分层窗口显示位置,第四个参数控制分层窗口大小,第五个参数为用CreateCompatibleDC函数取的DC,第六个一般填为零的POINT结构,第七个为无效就行,第八个填BLENDFUNCTION结构比较简单,最后参数填ULW_ALPHA,使窗口可设置透明度.
运行程序后发现窗口上并未出现黑色窗口,可用GetLastError函数查看错误码.
...
//为0就行
POINT pSrc = { 0, 0 };
//更新分层窗口
int e1 = GetLastError();
UpdateLayeredWindow(hLayeredWindow, hLayeredWindowDC, &ptDst, &sizeWnd, hCompatibleDC, &pSrc, NULL, &blend, ULW_ALPHA);
int e2 = GetLastError();
//释放`DC`
DeleteDC(hLayeredWindowDC);
...
运行程序后可发现e1的值为0,就可排除在更新分层窗口前错误的可能,e2的值为31,意为"连到系统上的设备没有有效"
SetDIBits函数
为了解决上述问题,要用setDIBits函数设置像素,它的定义如下
int SetDIBits([in] HDC hdc,[in] HBITMAP hbm,[in] UINT start,[in] UINT cLines,[in] const VOID *lpBits,[in] const BITMAPINFO *lpbmi,[in] UINT ColorUse
);
它的功能是使用指定DIB中找到的颜色数据,在兼容位图上设置像素.就是给一个Bitmap填充像素.
第一个参数是设环的句柄,可直接填NULL;第二个参数是一个位图句柄,只需要创建一个位图传进去就行了;第三个参数直接填0,意为从0开始读取并填充;
第四个参数填要显示的窗口的高度,如果以左上角为原点,此值需要为负数;第五个参数是RGB数组的指针,控制每个像素的颜色与透明度,第六个参数是一个BITMAPINFO结构,里面还有一个BITMAPINFOHEADER结构都比较简单,最后参数必须是以下值之一
位图
位图头
| 值 | 含义 |
|---|---|
DIB_PAL_COLORS | 颜色表包含一个由16位索引组成的,在由hdc参数标识的设环的逻辑调色板中包含的数组. |
DIB_RGB_COLORS | 提供颜色表,并包含文本RGB值 |
接着就可直接上代码
...
//为0就行
POINT pSrc = { 0, 0 };
//创建一副与当前`DC`兼容的位图
HBITMAP hCustomBmp = NULL;
hCustomBmp = CreateCompatibleBitmap(hLayeredWindowDC, 100, 100);
//在`hCompatibleDC`中指定`hCustomBmp`
SelectObject(hCompatibleDC, hCustomBmp);
//填充`bmpInfo`
BITMAPINFO bmpInfo = { 0 };
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = 100;
bmpInfo.bmiHeader.biHeight = -(int)100;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biBitCount = 32;
//初化位图
char* datas = new char[100 * 100 * 4];
ZeroMemory(datas, 100 * 100 * 4);
//填充像素
char* pdata = datas;
for (int i = 0; i < 100; i++)
{for (int j = 0; j < 100; j++){//顺序并不是`RGBA`,而是`BGRA`,*pdata = 233; //蓝*(pdata + 1) = 222; //绿*(pdata + 2) = 233; //红*(pdata + 3) = 233; //透pdata += 4;}
}
//设置位图中的像素
SetDIBits(NULL, hCustomBmp, 0, 100, datas, &bmpInfo, DIB_RGB_COLORS);
//更新分层窗口
...
现在运行程序就可在主窗口上,看见一个类似粉色的正方形了.
这样就创建并显示了一个分层窗口.
接着就要开始封装,来方便创建多个分层窗口.
HWND CreateLayeredWindow(HINSTANCE hInstance, HWND hWnd, int iWidth, int iHeight, int iPosX, int iPosY, COLORREF* colBGRA)
{//注册分层窗口RegWindow(hInstance, L"LayeredWindow", WindowProc, BLACK_BRUSH);//创建分层窗口HWND hLayeredWindow = CreateWindowEx(WS_EX_LAYERED, L"LayeredWindow", 0,WS_POPUP | WS_BORDER, 0, 0, iWidth, iHeight, NULL, NULL, hInstance, NULL);//将分层窗口设为本窗口的子窗口SetParent(hLayeredWindow, hWnd);//更新分层窗口//创建分层窗口的`DC`HDC hLayeredWindowDC = GetDC(hLayeredWindow);HDC hCompatibleDC = CreateCompatibleDC(hLayeredWindowDC);//填充`BLENDFUNCTION`结构BLENDFUNCTION blend = { 0 };blend.BlendOp = AC_SRC_OVER;blend.SourceConstantAlpha = 255;blend.AlphaFormat = AC_SRC_ALPHA;//控制显示位置POINT ptDst = { iPosX, iPosY };//控制窗口大小SIZE sizeWnd = { iWidth, iHeight };//为0就行POINT pSrc = { 0, 0 };//创建一副与当前`DC`兼容的位图HBITMAP hCustomBmp = NULL;hCustomBmp = CreateCompatibleBitmap(hLayeredWindowDC, iWidth, iHeight);//指定`hCustomBmp`到`hCompatibleDC`中SelectObject(hCompatibleDC, hCustomBmp);//填充`bmpInfo`BITMAPINFO bmpInfo = { 0 };bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);bmpInfo.bmiHeader.biWidth = iWidth;bmpInfo.bmiHeader.biHeight = iHeight;bmpInfo.bmiHeader.biPlanes = 1;bmpInfo.bmiHeader.biCompression = BI_RGB;bmpInfo.bmiHeader.biBitCount = 32;//设置位图中的像素SetDIBits(NULL, hCustomBmp, 0, iHeight, colBGRA, &bmpInfo, DIB_RGB_COLORS);//更新分层窗口UpdateLayeredWindow(hLayeredWindow, hLayeredWindowDC, &ptDst, &sizeWnd, hCompatibleDC, &pSrc, NULL, &blend, ULW_ALPHA);//释放`DC`DeleteDC(hLayeredWindowDC);DeleteDC(hCompatibleDC);return hLayeredWindow;
}
1,hInstance:实例句柄
2,hWnd:主窗口句柄
3,iWidth/iHeight:窗口宽高
4,iPosX/iPosY:窗口位置
5,colBGRA:窗口每像素点的信息
RGBA与BGRA
因为一些特性,SetDIBits函数中RGB数组的指针指向的内容顺序为BGRA而不是RGBA
所以colBGRA内容顺序必须是BGRA.
如果你到现在都对RGBA和BGRA感觉陌生,则接着的示例也许会让你恍然大悟.
//非`win32`窗口程序
#include <windows.h>
int main()
{//`COLORREF`就是存储`RGBA`的数据类型`α`默认为0一个`rgb`COLORREF rgb = RGB(1,2,3);//`bgr`就是把`rgb`反之COLORREF bgr = RGB(3,2,1);//`colBGRA`就是一个存储`多个像素点`的`BGRA`信息的`COLORREF`数组COLORREF bgrs[9];return 0;
}
RGB宏明显不能满足,因为RGB宏它无法改变透明度,所以就需要一个RGBA宏:
#define RGBA(r,g,b,a) (COLORREF)(((BYTE)(r) |((WORD)((BYTE)(g)) << 8)) |(((DWORD)((BYTE)(b)) << 16)) |(((DWORD)((BYTE)(a)) << 24)))
完整代码:
#include <windows.h>
#define RGBA(r,g,b,a) (COLORREF)(((BYTE)(r) |((WORD)((BYTE)(g)) << 8)) |(((DWORD)((BYTE)(b)) << 16)) |(((DWORD)((BYTE)(a)) << 24)))
//窗口过程函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT Msg,WPARAM wParam, LPARAM lParam)
{switch (Msg){case WM_DESTROY:PostQuitMessage(0);return 0;default:return DefWindowProc(hWnd, Msg, wParam, lParam);}return 0;
}
//注册窗口函数
void RegWindow(HINSTANCE hInstance, LPCWSTR lpClassName, WNDPROC wndProc, DWORD dwColor)
{WNDCLASS wnd;wnd.cbClsExtra = 0;wnd.cbWndExtra = 0;wnd.hbrBackground = (HBRUSH)(GetStockObject(dwColor));wnd.hCursor = LoadCursor(NULL, IDC_ARROW);wnd.hIcon = LoadCursor(NULL, IDI_APPLICATION);wnd.lpfnWndProc = wndProc;wnd.lpszClassName = lpClassName;wnd.lpszMenuName = NULL;wnd.style = CS_HREDRAW;wnd.hInstance = hInstance;RegisterClass(&wnd);
}
//创建分层窗口
HWND CreateLayeredWindow(HINSTANCE hInstance, HWND hWnd, int iWidth, int iHeight, int iPosX, int iPosY, COLORREF* colBGRA)
{//创建分层窗口`//`注册分层窗口RegWindow(hInstance, L"LayeredWindow", WindowProc, BLACK_BRUSH);//创建分层窗口HWND hLayeredWindow = CreateWindowEx(WS_EX_LAYERED, L"LayeredWindow", 0,WS_POPUP | WS_BORDER, 0, 0, iWidth, iHeight, NULL, NULL, hInstance, NULL);//将分层窗口设为本窗口的子窗口SetParent(hLayeredWindow, hWnd);//更新分层窗口`//`创建分层窗口的`DC`HDC hLayeredWindowDC = GetDC(hLayeredWindow);HDC hCompatibleDC = CreateCompatibleDC(hLayeredWindowDC);//填充`BLENDFUNCTION`结构BLENDFUNCTION blend = { 0 };blend.BlendOp = AC_SRC_OVER;blend.SourceConstantAlpha = 255;blend.AlphaFormat = AC_SRC_ALPHA;//控制显示位置POINT ptDst = { iPosX, iPosY };//控制窗口大小SIZE sizeWnd = { iWidth, iHeight };//为0就行POINT pSrc = { 0, 0 };//创建一副与当前`DC`兼容的位图HBITMAP hCustomBmp = NULL;hCustomBmp = CreateCompatibleBitmap(hLayeredWindowDC, iWidth, iHeight);//指定`hCustomBmp`到`hCompatibleDC`中SelectObject(hCompatibleDC, hCustomBmp);//填充`bmpInfo`BITMAPINFO bmpInfo = { 0 };bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);bmpInfo.bmiHeader.biWidth = iWidth;bmpInfo.bmiHeader.biHeight = iHeight;bmpInfo.bmiHeader.biPlanes = 1;bmpInfo.bmiHeader.biCompression = BI_RGB;bmpInfo.bmiHeader.biBitCount = 32;//设置位图中的像素SetDIBits(NULL, hCustomBmp, 0, iHeight, colRGBA, &bmpInfo, DIB_RGB_COLORS);//更新分层窗口UpdateLayeredWindow(hLayeredWindow, hLayeredWindowDC, &ptDst, &sizeWnd, hCompatibleDC, &pSrc, NULL, &blend, ULW_ALPHA);//释放`DC`DeleteDC(hLayeredWindowDC);DeleteDC(hCompatibleDC);return hLayeredWindow;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE PrevhInstance, LPSTR lpCmdLine, int nCmdShow)
{//注册窗口RegWindow(hInstance, L"WINDOW", WindowProc, WHITE_BRUSH);//创建窗口HWND hWnd = CreateWindow(L"WINDOW", 0,WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, NULL, NULL, hInstance, NULL);//显示窗口ShowWindow(hWnd, SW_SHOW);//更新窗口UpdateWindow(hWnd);//创建`BGRA`数据COLORREF* colBGRA = new COLORREF[100*100];//初化ZeroMemory(colBGRA, 100 * 100 * sizeof(COLORREF));//填充半透明蓝色for (int i = 0; i < 100*100; i++){//虽然用的是`RGBA`,但实际上是`BGRA`colBGRA[i] = RGBA(233, 0, 0, 128);//`BGRA`}//创建分层窗口HWND hLW = CreateLayeredWindow(hInstance, hWnd, 100, 100, 0, 0, colBGRA);//消息循环MSG msg;while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return 0;
}
