介面設計常需將不規則圖形(如角色、圖示)疊加於背景上,需實現位圖「透明色」不繪製、前景正常顯示。本文詳細解析 Windows GDI 透明位圖繪製原理(掩碼位圖法),並提供封裝於 CStatic 衍生類 CTransparentImage 的完整實作代碼。

圖 1:對話框上透明顯示不規則位圖(紫色背景未繪製)
建立兩個記憶體 DC:
掩碼製作:將 Image DC 以 SRCCOPY 複製到 Mask DC,並將背景色設為透明色(如 RGB(255,0,255)),GDI 會自動將透明色轉為白色(1),非透明色轉為黑色(0)。
繪製三步驟(ROP 碼運算):
Image DC --SRCINVERT--> 目標 DC:目標反相(前景變反色,背景不變)。Mask DC --SRCAND--> 目標 DC:掩碼白色(透明區)保留目標,黑色(前景)清零目標。Image DC --SRCINVERT--> 目標 DC:前景反相還原,背景維持原樣。若裝置不支援 MaskBlt,可用等效的四次 BitBlt 組合(複製目標→Mask AND→Image XOR→目標 OR)。
class CTransparentImage : public CStatic {
DECLARE_MESSAGE_MAP()
protected:
afx_msg void OnPaint();
};
BEGIN_MESSAGE_MAP(CTransparentImage, CStatic)
ON_WM_PAINT()
END_MESSAGE_MAP()
void CTransparentImage::OnPaint() {
HBITMAP hBmp = GetBitmap();
if (!hBmp) { Default(); return; }
CPaintDC dc(this);
CRect rc; GetClientRect(&rc);
// 雙緩衝 DC
CDC bufDC; bufDC.CreateCompatibleDC(&dc);
CBitmap bufBmp; bufBmp.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height());
CBitmap* pOldBuf = bufDC.SelectObject(&bufBmp);
// Mask DC
CDC maskDC; maskDC.CreateCompatibleDC(&dc);
CBitmap maskBmp; maskBmp.CreateBitmap(rc.Width(), rc.Height(), 1, 1, NULL);
CBitmap* pOldMask = maskDC.SelectObject(&maskBmp);
// 填充透明色(品紅)
bufDC.FillSolidRect(&rc, RGB(255,0,255));
// 載入原始位圖到記憶體 DC
CDC memDC; memDC.CreateCompatibleDC(&dc);
CBitmap* pOldMem = memDC.SelectObject(CBitmap::FromHandle(hBmp));
bufDC.BitBlt(0, 0, rc.Width(), rc.Height(), &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(pOldMem);
// 製作掩碼:設背景色為透明色,複製到 Mask DC
COLORREF oldBk = bufDC.SetBkColor(RGB(255,0,255));
maskDC.BitBlt(0, 0, rc.Width(), rc.Height(), &bufDC, 0, 0, SRCCOPY);
bufDC.SetBkColor(oldBk);
// 嘗試 MaskBlt(支援硬體加速)
if (!dc.MaskBlt(0, 0, rc.Width(), rc.Height(), &bufDC, 0, 0, maskBmp, 0, 0, 0x00AA0029)) {
// 回退:四次 BitBlt 模擬
CDC cpyDC; cpyDC.CreateCompatibleDC(&dc);
CBitmap cpyBmp; cpyBmp.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height());
CBitmap* pOldCpy = cpyDC.SelectObject(&cpyBmp);
cpyDC.BitBlt(0, 0, rc.Width(), rc.Height(), &dc, 0, 0, SRCCOPY);
cpyDC.BitBlt(0, 0, rc.Width(), rc.Height(), &maskDC, 0, 0, SRCAND);
bufDC.BitBlt(0, 0, rc.Width(), rc.Height(), &maskDC, 0, 0, SRCINVERT);
cpyDC.BitBlt(0, 0, rc.Width(), rc.Height(), &bufDC, 0, 0, SRCPAINT);
dc.BitBlt(0, 0, rc.Width(), rc.Height(), &cpyDC, 0, 0, SRCCOPY);
cpyDC.SelectObject(pOldCpy);
}
// 清理
maskDC.SelectObject(pOldMask);
bufDC.SelectObject(pOldBuf);
}
IDB_DRAGON(背景色建議統一為 RGB(255,0,255))。Picture Control (IDC_STATIC),屬性設 Type: Bitmap、Image: IDB_DRAGON。CTransparentImage(基類 CStatic)。| 步驟 | GDI 函數 | ROP 碼 | 說明 |
|---|---|---|---|
| 填充透明色 | FillSolidRect | - | 統一背景色為品紅 |
| 複製原圖 | BitBlt | SRCCOPY | Image DC → Buffer DC |
| 產生掩碼 | BitBlt | SRCCOPY | Buffer DC → Mask DC (SetBkColor 關鍵) |
| 透明合成 | MaskBlt | ROP4_TRANSPARENTBLIT | 單次硬體加速合成 |
| 軟體模擬 | 4×BitBlt | SRCAND/XOR/PAINT | 相容舊顯卡/驅動 |
AlphaBlend() 支援 32-bit 位圖(含 Alpha 通道),邊緣抗鋸齒、半透明效果更佳。Graphics::DrawImage() 原生支援 PNG/透明色,代碼更簡潔。WS_EX_LAYERED + UpdateLayeredWindow() 實現視窗級透明/不規則視窗。本文完整呈現了經典 GDI 透明位圖繪製流程:掩碼製作、三次 ROP 運算、MaskBlt 與軟體回退。封裝於 CStatic 衍生類中,即插即用。雖現代專案建議改用 Alpha Blending 或 GDI+,但理解 ROP 運算邏輯對圖形程式設計仍具核心價值。