上一篇文章介绍的是使用SetLayeredWindowAttributes
API来实现异型窗口,但是很明显效果很差劲,在剔除颜色的过程中凹凸边缘区域的像素点会剔除的非常不明显,在查找信息之后,得出的说法是当显示屏在显示图像的时候在颜色重叠的地方会产生颜色色差,而SetLayeredWindowAttributes 又是从屏幕中取出dc颜色的!
但这次我们使用UpdateLayeredWindow方法来将PNG图像直接加载到窗口上!
思路:
使用gdi加载png图片,然后使用UpdateLayeredWindow加载到窗口上!
分层窗口的概念可以参考我的上一篇文章里的介绍!
这里先看一下运行效果:
API介绍:
UpdateLayeredWindow
函数原型:
BOOL WINAPI UpdateLayeredWindow(
_In_ HWND hwnd,
_In_opt_ HDC hdcDst,
_In_opt_ POINT *pptDst,
_In_opt_ SIZE *psize,
_In_opt_ HDC hdcSrc,
_In_opt_ POINT *pptSrc,
_In_ COLORREF crKey,
_In_opt_ BLENDFUNCTION *pblend,
_In_ DWORD dwFlags
);
参数介绍:
参数 |
类型 |
说明 |
hwnd |
一个分层窗口的句柄。分层窗口在用CreateWindowEx函数创建窗口时应指定WS_EX_LAYERED扩展样式。 Windows 8: WS_EX_LAYERED扩展样式支持顶级窗口和子窗口。之前的Windows版本中WS_EX_LAYERED扩展样式仅支持顶级窗口 |
|
hdcDst |
屏幕的设备上下文(DC)句柄。如果指定为NULL,那么将会在调用函数时自己获得。它用来在窗口内容更新时与调色板颜色匹配。如果hdcDst为NULL,将会使用默认调色板。 如果hdcSrc指定为NULL,那么hdcDst必须指定为NULL。 |
|
pptDst |
POINT* |
指向分层窗口相对于屏幕的位置的POINT结构的指针。如果保持当前位置不变,pptDst可初始化为0,0。 |
psize |
SIZE* |
指向分层窗口的大小的SIZE结构的指针。如果窗口的大小保持不变,psize可以初始化为0。如果hdcSrc指定为NULL,psize必须指定为NULL。 |
hdcSrc |
分层窗口绘图表面的设备上下文句柄。这个句柄可以通过调用函数CreateCompatibleDC获得。如果窗口的形状和可视范围保持不变,hdcSrc可以指定为NULL。 |
|
pptSrc |
POINT* |
指向分层窗口绘图表面在设备上下文位置的POINT结构的指针。如果hdcSrc指定为NULL,pptSrc就应该指定为NULL。 |
crKey |
指定合成分层窗口时使用的颜色值。要生成一个类型为COLORREF的值,使用RGB宏。 |
|
pblend |
指向指定合成分层窗口时使用的透明度结构的指针。 |
|
dwFlags |
可以是以下值之一。 如果hdcSrc指定为NULL,dwFlags应该指定为0。 |
值 |
意义 |
ULW_ALPHA 0x00000002 |
使用参数pblend作为混合函数,如果显示模式为256色或低于256色,使用这个值实现的效果和使用ULW_OPAQUE的效果相同。 |
ULW_COLORKEY 0x00000001 |
使用参数crKey值作为透明颜色。 |
ULW_OPAQUE 0x00000004 |
绘制一个不透明的分层窗口。 |
示列代码:
#include <GdiPlus.h>
using namespace Gdiplus;
//导入GDI+的库
#pragma comment(lib, "Gdiplus.lib")
void setWindow(HWND hWnd /*窗口句柄,必须具有WS_EX_LAYERED扩展风格*/) {
//初始化gdi+
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
//定义png的高与宽
int img_h = 256;
int img_w = 256;
//申请内存dc与程序dc关联
HDC hDC = ::GetDC(hWnd);
HDC hMemDC = ::CreateCompatibleDC(hDC);
//申请一个位图布局
BITMAPINFO bitmapinfo;
bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapinfo.bmiHeader.biBitCount = 32;
bitmapinfo.bmiHeader.biHeight = img_h;
bitmapinfo.bmiHeader.biWidth = img_w;
bitmapinfo.bmiHeader.biPlanes = 1;
bitmapinfo.bmiHeader.biCompression = BI_RGB;
bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
bitmapinfo.bmiHeader.biClrUsed = 0;
bitmapinfo.bmiHeader.biClrImportant = 0;
bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * bitmapinfo.bmiHeader.biBitCount / 8;
//创建内存布局
HBITMAP hBitmap = CreateDIBSection(hMemDC, &bitmapinfo, 0, NULL, 0, 0);
//选入内存
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
//加载png图像
Gdiplus::Image* pImage = Gdiplus::Image::FromFile(L"d:\\1.png");
//让gdi与内存dc关联,将gdi里的数据加载到内存位图里
Graphics g(hMemDC);
//设置内存位图要显示的区域,注意gdi会关联到内存位图里
g.DrawImage(pImage, RectF(0, 0, img_w, img_h), 0, 0, img_w, img_h, UnitPixel, NULL, NULL, NULL);
delete pImage; //删除gdi
//设置要显示的坐标
Point DestPt(0, 0);
Size psize(img_w, img_h);
//位图信息
BLENDFUNCTION blendFunc32bpp;
blendFunc32bpp.AlphaFormat = AC_SRC_ALPHA;
blendFunc32bpp.BlendFlags = 0;
blendFunc32bpp.BlendOp = AC_SRC_OVER;
blendFunc32bpp.SourceConstantAlpha = 255;
//update更新到分层窗口上
UpdateLayeredWindow(hWnd, hDC, NULL, (SIZE*)&psize, hMemDC, (POINT*)&DestPt, NULL, &blendFunc32bpp, ULW_ALPHA);
ShowWindow(hWnd, SW_SHOW);
}
添加到窗口里的完整代码:
#include <windows.h>
HWND hWnd; //progman
//消息函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//判断消息ID
switch (uMsg) {
case WM_DESTROY: // 窗口销毁消息
PostQuitMessage(0); // 发送退出消息
return 0;
}
// 其他的消息调用缺省的消息处理程序
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
// 3、注册窗口类型
BOOL RegisterWindow(LPCSTR lpcWndName, HINSTANCE hInstance)
{
ATOM nAtom = 0;
// 构造创建窗口参数
WNDCLASS wndClass = { 0 };
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WindowProc; // 指向窗口过程函数
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = NULL;
wndClass.hCursor = NULL;
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = lpcWndName; // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
nAtom = RegisterClass(&wndClass);
return TRUE;
}
//创建窗口(lpClassName 一定是已经注册过的窗口类型)
HWND CreateMyWindow(LPCTSTR lpClassName, HINSTANCE hInstance)
{
HWND hWnd = NULL;
// 创建窗口
hWnd = CreateWindowEx(WS_EX_LAYERED,lpClassName, "test", WS_OVERLAPPEDWINDOW^WS_THICKFRAME, 0, 0, 1000, 800, NULL, NULL, hInstance, NULL);
return hWnd;
}
//异型窗口开始
#include <GdiPlus.h>
using namespace Gdiplus;
//导入GDI+的库
#pragma comment(lib, "Gdiplus.lib")
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
void DisplayMyWnd(HWND hWnd)
{
//初始化gdi+
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
//定义png的高与宽
int img_h = 256;
int img_w = 256;
//申请内存dc与程序dc关联
HDC hDC = ::GetDC(hWnd);
HDC hMemDC = ::CreateCompatibleDC(hDC);
//申请一个位图布局
BITMAPINFO bitmapinfo;
bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapinfo.bmiHeader.biBitCount = 32;
bitmapinfo.bmiHeader.biHeight = img_h;
bitmapinfo.bmiHeader.biWidth = img_w;
bitmapinfo.bmiHeader.biPlanes = 1;
bitmapinfo.bmiHeader.biCompression = BI_RGB;
bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
bitmapinfo.bmiHeader.biYPelsPerMeter = 0;
bitmapinfo.bmiHeader.biClrUsed = 0;
bitmapinfo.bmiHeader.biClrImportant = 0;
bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * bitmapinfo.bmiHeader.biBitCount / 8;
//创建内存布局
HBITMAP hBitmap = CreateDIBSection(hMemDC, &bitmapinfo, 0, NULL, 0, 0);
//选入内存
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
//加载png图像
Gdiplus::Image* pImage = Gdiplus::Image::FromFile(L"d:\\1.png");
//让gdi与内存dc关联,将gdi里的数据加载到内存位图里
Graphics g(hMemDC);
//设置内存位图要显示的区域,注意gdi会关联到内存位图里
g.DrawImage(pImage, RectF(0, 0, img_w, img_h), 0, 0, img_w, img_h, UnitPixel, NULL, NULL, NULL);
delete pImage; //删除gdi
//设置要显示的坐标
Point DestPt(0, 0);
Size psize(img_w, img_h);
//位图信息
BLENDFUNCTION blendFunc32bpp;
blendFunc32bpp.AlphaFormat = AC_SRC_ALPHA;
blendFunc32bpp.BlendFlags = 0;
blendFunc32bpp.BlendOp = AC_SRC_OVER;
blendFunc32bpp.SourceConstantAlpha = 255;
//update更新到分层窗口上
UpdateLayeredWindow(hWnd, hDC, NULL, (SIZE*)&psize, hMemDC, (POINT*)&DestPt, NULL, &blendFunc32bpp, ULW_ALPHA);
//窗口中心显示
int scrWidth = GetSystemMetrics(SM_CXSCREEN);
int scrHeight = GetSystemMetrics(SM_CYSCREEN);
RECT rect;
GetWindowRect(hWnd, &rect);
ShowWindow(hWnd, SW_SHOW);
//重新设置rect里的值
rect.left = (scrWidth - rect.right) / 2;
rect.top = (scrHeight - rect.bottom) / 2;
//移动窗口到指定的位置
SetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);
//settxt(editHwnd,"dasdas");
//char *str = gettxt(editHwnd);
//MessageBox(NULL, str, 0, 0);
UpdateWindow(hWnd);
}
void doMessage() // 消息循环处理函数
{
MSG msg = { 0 };
// 获取消息
while (GetMessage(&msg, NULL, 0, 0)) // 当接收到WM_QIUT消息时,GetMessage函数返回0,结束循环
{
TranslateMessage(&msg);
DispatchMessage(&msg); // 派发消息,到WindowPro函数处理
}
}
// 入口函数
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
LPCTSTR lpClassName = "MyWnd"; // 注册窗口的名称
RegisterWindow(lpClassName, hInstance);
hWnd = CreateMyWindow(lpClassName, hInstance);
DisplayMyWnd(hWnd);
doMessage();
return 0;
}