程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 在VC6.0中用GDI+調用png圖片實現半透明漸變的特效窗口

在VC6.0中用GDI+調用png圖片實現半透明漸變的特效窗口

編輯:關於VC++

一、概述

GDI+的應用使得平面圖形圖象編程變的更加容易,本文以一個基於對話框的時鐘程序為例,在VC6.0中調用*.png圖片實現半透明漸變窗口,該程序實現了指針式和數字式兩種時鐘顯示方式。窗口實現了半透明漸變窗口、窗口拖動無移動矩形框、隱藏了任務欄窗體按鈕等。

效果圖如下:

圖一 程序執行後與WindowXP桌面背景效果圖

二、准備工作

1、圖片資源准備工作。首先在Photoshop中編輯好時鐘的背景、時針、分針以及數字時鐘顯示方式的所有圖片,如下圖:將這些圖片保存成為帶透明通道的.png格式(GDI+調用顯示時能夠透明調背景)。這樣程序中圖片資源就准備好了。

2、下面開始做好在VC6.0下展開此項工作的基本准備工作。

(1)、下載gdiplus forVC6.0的SDK,(總共兩兆多)

(2)、在C盤建立文件夾“GDI+”將開發包拷貝在裡面,亦即建立如下路徑,以便例子代碼順利編譯(當然你可以放到任意你喜歡的地方,只要在你的Project中正確包含路徑即可!)。

C:\GDI+\Includes
C:\GDI+\Lib
C:\GDI+\gdiplus.dll

(3)在stdAfx.h中添加對GDI+環境的設置

#define UNICODE
#ifndef ULONG_PTR
#define ULONG_PTR unsigned long*
#endif
#include "c:\gdi+\includes\gdiplus.h" ////請修改為你的頭文件路徑
using namespace Gdiplus;
#pragma comment(lib, "c:\\gdi+\\lib\\gdiplus.lib") ////請修改為你的.lib文件路徑

(4)在GDIPClock.cpp中編輯app的InitInstance()中添加如下代碼進行GDI+的初始化工作

GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
......
//在對話框程序結束後
//關閉gdiplus的環境
GdiplusShutdown(gdiplusToken);

三、程序的實現全過程

1、建立一個基於對話框的Project,這裡的名稱為GDIPClock

2、在GDIPClockDlg.h中定義所有類成員變量,包括所有圖片的指針和圖片的長寬尺寸信息。

    Image *m_pImageClock;
    Image *m_pImageClock1;
    Image *m_pImageHHour;
    Image *m_pImageHMinu;
    Image *m_pImageHSec;
    Image *m_pImageNum;
    int m_BakWidth , m_BakHeight ;
    int m_HourWidth, m_HourHeight;
    int m_MinuWidth , m_MinuHeight;
    int m_SecWidth , m_SecHeight ;
    HINSTANCE hFuncInst ;
    Typedef BOOL (WINAPI*MYFUNC)(HWND,HDC,POINT*,SIZE*,HDC,POINT*,
COLORREF,BLENDFUNCTION*,DWORD);     
    MYFUNC UpdateLayeredWindow;

在這一步中需要特別說明的是,在創建透明窗口式需要調用一個Windows API函數UpdateLayeredWindow(),該函數在.net以上的版本的SDK中有申明,但是在VC6.0下要調用要麼下載200多兆的高版本SDK,要麼從動態鏈接庫“User32.dll”中調用,這裡選擇從“User32.dll”中調用。以上定義中後三項就是為此作准備的。

3、在對話框的OnCreate()中添加如下代碼:對2的函數和成員變量進行初始化!(其中ImageFromIDResource()函數為從資源中載入Png圖像的一個方法!)

int CGDIPClockDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDialog::OnCreate(lpCreateStruct) == -1)
        return -1;
   hFuncInst = LoadLibrary("User32.DLL");
    BOOL bRet=FALSE;
    if(hFuncInst)
        UpdateLayeredWindow=(MYFUNC)GetProcAddress(hFuncInst, "UpdateLayeredWindow");
    else
    {
        AfxMessageBox("User32.dll ERROR!");
        exit(0);
    }
    //初始化gdiplus的環境
    // Initialize GDI+.
    m_Blend.BlendOp=0; //theonlyBlendOpdefinedinWindows2000
    m_Blend.BlendFlags=0; //nothingelseisspecial...
    m_Blend.AlphaFormat=1; //...
    m_Blend.SourceConstantAlpha=255;//AC_SRC_ALPHA

   // png圖片添加到資源中了在"PNG"下:所以這裡可以從資源中調用,
   // 這裡Image沒有提供字節調用資源中圖像的函數,
   // ImageFromIDResource()是通過資源名稱"PNG"和資源ID號將圖像
   // 的Image指針傳遞給指針應用。來完成的。

   ImageFromIDResource(IDR_PNGBAK1,"PNG",m_pImageClock1);
   ImageFromIDResource(IDR_PNGNUM,"PNG",m_pImageNum);
   ImageFromIDResource(IDR_PNGBAK,"PNG",m_pImageClock);
   ImageFromIDResource(IDR_PNGHOUR,"PNG",m_pImageHHour);
   ImageFromIDResource(IDR_PNGMIN,"PNG",m_pImageHMinu);
   ImageFromIDResource(IDR_PNGSEC,"PNG",m_pImageHSec);
    m_BakWidth =m_pImageClock->GetWidth();
    m_BakHeight =m_pImageClock->GetHeight();
    m_HourWidth =m_pImageHHour->GetWidth();
    m_HourHeight=m_pImageHHour->GetHeight();
    m_MinuWidth =m_pImageHMinu->GetWidth();
    m_MinuHeight=m_pImageHMinu->GetHeight();
    m_SecWidth =m_pImageHSec->GetWidth();
    m_SecHeight =m_pImageHSec->GetHeight();
    ::SetWindowPos(m_hWnd, HWND_TOPMOST,0,0,m_BakWidth,m_BakHeight,SWP_NOSIZE|SWP_NOMOVE); 
    return 0;
}
4.在OnInitDialog()種添加如下代碼對調用透明窗體初始化和設置時鐘進行刷新,代碼意義有注解://啟動後立刻更新窗口樣式為透明窗體
    UpdateClockDisplay();
    SetTimer(1,500,NULL);
//去除任務欄窗口對應按鈕
    ModifyStyleEx (WS_EX_APPWINDOW,WS_EX_TOOLWINDOW );
void CGDIPClockDlg::OnTimer(UINT nIDEvent)
{
    // TODO: Add your message handler code here and/or call default
    UpdateClockDisplay();
    CDialog::OnTimer(nIDEvent);
}

5、透明窗體創建於刷新,均調用以下函數完成,函數的參數表示整個窗體的透明度

在該函數中包括了GDI+中對Image.DrawImage()函數的集中重載方式的使用,還有在GDI+中圖像變換矩陣的使用初步研究。

BOOL CGDIPClockDlg::UpdateClockDisplay(int Transparent)
{
    HDC hdcTemp=GetDC()->m_hDC;
    m_hdcMemory=CreateCompatibleDC(hdcTemp);
    HBITMAP hBitMap=CreateCompatibleBitmap(hdcTemp,m_BakWidth,m_BakHeight);
    SelectObject(m_hdcMemory,hBitMap);
    if(Transparent<0||Transparent>100)   Transparent=100;
    m_Blend.SourceConstantAlpha=int(Transparent*2.55);
    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(m_BakWidth, 0),
                   Point(0, m_BakHeight)
                  };
    static bool bFly=false;
    bFly?graph.DrawImage(m_pImageClock, points, 3): graph.DrawImage(m_pImageClock1, points, 3);
    bFly=!bFly;
    int OxyX=140;//m_BakWidth/2+8;
    int OxyY=90;//m_BakHeight/2+10;
    SYSTEMTIME SystemTime;  // address of system time structure
    GetLocalTime(&SystemTime);

    // 定義一個單位矩陣,坐標原點在表盤中央
    Matrix matrixH(1,0,0,1,OxyX,OxyY);
    // 時針旋轉的角度度
    matrixH.Rotate(SystemTime.wHour*30+SystemTime.wMinute/2.0-180);
    Point pointsH[] = { Point(0, 0),Point(m_HourWidth, 0),Point(0, m_HourHeight)};
    matrixH.Translate(-m_HourWidth/2,-m_HourHeight/6);
    // 用該矩陣轉換points
    matrixH.TransformPoints( pointsH, 3);
    graph.DrawImage (m_pImageHHour,pointsH, 3);
    // 定義一個單位矩陣,坐標原點在表盤中央
    Matrix matrixM(1,0,0,1,OxyX,OxyY);
    // 分針旋轉的角度度
    matrixM.Rotate(SystemTime.wMinute*6-180);
    Point pointsM[] = { Point(0, 0),Point(m_MinuWidth, 0),Point(0, m_MinuHeight)};
    matrixM.Translate(-m_MinuWidth/2,-m_MinuHeight/6);
    // 用該矩陣轉換pointsM
    matrixM.TransformPoints( pointsM, 3);
    graph.DrawImage (m_pImageHMinu,pointsM, 3);

    // 定義一個單位矩陣,坐標原點在表盤中央
    Matrix matrix(1,0,0,1,OxyX,OxyY);
    // 秒針旋轉的角度度
    matrix.Rotate(SystemTime.wSecond*6-180);
    Point pointsS[] = { Point(0, 0),Point( m_SecWidth,0),Point(0,m_SecHeight )};
    matrix.Translate(-m_SecWidth/2,-m_SecHeight/7);
    // 用該矩陣轉換pointsS
    matrix.TransformPoints( pointsS, 3);
    graph.DrawImage (m_pImageHSec,pointsS, 3);
//HH:MM:SS
    //該函數從m_pImageClock中剪切指定rect中的像素draw到指定位置
    graph.DrawImage(m_pImageNum,0, 0, 14*(SystemTime.wHour/10), 0,14,23,UnitPixel);
    //該函數從m_pImageClock中剪切指定rect中的像素draw到指定位置
    graph.DrawImage(m_pImageNum,20,0, 14*(SystemTime.wHour%10), 0,14,23,UnitPixel);
   //該函數從m_pImageClock中剪切指定rect中的像素draw到指定位置
   graph.DrawImage(m_pImageNum,20*2,0, 140, 0,14,23,UnitPixel);
    //該函數從m_pImageClock中剪切指定rect中的像素draw到指定位置
    graph.DrawImage(m_pImageNum,20*3, 0, 14*(SystemTime.wMinute/10), 0,14,23,UnitPixel);
    //該函數從m_pImageClock中剪切指定rect中的像素draw到指定位置
    graph.DrawImage(m_pImageNum,20*4,0, 14*(SystemTime.wMinute%10), 0,14,23,UnitPixel);
    //該函數從m_pImageClock中剪切指定rect中的像素draw到指定位置
    graph.DrawImage(m_pImageNum,20*5,0, 140, 0,14,23,UnitPixel);
    //該函數從m_pImageClock中剪切指定rect中的像素draw到指定位置
    graph.DrawImage(m_pImageNum,20*6, 0, 14*(SystemTime.wSecond/10), 0,14,23,UnitPixel);
    //該函數從m_pImageClock中剪切指定rect中的像素draw到指定位置
    graph.DrawImage(m_pImageNum,20*7,0, 14*(SystemTime.wSecond%10), 0,14,23,UnitPixel);
    SIZE sizeWindow={m_BakWidth,m_BakHeight};
    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;
}
BOOL CGDIPClockDlg::ImageFromIDResource(UINT nID, LPCTSTR sTR,Image * &pImg)
{
    HINSTANCE hInst = AfxGetResourceHandle();
    HRSRC hRsrc = ::FindResource (hInst,MAKEINTRESOURCE(nID),sTR); // type
    if (!hRsrc)
       return FALSE;
    // load resource into memory
    DWORD len = SizeofResource(hInst, hRsrc);
    BYTE* lpRsrc = (BYTE*)LoadResource(hInst, hRsrc);
    if (!lpRsrc)
       return FALSE;
    // 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);
}
void CGDIPClockDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
  //禁止顯示移動矩形窗體框
  ::SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,TRUE,NULL,0);
  //非標題欄移動整個窗口
  SendMessage(WM_SYSCOMMAND,0xF012,0);
  // PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));
  CDialog::OnLButtonDown(nFlags, point);
}
詳細實現過程請參考源代碼!

四、結束語

編寫該程序的主要動力來自於對GDI+圖像、圖形功能的好奇,網上好多例子和文章都是關於C#或delphi等語言的。本人一直以來習慣於使用VC6.0。希望通過此文能增進與大家交流。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved