一、實現原理
我們知道windows窗口默認都是矩形,要實現任意形狀的窗口就需要自繪。為此從CBUTTON派生一個按鈕類CControlButton,重載DrawItem消息處理進行自繪。圖片的背景是矩形的,假如我們的按鈕圖片是圓形的,當把圖片繪制上去之後,我們發現多出了背景部分。如何消除背景呢?
為了解決這個問題,我們可以用BitBlt 中的MERGEPAINT和SRCAND的方式進行繪制。 MERGEPAINT是把圖形反色後再同貼圖目的地進行OR操作,而SRCAND是把圖形和貼圖目的地進行AND操作。在計算機中,使用的是數字圖像處理,每一種顏色都是由RGB表示的,RGB是指紅、綠、藍三原色,只要有這3種顏色和對應的顏色強度就可以合成各種顏色了。比如,黑色的RGB值為(0,0,0),白色的RGB值為(255,255,255),括號內對應的是紅綠藍3種顏色的強度。在數字圖像處理中可以實現OR、AND等邏輯運算。任何顏色同白色進行OR運算結果都為白色,進行AND運算結果都是該顏色本身;任何顏色跟黑色進行OR運算結果都為該顏色本身;進行AND運算結果都是黑色。為此,我們准備兩張圖片,如下圖所示:
圖1 圖2
圖1的背景為白色,圖2是將圖1中需要顯示部分填充黑色而得。實現去除背景貼圖關鍵代碼如下:
if (IsMask==TRUE) //值為真則去除圖片背景
MaskDC是圖2的DC,MemDC為圖1的DC。
{
CDC MaskDC;
MaskDC.CreateCompatibleDC(pDC);
if (IsBackBmp==TRUE)//使用和主窗口相同的背景圖片
{
CBitmap *pOldBmp;
CDC BackDC;
BackDC.CreateCompatibleDC(pDC);
pOldBmp = MaskDC.SelectObject(&m_MaskBitmap);
BackDC.SelectObject(&m_BackBitmap);
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&BackDC,BackRect.left,BackRect.top,SRCCOPY);
}
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&MaskDC,0,0,MERGEPAINT);
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&MemDC,0,0,SRCAND);
ReleaseDC(&MaskDC);
}
else
{
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&MemDC,0,0,SRCCOPY);
}
效果如下圖所示:
可能這時你就納悶了,為什麼背景色還是白色呢,是不是代碼沒有去掉圖片的背景色呢?答案是貼圖的時候已經去掉了背景色。請看分析
按鈕是一個子窗口,默認情況下主窗口和按鈕子窗口背景都是白色,但是往往我們需要在主窗口上繪制一張圖片,這樣窗口看起來就比較美觀。這樣子做之後,按鈕子窗口和主窗口的背景就不一樣了。在進行按鈕自繪的時候,那就是把按鈕背景作為目的地進行OR、AND運算,因為按鈕背景就是白色的,所以效果看起來也就是白色的。
要解決這個問題也很簡單,我們獲取按鈕所在主窗口中的矩形區域,把這個區域的主窗口背景繪制到按鈕中,再進行繪制按鈕圖片的操作就可以了。
通過這樣做之後,效果如下圖:
為此,我們已經得到一個圖片按鈕了。但僅僅這樣還不行,這按鈕的響應區域還是矩形區域,也就是說除了按鈕圖片之外的區域也響應鼠標點擊。那我們就需要構造一個按鈕圖片區域,使用庫函數SetWindowRgn就可以確定響應區域了。SetWindowRgn有個參數為HRGN類型,因此我們需要獲得一個HRGN。
Jean-Edouard Lachand-Robert 寫了一個 BitmapToRegion 函數,函數的功能為把一張位圖根據一種顏色轉化為一個區域,這個我們就可以得到一個HRGN。有關BitmapToRegion詳情請看代碼說明。我們用圖2中的黑色區域去轉化成區域,為此我們就得了一個圖片按鈕的響應區域了。
另外,CControlButton類還提供了通常的四態按鈕的支持,即鼠標劃過、點擊、正常、獲得焦點四種情況對應加載四張不同的位圖。
二、成員函數介紹
① void CControlButton::SetMaskBitmapId(int mask, bool action)
功能:設置圖2資源圖片
返回值:無
參數:mask ,圖2的資源ID
action,值為TRUE則有效,FALSE為無效
② void CControlButton::SetBackBmp(int nBgdBmpId,CRect rect);
功能:設置按鈕背景圖片
返回值:無
參數:nBgdBmpId ,主窗口背景圖片資源ID
rect , 按鈕在主窗口中的客戶區矩形, 使用GetWindowRect, ScreenToClient這兩個函數即可以輕松獲得。
③ void CControlButton::SetRgnMask(int nMaskBmpId, bool nAction)
功能:設置有效區域函數:
返回值:無
參數:nMaskBmpId ,圖2的資源ID
nAction ,值為TRUE則設置有效,FALSE則無效,通過這樣可以使用或禁止構造響應區域
④void CControlButton::SetBitmapId(int nOver,int nNormal,int nPressed,int nFocus)
功能:設置按鈕動態加載的四幅圖片 :
返回值:無
參數:nOver,鼠標劃過對應按鈕圖片資源ID。
nNormal ,正常狀態下 對應按鈕圖片資源ID
nPressed ,按下按鈕對應圖片資源ID
nFocus ,獲得焦點情況下圖片資源ID
三、使用說明
CControlButton類從CButton類派生,使用時,只需在界面上放置一個按鈕控件,添加CControlButton類,關聯一個CControlButton的控件變量,然後進行初始化即可:
CRect btnRect; //定義按鈕矩形變量
m_demoBtn.GetWindowRect(btnRect); //獲取按鈕窗口矩形區域
ScreenToClient(btnRect); //轉換成客戶區域
//設置按鈕的背景圖片,跟主窗口的背景圖片一樣
m_demoBtn.SetBackBmp(IDB_BACKGROUND,btnRect);
m_demoBtn.SetRgnMask(IDB_OKmask,TRUE);//設置響應區域,TRUE設置構造區域有效
m_demoBtn.SetMaskBitmapId(IDB_OKmask,TRUE); //設置掩碼圖片
//設置按鈕的四種狀態圖
m_demoBtn.SetBitmapId(IDB_btn_ok_b,IDB_btn_ok_a,IDB_btn_ok_c,IDB_btn_ok_a);
四、結束語
本類是在我朋友hurryboylqs四態圖片按鈕類的基礎上完成,衷心感謝hurryboylqs的幫助,希望本文對大家有一點點幫助。本人深知本類還有一些不足之處,如若大家對本類有修改完善,也請連修改說明給我發送一份,萬分感謝!
Email:[email protected]
本文配套源碼