Windows 視窗預設為矩形,實現非矩形按鈕需解決兩大問題:
準備兩張同尺寸位圖:
利用光柵運算特性:
ANY_COLOR OR WHITE = WHITE、ANY_COLOR AND WHITE = ANY_COLORANY_COLOR OR BLACK = ANY_COLOR、ANY_COLOR AND BLACK = BLACK繪製序列(關鍵代碼):
if (IsMask) {
CDC maskDC, memDC, backDC;
maskDC.CreateCompatibleDC(pDC); maskDC.SelectObject(&m_MaskBitmap); // 圖2
memDC.CreateCompatibleDC(pDC); memDC.SelectObject(&m_ColorBitmap); // 圖1
// 1. 先把主視窗背景畫到按鈕區域(解決按鈕背景≠主視窗背景)
if (IsBackBmp) {
backDC.CreateCompatibleDC(pDC);
backDC.SelectObject(&m_BackBitmap);
pDC->BitBlt(0,0, w,h, &backDC, backRect.left, backRect.top, SRCCOPY);
}
// 2. MERGEPAINT: 遮罩圖反色 OR 目標 → 前景變白、背景保持
pDC->BitBlt(0,0,w,h, &maskDC, 0,0, MERGEPAINT);
// 3. SRCAND: 彩色圖 AND 目標 → 前景保留、背景變黑(已被主背景覆蓋)
pDC->BitBlt(0,0,w,h, &memDC, 0,0, SRCAND);
}
else {
pDC->BitBlt(0,0,w,h, &memDC, 0,0, SRCCOPY); // 普通模式
}
採用 Jean-Edouard Lachand-Robert 開源演算法,依指定顏色(遮罩圖的黑色)將位圖轉為 HRGN,再呼叫 SetWindowRgn 讓系統僅在該區域分發滑鼠訊息。
SetBitmapId(nOver, nNormal, nPressed, nFocus) 對應滑鼠懸停、正常、按下、擁有焦點四種狀態,DrawItem 根據 DRAWITEMSTRUCT::itemState 選擇對應位圖繪製。
| 函式 | 用途 | 關鍵參數 |
|---|---|---|
SetMaskBitmapId(id, enable) | 設定遮罩圖(圖2) | 資源 ID、啟用旗標 |
SetBackBmp(bgId, rect) | 設定主視窗背景圖同步 | 背景資源 ID、按鈕在主視窗 client 矩形 |
SetRgnMask(maskId, enable) | 啟用/禁用命中區域 | 遮罩圖資源 ID、旗標 |
SetBitmapId(over,normal,pressed,focus) | 四態圖片資源 ID | 四個資源 ID |
CRect rc; m_btn.GetWindowRect(rc); ScreenToClient(rc);
m_btn.SetBackBmp(IDB_BACKGROUND, rc); // 主視窗背景同步
m_btn.SetRgnMask(IDB_OK_MASK, TRUE); // 構造命中區域
m_btn.SetMaskBitmapId(IDB_OK_MASK, TRUE); // 遮罩圖
m_btn.SetBitmapId(IDB_BTN_HOVER, IDB_BTN_NORM, // 四態
IDB_BTN_DOWN, IDB_BTN_FOCUS);
CImage/GDI+ 支援 PNG Alpha 通道,簡化遮罩製作流程。通過「雙重遮罩繪製」解決視覺去背、「BitmapToRegion」解決命中測試,配合四態切換,CControlButton 提供了一個開箱即用的任意形狀按鈕方案。原始碼結構清晰,易於擴展為支援 PNG、高 DPI、動畫幀等現代需求。