動畫能讓介面生動活潑,且不需特殊多媒體硬體支援。本文以經典「淡入淡出」為例,說明如何結合 Windows GDI、調色板與三元光柵操作 (ROP) 在 Visual C++ 中實現位圖淡入淡出轉場。
電影業起源技術:製作中間過渡影格。電腦上則採「打洞法」——在已顯示的位圖 A 上逐步開啟微小洞口,透出位圖 B 的像素。洞口越多,A 越模糊,B 越清晰,最終完全切換至 B。
256 色模式下,系統調色板 (硬體調色板) 為共享資源。Windows 引入邏輯調色板:
MFC 提供 CPalette、CDC 封裝調色板操作:
CPalette::CreatePalette() 建立調色板CDC::SelectPalette() 選入裝置CDC::RealizePalette() 實現調色板映射CDC::BitBlt 在來源與目的裝置位圖間執行邏輯運算,涉及三個對象:目的畫刷、來源位圖、目的位圖。畫刷為 8×8 單色位圖,可用 CreatePatternBrush 建立。
過程分兩步:
Pattern & Destination (黑=0 保留洞,白=1 保留原像素)。(~Pattern) & Source (黑處取來源,白處為 0)。(Pattern & Destination) | ((~Pattern) & Source)。代入 Pattern=0xF0, Destination=0xAA, Source=0xCC 計算得 ROP 碼 0x00AC0744。
CBrush* CDissolve::CreateDissolveBrush(int Step)
{
// 8x8=64 個像素的擴散序列 (Bayer dithering 順序)
static int data[65] = {
1, 5, 33, 37, 19, 23, 51, 55, 17, 21, 49, 53, 3, 7, 35, 39,
10, 14, 42, 46, 28, 32, 60, 64, 26, 30, 58, 62, 12, 16, 44, 48,
8, 4, 40, 36, 22, 18, 54, 50, 24, 20, 56, 52, 6, 2, 38, 34,
15, 11, 47, 43, 29, 25, 61, 57, 31, 27, 63, 59, 13, 9, 45, 41
};
if (Step < 0 || Step >= m_maxstep) return NULL;
CBrush* pBrush = new CBrush;
if (Step == m_maxstep - 1) // 最後一步全黑畫刷
{
pBrush->CreateSolidBrush(RGB(0,0,0));
return pBrush;
}
BYTE pixels[16] = {0};
for (int i = 0; i < 8; i++) pixels[i*2] = 0xFF; // 初始全白 (11111111)
int pixelsToClear = Step * (64 / m_maxstep);
for (int i = 1; i <= pixelsToClear; i++)
{
int idx = data[i] - 1;
int row = idx / 8;
int col = idx % 8;
pixels[row*2] &= ~(BYTE)pow(2, col); // 將對應位清為 0 (打洞)
}
CBitmap bitmap;
if (!bitmap.CreateBitmap(8, 8, 1, 1, pixels)) return NULL;
if (!pBrush->CreatePatternBrush(&bitmap)) return NULL;
return pBrush;
}
void CDissolveDlg::OnPaint()
{
CPaintDC dc(this);
CRect rect; GetClientRect(&rect);
// 雙緩衝防閃爍
CMemDC memDC(dc, &rect);
CDC* pDC = &memDC;
// 繪製來源位圖 B 到記憶體 DC
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &srcDC, 0, 0, SRCCOPY);
// 依步驟產生畫刷並執行淡入淡出 ROP
for (int step = 0; step < m_maxstep; step++)
{
CBrush* pBrush = CreateDissolveBrush(step);
if (!pBrush) continue;
CBrush* pOldBrush = pDC->SelectObject(pBrush);
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &destDC, 0, 0, 0x00AC0744);
pDC->SelectObject(pOldBrush);
delete pBrush;
Sleep(30); // 控制動畫速度
}
}
CMemDC (記憶體 DC) 先畫好再一次 BitBlt 到螢幕,消除閃爍。RealizePalette 確保色彩正確。淡入淡出是基礎且實用的轉場動畫。掌握調色板、畫刷、ROP 運算與雙緩衝技巧,即可擴展至更豐富的視覺效果 (如推拉、百葉窗、溶解等)。本文範例程式碼可直接整合至 MFC 專案中使用。