Windows上实现异形窗口有多种形式,比如SetWindowRgn方式,UpdateLayeredWindow方式,DirectX方式等,这儿要介绍的是最简单但最有效的UpdateLayeredWindow方式。
UpdateLayeredWindow可以将一个位图用alpha混合的方式更新到屏幕上,让Layered window具有半透明和异形的效果。
UpdateLayeredWindow函数定义如下:
BOOL UpdateLayeredWindow( HWND hwnd, // layered窗口句柄,窗口必须有WS_EX_LAYERED样式 HDC hdcDst, // 屏幕DC,可以通过GetDC(NULL)取得 POINT *pptDst, // layered窗口要移动到的位置 SIZE *psize, // 设置layered窗口的大小 HDC hdcSrc, // 源DC,类似BitBlt的源DC POINT *pptSrc, // 要画到layered窗口上的源DC上的起点 COLORREF crKey, // 要作为透明的颜色,这个参数针对位图很有效 BLENDFUNCTION *pblend, // 混合模式,指定alphaFormat为AC_SRC_ALPHA,就使用alpha混合方式。 DWORD dwFlags // 标志使用哪种混合模式,可以是ULW_ALPHA 表示使用alpha混合,ULW_OPAQUE表示不使用alpha混合,ULW_COLORKEY表示指定的颜色为透明色。 );
其实只要这个函数调用正确,透明窗口就实现了,下面是实现的一个透明窗口示例,用的是筱筱姑娘最喜欢的阿狸作为透明窗口主角。
文章中给出类的定义说明,示例程序和源码通过这里下载
#include <Windows.h> #include <GdiPlus.h> using namespace Gdiplus; /** * 透明窗口Sprite类,使用GDI+绘制PNG半透明窗口,Sprite维护一组图像和自身的坐标,可以更换绘制的图像 * 使用UpdateLayeredWindow方式绘画layered window示例. * @author Hoverlees http://www.hoverlee.com * 文章地址 <a href="http://www.hoverlees.com/blog/?p=1122">http://www.hoverlees.com/blog/?p=1122</a> */ class WSprite{ private: /*透明窗口类名*/ static const char* className; /*GDI+ token*/ static ULONG_PTR gdiToken; /*透明窗口句柄,一个对象维护一个透明窗口*/ HWND spriteWindow; /*应用程序句柄*/ HINSTANCE hInstance; /*透明窗口宽度*/ unsigned int spriteWidth; /*透明窗口高度*/ unsigned int spriteHeight; /*透明窗口缓冲设备上下文*/ HDC hDCWindow; /*屏幕设备上下文*/ HDC hDCScreen; /*透明窗口缓冲位图*/ HBITMAP hWindowBmp; /*图像列表,一个图像作为一帧*/ Image** frames; /*总帧数*/ unsigned int totalFrame; /*当前帧*/ unsigned int currentFrame; /*Sprite的x坐标,相对于屏幕的左上方*/ int x; /*Sprite的y坐标*/ int y; /** * 注册Sprite窗口类,该函数会判断该窗口类是不是已经注册,如果已注册就不重复注册 */ void registerWindowClass(); /** * 画出当前帧到layered window上。 */ void drawFrame(); public: /** * Sprite窗口回调函数,符合WindowProc函数格式 */ static LRESULT CALLBACK SpriteWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam); /** * 初始化gdi+,程序启动时调用 */ static void gdiInit(); /** * 释放gdi+ */ static void gdiUninit(); /** * 创建一个新的WSprite对象 * @param hInstance 应用程序句柄 * @param spriteWidth Sprite窗口宽度 * @param spriteHeight Sprite窗口高度 */ WSprite(HINSTANCE hInstance,unsigned int spriteWidth,unsigned int spriteHeight); ~WSprite(); /** * 从资源中加载一张图像,由于Bitmap类的FromResource只针对RT_BITMAP资源,这个函数弥补了它的不足,可以加载任何资源 * @param rcName 资源名,可以用MKINTRESOURCE * @param rcType 资源类型,可以是整数或字符串 * @return 加载到的图像指针 */ Image* loadImageFromResource(LPCTSTR rcName,LPCTSTR rcType); /** * 重绘对象,如果在换了帧和移动了对象,应该调用一次绘画 */ void paint(); /** * 显示对象 */ void show(); /** * 隐藏对象 */ void hide(); /** * 设置对象的帧图像 * @param frames 帧图像数组 * @param totalFrame 总帧数 * @param initFrame 初始帧 */ void setFrameImages(Image* frames[],unsigned int totalFrame,unsigned int initFrame=1); /** * 设置Sprite在屏幕上的坐标 * @param x x坐标 * @param y y坐标 */ void setPosition(int x,int y); /*获取Sprite坐标*/ int getX(); int getY(); /** * 移动Sprite,相对于当前的位置 * @param dx 横向移动值 * @param dy 纵向移动值 */ void move(int dx,int dy); /** * 判断Sprite是否可见 * @return 窗口可见为true,不可见为false */ bool isVisible(); /** * 跳转到指定帧,跳转后需要调用paint更新窗口。 * @param 跳转到的帧数 * @return 跳转成功返回true.如果没有指定帧,则返回false. */ bool gotoFrame(unsigned int frame); /** * 跳转到下一帧,如果当前是最后一帧,则跳转到第一帧,跳转后需要调用paint更新窗口。 */ void nextFrame(); /** * 跳转到上一帧,如果当前是第一帧,则跳转到最后一帧,跳转后需要调用paint更新窗口。 */ void prevFrame(); /** * 取得当前所在帧 * @return 当前帧 */ int getCurrentFrame(); };