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

調色板動畫實現圖像漸顯/漸隱:原理、計時與完整 MFC 實作碼

     編輯:關於VC++

前言

圖像漸顯/漸隱廣泛用於多媒體、簡報、遊戲轉場。傳統逐像素修改速度太慢,Windows 調色板動畫 透過直接修改邏輯調色板表項顏色,配合 多媒體計時器 實現高效、流暢的顏色漸變。本文完整解析原理、關鍵 API、計時迴圈架構,並提供可直接整合的 FadeIn/FadeOut 函式碼。

核心技術:調色板動畫

關鍵類別與函式

  • CPalette::CreatePalette — 建立邏輯調色板,peFlags = PC_RESERVED 防止系統色彩映射干擾
  • CDC::SelectPalette / RealizePalette — 選入並實現調色板
  • CPalette::AnimatePalette — 核心,快速批量更新調色板表項顏色,無需逐像素重繪
  • CPalette::GetPaletteEntries / SetPaletteEntries — 讀取/設定表項 RGB 值

PALETTEENTRY 結構

typedef struct tagPALETTEENTRY {
    BYTE peRed, peGreen, peBlue, peFlags; // peFlags 必為 PC_RESERVED
} PALETTEENTRY;

計時機制:PeekMessage 迴圈 + WM_TIMER

為避免阻塞訊息泵,採用訊息截獲迴圈:

pWnd->SetTimer(0x100, uTimeOut, NULL); // 設定計時器
pWnd->SetCapture();                    // 捕獲滑鼠,防止外部干擾

BOOL bDone = FALSE;
MSG msg;
while (!bDone)
{
    if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        if (msg.message == WM_TIMER && msg.wParam == 0x100)
        {
            // ★ 執行漸變步驟:修改調色板、呼叫 AnimatePalette
            // 判斷是否完成 → bDone = TRUE
        }
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg); // 分發非計時器訊息,保持 UI 回應
    }
}
::ReleaseCapture();
pWnd->KillTimer(0x100);
// 還原原始調色板

完整實作:FadeIn(漸顯:黑 → 原圖色彩)

void FadeIn(CWnd* pWnd, CPalette* pPal, int nDelta, UINT uTimeOut)
{
    int nTotal = pPal->GetEntryCount();
    PALETTEENTRY palOrig[256], palCurrent[256];
    pPal->GetPaletteEntries(0, nTotal, palOrig); // 備份原始色彩

    // 1. 將調色板設為全黑
    for (int i=0; iSetPaletteEntries(0, nTotal, palCurrent);
    pPal->AnimatePalette(0, nTotal, palCurrent);

    // 2. 計時漸變迴圈
    pWnd->SetTimer(0x100, uTimeOut, NULL);
    pWnd->SetCapture();
    BOOL bDone = FALSE;
    MSG msg;

    while (!bDone)
    {
        if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_TIMER && msg.wParam == 0x100)
            {
                CClientDC dc(pWnd);
                CPalette* pOld = dc.SelectPalette(pPal, FALSE);
                dc.RealizePalette();

                pPal->GetPaletteEntries(0, nTotal, palCurrent);
                BOOL rZero=TRUE, gZero=TRUE, bZero=TRUE;

                for (int i=0; iAnimatePalette(0, nTotal, palCurrent);
            }
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }
    }
    ::ReleaseCapture();
    pWnd->KillTimer(0x100);
    // 還原精確原始調色板
    pPal->SetPaletteEntries(0, nTotal, palOrig);
    pPal->AnimatePalette(0, nTotal, palOrig);
}

完整實作:FadeOut(漸隱:原圖色彩 → 黑)

void FadeOut(CWnd* pWnd, CPalette* pPal, int nDelta, UINT uTimeOut)
{
    int nTotal = pPal->GetEntryCount();
    PALETTEENTRY palOrig[256], palCurrent[256];
    pPal->GetPaletteEntries(0, nTotal, palOrig);

    pWnd->SetTimer(0x100, uTimeOut, NULL);
    pWnd->SetCapture();
    BOOL bDone = FALSE;
    MSG msg;

    while (!bDone)
    {
        if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_TIMER && msg.wParam == 0x100)
            {
                CClientDC dc(pWnd);
                CPalette* pOld = dc.SelectPalette(pPal, FALSE);
                dc.RealizePalette();

                pPal->GetPaletteEntries(0, nTotal, palCurrent);
                BOOL rZero=TRUE, gZero=TRUE, bZero=TRUE;

                for (int i=0; i nDelta) { palCurrent[i].peRed -= nDelta; rZero=FALSE; }
                    else if (palCurrent[i].peRed > 1) { palCurrent[i].peRed--; rZero=FALSE; }
                    if (palCurrent[i].peGreen > nDelta) { palCurrent[i].peGreen -= nDelta; gZero=FALSE; }
                    else if (palCurrent[i].peGreen > 1) { palCurrent[i].peGreen--; gZero=FALSE; }
                    if (palCurrent[i].peBlue > nDelta) { palCurrent[i].peBlue -= nDelta; bZero=FALSE; }
                    else if (palCurrent[i].peBlue > 1) { palCurrent[i].peBlue--; bZero=FALSE; }
                }
                bDone = rZero && gZero && bZero;
                pPal->AnimatePalette(0, nTotal, palCurrent);
            }
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }
    }
    ::ReleaseCapture();
    pWnd->KillTimer(0x100);
    pPal->SetPaletteEntries(0, nTotal, palOrig);
    pPal->AnimatePalette(0, nTotal, palOrig);
}

使用範例

// 載入 256 色位圖並取得調色板
CBitmap bmp; bmp.LoadBitmap(IDB_MYIMAGE);
CPalette pal;
{
    CDC dc; dc.CreateCompatibleDC(NULL);
    CBitmap* pOld = dc.SelectObject(&bmp);
    // 從位圖建立調色板(略,需 GetDIBColorTable 或手動建構)
    // 此處假設 pal 已正確建立並 SelectObject 到顯示 DC
}
FadeIn(this, &pal, 10, 50); // 每步增 10,間隔 50ms
// ... 顯示一段時間 ...
FadeOut(this, &pal, 10, 50);

適用場景與限制

優點限制
極高效能:無像素級操作,適合全螢幕轉場僅適用 256 色 (8bpp) 裝置環境;高色彩/真彩色模式無調色板動畫機制
平滑度由計時器間隔與 nDelta 控制現代系統多為 32bpp,需強制 8bpp 模式或改用 Alpha Blending / DirectX
程式碼簡潔,易封裝為工具函式調色板為全域資源,全螢幕獨佔時體驗最佳;視窗模式易受其他視窗調色板干擾

現代替代方案

  • Layered Window + Alpha Blending (WS_EX_LAYERED, UpdateLayeredWindow) — 支援真彩色、視窗級漸變,Win2000+ 原生支援。
  • GDI+ ColorMatrix — 矩陣調整 Alpha 通道繪製。
  • Direct2D / DirectComposition — GPU 加速、獨立動畫執行緒,現代 UWP/Win32 首選。

結語

調色板動畫是 Windows 早期多媒體編程的經典技術,理解其「修改查找表而非像素資料」的核心思想,有助於掌握現代圖形管線中 LUT、Shader、Color Matrix 等同源概念。維護舊專案或開發 8bpp 嵌入式介面時,本文提供的 FadeIn/FadeOut 可直接落地使用。

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