首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC6.0 使用 GDI+ 載入 PNG 實現半透明漸變時鐘視窗

     編輯:關於VC++

專案概述

本文以一個基於對話框的時鐘程式為例,示範在 VC6.0 環境下如何使用 GDI+ 載入帶透明通道的 PNG 圖片,實現半透明漸變視窗效果。程式支援指針式與數字式雙模式顯示,並具備視窗拖動無矩形框、隱藏工作列按鈕等特性。

圖一:程式執行效果與 Windows XP 桌面融合

環境建置準備

圖片資源準備

使用 Photoshop 製作時鐘背景、時針、分針、秒針及數字顯示所需圖片,儲存為帶 Alpha 通道的 PNG 格式,確保 GDI+ 繪製時背景透明。

GDI+ SDK 設定

  1. 下載 GDI+ for VC6.0 SDK
  2. 在 C 槽建立 GDI+ 資料夾,依下列路徑放置檔案:
    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")
  4. 在 App 的 InitInstance() 中初始化與關閉 GDI+:
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    // ... 程式結束前
    GdiplusShutdown(gdiplusToken);

核心實作細節

類別成員變數定義

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;

關鍵說明:VC6.0 SDK 缺少 UpdateLayeredWindow 宣告,需從 User32.dll 動態載入,後三行即為此用途。

OnCreate 初始化

int CGDIPClockDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDialog::OnCreate(lpCreateStruct) == -1) return -1;

    // 載入 User32.dll 並取得 UpdateLayeredWindow 函數指標
    hFuncInst = LoadLibrary(_T("User32.DLL"));
    if (hFuncInst)
        UpdateLayeredWindow = (MYFUNC)GetProcAddress(hFuncInst, "UpdateLayeredWindow");
    else { AfxMessageBox(_T("User32.dll ERROR!")); exit(0); }

    // 混合函數設定
    m_Blend.BlendOp = AC_SRC_OVER;
    m_Blend.BlendFlags = 0;
    m_Blend.AlphaFormat = AC_SRC_ALPHA;
    m_Blend.SourceConstantAlpha = 255;

    // 從資源載入 PNG 圖片
    ImageFromIDResource(IDR_PNGBAK1, _T("PNG"), m_pImageClock1);
    ImageFromIDResource(IDR_PNGNUM, _T("PNG"), m_pImageNum);
    ImageFromIDResource(IDR_PNGBAK, _T("PNG"), m_pImageClock);
    ImageFromIDResource(IDR_PNGHOUR, _T("PNG"), m_pImageHHour);
    ImageFromIDResource(IDR_PNGMIN, _T("PNG"), m_pImageHMinu);
    ImageFromIDResource(IDR_PNGSEC, _T("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;
}

OnInitDialog 與定時器

BOOL CGDIPClockDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    UpdateClockDisplay();           // 立即更新顯示
    SetTimer(1, 500, NULL);         // 500ms 定時器
    ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_TOOLWINDOW); // 隱藏工作列按鈕
    return TRUE;
}

void CGDIPClockDlg::OnTimer(UINT nIDEvent)
{
    UpdateClockDisplay();
    CDialog::OnTimer(nIDEvent);
}

核心繪圖與分層視窗更新

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, OxyY = 90;  // 表盤中心座標
    SYSTEMTIME st; GetLocalTime(&st);

    // 時針旋轉矩陣
    Matrix matrixH(1, 0, 0, 1, OxyX, OxyY);
    matrixH.Rotate(st.wHour * 30 + st.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);
    matrixH.TransformPoints(pointsH, 3);
    graph.DrawImage(m_pImageHHour, pointsH, 3);

    // 分針旋轉矩陣
    Matrix matrixM(1, 0, 0, 1, OxyX, OxyY);
    matrixM.Rotate(st.wMinute * 6 - 180);
    Point pointsM[] = { Point(0, 0), Point(m_MinuWidth, 0), Point(0, m_MinuHeight) };
    matrixM.Translate(-m_MinuWidth/2, -m_MinuHeight/6);
    matrixM.TransformPoints(pointsM, 3);
    graph.DrawImage(m_pImageHMinu, pointsM, 3);

    // 秒針旋轉矩陣
    Matrix matrixS(1, 0, 0, 1, OxyX, OxyY);
    matrixS.Rotate(st.wSecond * 6 - 180);
    Point pointsS[] = { Point(0, 0), Point(m_SecWidth, 0), Point(0, m_SecHeight) };
    matrixS.Translate(-m_SecWidth/2, -m_SecHeight/7);
    matrixS.TransformPoints(pointsS, 3);
    graph.DrawImage(m_pImageHSec, pointsS, 3);

    // 數字時間繪製 (從數字圖片裁切對應數字)
    graph.DrawImage(m_pImageNum, 0, 0, 14*(st.wHour/10), 0, 14, 23, UnitPixel);
    graph.DrawImage(m_pImageNum, 20, 0, 14*(st.wHour%10), 0, 14, 23, UnitPixel);
    graph.DrawImage(m_pImageNum, 40, 0, 14*10, 0, 14, 23, UnitPixel); // 冒號
    graph.DrawImage(m_pImageNum, 60, 0, 14*(st.wMinute/10), 0, 14, 23, UnitPixel);
    graph.DrawImage(m_pImageNum, 80, 0, 14*(st.wMinute%10), 0, 14, 23, UnitPixel);
    graph.DrawImage(m_pImageNum, 100, 0, 14*10, 0, 14, 23, UnitPixel); // 冒號
    graph.DrawImage(m_pImageNum, 120, 0, 14*(st.wSecond/10), 0, 14, 23, UnitPixel);
    graph.DrawImage(m_pImageNum, 140, 0, 14*(st.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 & WS_EX_LAYERED) != WS_EX_LAYERED)
        SetWindowLong(m_hWnd, GWL_EXSTYLE, dwExStyle | WS_EX_LAYERED);

    BOOL bRet = UpdateLayeredWindow(m_hWnd, hdcScreen, &ptWinPos, &sizeWindow,
                                    m_hdcMemory, &ptSrc, 0, &m_Blend, ULW_ALPHA);

    // 資源釋放
    graph.ReleaseHDC(m_hdcMemory);
    ::ReleaseDC(m_hWnd, hdcScreen);
    ::ReleaseDC(m_hWnd, hdcTemp);
    DeleteObject(hBitMap);
    DeleteDC(m_hdcMemory);
    m_hdcMemory = NULL;

    return bRet;
}

PNG 資源載入輔助函數

BOOL CGDIPClockDlg::ImageFromIDResource(UINT nID, LPCTSTR sTR, Image*& pImg)
{
    HINSTANCE hInst = AfxGetResourceHandle();
    HRSRC hRsrc = ::FindResource(hInst, MAKEINTRESOURCE(nID), sTR);
    if (!hRsrc) return FALSE;

    DWORD len = SizeofResource(hInst, hRsrc);
    BYTE* lpRsrc = (BYTE*)LoadResource(hInst, hRsrc);
    if (!lpRsrc) return FALSE;

    HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len);
    BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
    memcpy(pmem, lpRsrc, len);

    IStream* pstm;
    CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
    pImg = Gdiplus::Image::FromStream(pstm);

    GlobalUnlock(m_hMem);
    pstm->Release();
    FreeResource(lpRsrc);
    return TRUE;
}

滑鼠拖動視窗 (無矩形框)

void CGDIPClockDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
    ::SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, NULL, 0);
    SendMessage(WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0);
    CDialog::OnLButtonDown(nFlags, point);
}

總結

本文完整展示了在 VC6.0 中整合 GDI+、動態載入 User32.dll API、使用矩陣變換繪製旋轉指針、分層視窗實現半透明效果等關鍵技術。完整原始碼可供下載參考。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved