windows上半透明png窗口实现示例

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();
};

Leave a comment

Your email address will not be published. Required fields are marked *