IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]VC++玩转炫酷悬浮窗3---GDI+完美实现不规则窗体

    lincyang发表于 2014-09-05 11:09:48
    love 0
    

    目标
    上一篇不规则窗体虽然实现了,但是图形有锯齿,给人以上世纪的老古董感觉,跟酷炫不搭边。今天就要用高级一些的技术做出完美的光滑的无锯齿的不规则窗体。
    计划&方案
    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









沪ICP备19023445号-2号
友情链接