目标
上一篇不规则窗体虽然实现了,但是图形有锯齿,给人以上世纪的老古董感觉,跟酷炫不搭边。今天就要用高级一些的技术做出完美的光滑的无锯齿的不规则窗体。
计划&方案
PNG图片本身就是带透明效果的,把此图片作为窗体,用GDI+将其实现。
那么什么是GDI+呢?先要说一说GDI, Graphics Devices Interface,图形设备接口,负责系统与绘图程序之间的信息交换,处理所有Windows图形程序的输出。而GDI+是其增强版,xp时代是其一个子系统,负责在显示屏幕和打印设备输出信息。程序员根据其提供的众多函数来实现图形程序编程,不用关心图形硬件的实现细节。
正所谓:任凭弱水三千,我只取一瓢饮。这是我第一次接触GDI+,只是在程序中用到了一些函数,照猫画虎会用几个函数而已。
本文的重点函数是:UpdateLayeredWindow,
BOOL WINAPI UpdateLayeredWindow( _In_ HWND hwnd,//窗口句柄 _In_opt_ HDC hdcDst,//当前窗口HDC _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 );
请参看官网:http://msdn.microsoft.com/en-us/library/windows/desktop/ms633556(v=vs.85).aspx
实践
一、 GDI+在VS2012上的配置
VS2012上已经有了GDI+支持,不用单独下载安装包了。现在只需项目中引入gdiplus.lib和加入头文件即可。Properties-Configuration Properties -Linker - Input - Additional Dependencies 加入“gdiplus.lib”。
为了在全工程使用gdi+,在stdafx.h中加入头文件的包含和使用命名空间。
#include "gdiplus.h" using namespace Gdiplus;
二、 GDI+的初始化
//在应用初始化时,启动gdi+ BOOL CXXXApp::InitInstance() { ... //use GDIplus begin GdiplusStartupInput gdiplusStartupInput; GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL); //use GDIplus end ... } //在程序退出时,关闭gdi+ int CXXXApp::ExitInstance() { //close gdiplus environment GdiplusShutdown(m_gdiplusToken); return CWinApp::ExitInstance(); }
三、 将png图片加入到项目资源
当我们在Resource界面添加资源时,Add Resource界面只有Icon和Bitmap,而没有PNG可选,不要理它,我们只管Import就好了,你会发现加入PNG图片后,PNG文件夹就神奇的在资源界面出现了。
四、 将图片载入到内存
借用一个很酷的函数,通过它将各种格式的图片载入到内存中。如下:
///resurceID: resource ID ///imgType: type of image ///pImg: pointer to image BOOL CAfloatWindowDlg::ImageFromIDResource(UINT resurceID,LPCTSTR imgType,Image * &pImg) { HINSTANCE hInst = AfxGetResourceHandle(); HRSRC hRsrc = ::FindResource (hInst,MAKEINTRESOURCE(resurceID),imgType); // type if (hRsrc) { // load resource into memory DWORD len = SizeofResource(hInst, hRsrc); BYTE* lpRsrc = (BYTE*)LoadResource(hInst, hRsrc); if (lpRsrc) { // Allocate global memory on which to create stream HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len); BYTE* pmem = (BYTE*)GlobalLock(m_hMem); memcpy(pmem,lpRsrc,len); IStream* pstm; CreateStreamOnHGlobal(m_hMem,FALSE,&pstm); // load from stream pImg=Gdiplus::Image::FromStream(pstm); // free/release stuff GlobalUnlock(m_hMem); pstm->Release(); FreeResource(lpRsrc); return TRUE; } } return FALSE; }
五、 将窗口背景换成你的图片
参考了蝴蝶钟的源码,最重要的是看UpdateLayeredWindow的使用。
BOOL CAfloatWindowDlg::UpdateDisplay(Image *image, int Transparent) { int imageWidth = 240; int imageHeight = 240;//magic number of my image width and height HDC hdcTemp=GetDC()->m_hDC; m_hdcMemory=CreateCompatibleDC(hdcTemp); HBITMAP hBitMap=CreateCompatibleBitmap(hdcTemp,imageWidth,imageHeight); SelectObject(m_hdcMemory,hBitMap); if(Transparent<0||Transparent>100) Transparent=100; m_Blend.SourceConstantAlpha=int(Transparent*2.55);//1~255, if you want to change transparent, modify this. HDC hdcScreen=::GetDC (m_hWnd); RECT rct; GetWindowRect(&rct); POINT ptWinPos={rct.left,rct.top}; Graphics graph(m_hdcMemory); Point points[] = { Point(0, 0), Point(imageWidth, 0), //width Point(0, imageHeight)//height }; graph.DrawImage(image,points,3);//Do it! SIZE sizeWindow={imageWidth,imageHeight}; POINT ptSrc={0,0}; DWORD dwExStyle=GetWindowLong(m_hWnd,GWL_EXSTYLE); if((dwExStyle&0x80000)!=0x80000) SetWindowLong(m_hWnd,GWL_EXSTYLE,dwExStyle^0x80000); BOOL bRet=FALSE; bRet= UpdateLayeredWindow( m_hWnd,hdcScreen,&ptWinPos, &sizeWindow,m_hdcMemory,&ptSrc,0,&m_Blend,2); graph.ReleaseHDC(m_hdcMemory); ::ReleaseDC(m_hWnd,hdcScreen); hdcScreen=NULL; ::ReleaseDC(m_hWnd,hdcTemp); hdcTemp=NULL; DeleteObject(hBitMap); DeleteDC(m_hdcMemory); m_hdcMemory=NULL; return bRet; }
六、 结束
至此我们的事例就完成了重要的功能,运行结果如下图。
锯齿什么的,不用担心了。
源码在此,并增加了动画。请参考。
参考:
《GDI+ 透明窗口.UpdateLayeredWindow》 http://blog.csdn.net/zdl1016/article/details/3298744
《GDI+编程小结》 http://blog.csdn.net/byxdaz/article/details/5972759