繪制半透明位圖
有的時侯,,我們希望顯示一幅半透明的位圖。也就是說我們將一幅位圖B
顯示到A位圖上,,又希望透過B位圖看到A位圖的一部分圖像但不是全部,。比如A位 圖是一幅曲線圖,B是一幅提示位圖,,我們想在顯示提示的同時看到已顯示的曲 線,,但不需要曲線的背景,就需有用到半透明位圖,。曲線看上去就象從B位圖中滲 透過來,,其實半透明技術就是一種滲透技術,,滲透公式我們可選用多種,在這里 我們選用(A AND 0x7F)OR B,。注意,,白色不能產(chǎn)生滲透。 //參數(shù)說明:
//hDIB -位圖句柄 //pPal -位圖調(diào)色板 //xDest -顯示位圖的左上角x坐標 //yDest -顯示位圖的左上角y坐標 void DrawSemiTransparentBitmap(CDC *pDC, int nXDest, int nYDest, HGLOBAL hDIB,CPalette *pPal) BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB ; int nColors = bmInfo.bmiHeader.biClrUsed ? bmInfo.bmiHeader.biClrUsed : 1 << bmInfo.bmiHeader.biBitCount; int nWidth = bmInfo.bmiHeader.biWidth; int nHeight = bmInfo.bmiHeader.biHeight; LPVOID lpDIBBits = (LPVOID)(bmInfo.bmiColors + nColors);
CDC memDC; memDC.CreateCompatibleDC( pDC ); CBitmap bmp; bmp.CreateCompatibleBitmap( pDC, nWidth, nHeight ); CBitmap *pOldBitmap = memDC.SelectObject( &bmp ); if( pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE&&nColors<256) CPalette *pOldMemPalette = memDC.SelectPalette(pPal, FALSE); memDC.RealizePalette(); ::SetDIBitsToDevice(memDC.m_hDC, 0, 0, nWidth, nHeight, 0, 0, 0, nHeight, lpDIBBits, (LPBITMAPINFO)hDIB, DIB_RGB_COLORS); CDC maskDC;
CBitmap mbm; maskDC.CreateCompatibleDC(pDC); mbm.CreateCompatibleBitmap(pDC, nWidth, nHeight); maskDC.SelectObject(&mbm); maskDC.FillSolidRect(CRect(0, 0, nWidth, nHeight), RGB(0x7F, 0x7F, 0x7F)); pDC->BitBlt(nXDest, nYDest, nWidth, nHeight, &maskDC, 0, 0, SRCAND); pDC->BitBlt(nXDest, nYDest, nWidth, nHeight, &memDC, 0, 0, SRCPAINT); memDC.SelectObject(pOldBitmap); } 如何畫透明位圖
在豐富多彩的軟件世界中,,位圖的處理技術尤為重要,。透明位圖的顯示作為
一種常用的圖像處理方法,被用在眾多的軟件中,。其基本原理,,也就是將一幅需 要透明顯示的位圖(其透明色為已知),制作出二幅需要的位圖A與B,,其中A為除 透明色外均填充為黑色,,B為把透明色填充為黑色其余色不變,再用指定光柵操作 將兩幅位圖合并,,可形成透明位圖,。 創(chuàng)建過程如下: 1、使用透明色背景,,將位圖拷貝到內(nèi)存屏蔽位圖中,。 2、利用與白色相與不變色,,與黑色相與成黑色的原理,,將內(nèi)存位圖的的背景設置 成黑色、前景設置成白色,,并將屏蔽位圖用與操作拷貝到此內(nèi)存位圖中,。形成位圖B。
3,、將顯示設備背景設置為白色,、前景設置成黑色,并將屏蔽位圖用與操作拷貝到 顯示設備中,。形成位圖A,。 4、將內(nèi)存位圖用或操作拷貝到顯示設備中,。最終形成透明位圖,。 //參數(shù)說明:
//hDIB -位圖句柄 //pPal -位圖調(diào)色板 //xDest -顯示位圖的左上角x坐標 //yDest -顯示位圖的左上角y坐標 //colorTransparent -透明色 void DrawTransparentBitmap( CDC *pDC, int nXDest, int nYDest,HGLOBAL hDIB, COLORREF colorTransparent, CPalette *pPal)
{
BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB ;
int nColors = bmInfo.bmiHeader.biClrUsed ? bmInfo.bmiHeader.biClrUsed :1< int nWidth = bmInfo.bmiHeader.biWidth; int nHeight = bmInfo.bmiHeader.biHeight; LPVOID lpDIBBits = (LPVOID)(bmInfo.bmiColors + nColors); CDC memDC,maskDC; memDC.CreateCompatibleDC( pDC ); CBitmap bmp; bmp.CreateCompatibleBitmap( pDC, nWidth, nHeight ); CBitmap *pOldBitmap = memDC.SelectObject( &bmp ); if( pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE&&nColors<256)
CPalette *pOldMemPalette = memDC.SelectPalette(pPal, FALSE); memDC.RealizePalette(); ::SetDIBitsToDevice(memDC.m_hDC, 0, 0, nWidth, nHeight, 0, 0, 0, nHeight, lpDIBBits, (LPBITMAPINFO)hDIB, DIB_RGB_COLORS); maskDC.CreateCompatibleDC(pDC); CBitmap maskBitmap; maskBitmap.CreateBitmap( nWidth, nHeight, 1, 1, NULL ); maskDC.SelectObject( &maskBitmap ); memDC.SetBkColor( colorTransparent ); maskDC.BitBlt( 0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY );
memDC.SetBkColor(RGB(0,0,0)); memDC.SetTextColor(RGB(255,255,255)); memDC.BitBlt(0, 0, nWidth, nHeight, &maskDC, 0, 0, SRCAND); pDC->SetBkColor(RGB(255,255,255)); pDC->SetTextColor(RGB(0,0,0)); pDC->BitBlt(nXDest, nYDest, nWidth, nHeight, &maskDC, 0, 0, SRCAND); pDC->BitBlt(nXDest, nYDest, nWidth, nHeight, &memDC,0, 0, SRCPAINT); memDC.SelectObject( pOldBitmap ); } 畫透明位圖通常的方法是使用遮罩。所謂遮罩就是一張黑白雙色的位圖,,他和 要透明的位圖是對應的,,遮罩描述了位圖中需要透明的部分,透明的部分是黑色的, 而不透明的是白色的,,白色的部分就是透明的部分,。 假設圖A是要畫的透明位圖,圖B是遮罩,,圖A上是一個大寫字母A,字母是紅色的,,背 景是黑色的,圖B背景是白色的,,上面有一個黑色的字母A和圖A的形狀是一樣的,。 比如我們要在一張藍天白云的背景上透明地畫圖A,,就是只把紅色的字母A畫上去,。我 們可以先將圖B和背景進行與操作,再把圖B和背景進行或操作就可以了,。
用VC++ MFC實現(xiàn)的代碼如下: void CDemoDlg::OnPaint() CPaintDC dc(this); Cbitmap BmpBack,BmpA,BmpB,*pOldBack,*pOldA,*pOldB; BmpBack.LoadBitmap(IDB_BACKGROUND); // 載入背景圖 BmpA.LoadBitmap(IDB_BITMAPA); //載入圖A BmpB.LoadBitmap(IDB_BITMAPB); //載入圖B CDC dcBack,dcA,dcB; //聲明三個內(nèi)存DC用于畫圖 dcBack.CreateCompatibleDC(&dc); dcA.CreateCompatibleDC(&dc); dcB.CreateCompatibleDC(&dc); //把這三個內(nèi)存DC創(chuàng)建成和PaintDC兼容的DC pOldBack=dcBack.SelectObject(&BmpBack);
pOldA=dcA.SelectObject(&BmpA); pOldB=dcB.SelectObject(&BmpB); //把三個位圖選入相應的DC dc.BitBlt(0,0,100,100,&dcBack,0,0,SRCCOPY); //畫背景 dc.BitBlt(0,0,48,48,&dcB,0,0,SRCAND); //用與的方式畫遮罩圖B dc.BitBlt(0,0,48,48,&dcA,0,0,SRCPAINT); //用或的方式畫遮圖A dcBack.SelectObject(pOldBack); dcBack.SelectObject(pOldA); dcBack.SelectObject(pOldB); //從內(nèi)存DC中刪除位圖 你會看到紅色的字母A透明地畫在背景上了,。 用遮罩的方法必須事先做好遮罩,遮罩和位圖大小一樣等于多消耗一倍的資源,,
比較浪費,。還有一種畫透明位圖的方法,基本原理是一樣的,,只是不用事先做好 遮罩,,根據(jù)需要動態(tài)生成遮罩,但是要求需要透明的位圖必須指定一種透明色,, 凡是這個透明色的地方則畫成透明的,。 用VC++ MFC實現(xiàn)的代碼如下: /* 這是一個用來畫透明位圖的函數(shù) CDC *pDC 需要畫位圖的CDC指針 UINT IDImage 位圖資源ID Crect &rect 指定位圖在pDC中的位置 COLORREF rgbMask 位圖的透明色 */
void DrawTransparentBitmap(CDC *pDC, UINT IDImage,Crect &rect, COLORREF rgbMask) { CDC ImageDC,MaskDC;
Cbitmap Image,*pOldImage; Cbitmap maskBitmap,*pOldMaskDCBitmap ; Image.LoadBitmap(IDImage); ImageDC.CreateCompatibleDC(pDC); pOldImage=ImageDC.SelectObject(&Image); MaskDC.CreateCompatibleDC(pDC); maskBitmap.CreateBitmap( rect.Width(), rect.Height(), 1, 1, NULL ); pOldMaskDCBitmap = MaskDC.SelectObject( &maskBitmap ); ImageDC.SetBkColor(rgbMask); MaskDC.BitBlt( 0, 0, rect.Width(), rect.Height(), &ImageDC, 0, 0, SRCCOPY ); ImageDC.SetBkColor(RGB(0,0,0));
ImageDC.SetTextColor(RGB(255,255,255)); ImageDC.BitBlt(0, 0, rect.Width(), rect.Height(), &MaskDC, 0, 0, SRCAND); pDC->BitBlt(rect.left,rect.top,rect.Width(), rect.Height(), &MaskDC, 0, 0, SRCAND); pDC->BitBlt(rect.left,rect.top,rect.Width(), rect.Height(), &ImageDC, 0, 0,SRCPAINT); MaskDC.SelectObject(pOldMaskDCBitmap); ImageDC.SelectObject(pOldImage); }
void CDemoDlg::OnPaint()
{
CPaintDC dc(this);
Cbitmap BmpBack,*pOldBack,; BmpBack.LoadBitmap(IDB_BACKGROUND); CDC dcBack;
dcBack.CreateCompatibleDC(&dc); pOldBack=dcBack.SelectObject(&BmpBack); dc.BitBlt(0,0,100,100,&dcBack,0,0,SRCCOPY); DrawTransparentBitmap(&dc,IDB_BITMAPA,Crect(0,0,48,48),RGB(192,192,0)); dcBack.SelectObject(pOldBack);
}
//XOR 方式
首先是異或運算:
異或的運算方法是一個二進制運算: 異或的運算方法是一個二進制運算: 1^1=0 0^0=0 1^0=1 0^1=1 兩者相等為0,不等為1.
這樣我們發(fā)現(xiàn)交換兩個整數(shù)的值時可以不用第三個參數(shù)。
如a=11,b=9.以下是二進制 a=a^b=1011^1001=0010; b=b^a=1001^0010=1011; a=a^b=0010^1011=1001; 這樣一來a=9,b=11了,。 再就是sgyxslsc先生的這篇文章(原文出處:http://sgyxslsc.spaces./blog/)
異 或運算有這么個特點:用一個數(shù)B與另一個數(shù)A進行兩次異或運算,,那么A的值保持不變??梢栽O想一下,,在顯示位圖的時候,圖片中的位與欲顯示的位圖的區(qū)域進 行兩次異或操作時,,欲顯示的位圖的區(qū)域不會產(chǎn)生任何變化,。也就是說,如果將圖片中的背景色與欲顯示的位圖的區(qū)域進行兩次異或運算,,那么背景色就被過濾掉 了,,但同樣,位圖中非背景的內(nèi)容也被過濾掉了,。
現(xiàn)在我們這樣,,第一次異或運算之后,加這樣一個操作,。把位圖中的主要內(nèi)容與0作與運算,,而背景區(qū)域與1作與運算,。這樣位圖的主要內(nèi)容區(qū)變?yōu)槎甲優(yōu)?,而背景區(qū)域還是原圖像與窗口背景的異或值,。這樣我們再進行第二次異或操作,,位圖的主區(qū)域與0異或保持不變,而背景區(qū)域被消除了,。 而執(zhí)行這個操作需要我們建立一個“面具”圖片,,這個圖片只有兩種顏色:黑和白。黑色的RGB值全部是0組成,,白色的RGB值肯定是全部由1組成,。而這張圖 片的特點就是 主要內(nèi)容的值都是0(黑),背景色都是1(白),。在兩次異或的中間用這個圖片以與的方式在顯示區(qū)域顯示就可以了,。 在BitBlet函數(shù)中,“異或”對應于“SRCINVERT”,,“與”對應于“SRCCOPY”,。下面是書中的程序: void CTransBitmapDlg::OnPaint() { CDC *PDC = GetDC(); CDC dcImage, dcTrans; //裝入欲顯示的位圖 CBitmap bitmap; bitmap.LoadBitmap(IDB_BITMAPID); //取得位圖的詳細信息 BITMAP bm; bitmap.GetBitmap(&bm); int nWidth = bm.bmWidth; int nHeight = bm.bmHeight; //建立與設備環(huán)境相關的位圖,dcImage中用來裝入欲顯示的位圖 //dcTrans中用來裝入過濾背景色的位圖 dcImage.CreateCompatibleDC(pDC); dcTrans.CreateCompatibleDC(pDC); CBitmap *pOldBitmapImage = dcImage.SelectObject(&bitmap); // 建立與欲顯示的位圖等大的單色位圖,,用來過濾背景色 CBitmap bitmapTrans; // “面具”就在這步做好 bitmapTrans.CreateBitmap(nWidth, nHeight, 1, 1, NULL); CBitmap *pOldBitmapTrans = dcTrans.SelectObject(&bitmapTrans); //強行將欲顯示的位圖的背景色設置為白色,,以方便建立dcTrans, 這步很重要,。如果你的 //位圖的背景色不是白色的,,你只需要修改這一步的參數(shù)。 dcImage.SetBkColor(RGB(255, 255, 255)); dcTrans.BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCCOPY); //在dcTrans中,,背景色變?yōu)榘咨?,而主要圖像的各種色彩都被設置成黑色 //顯示“面具”,這一步主要目的的主要目的在于讓你更能夠明白“面具”的真面目 pDC->BitBlt(0, bm.bmHeight, nWidth, nHeight, &dcTrans, 0, 0, SRCCOPY); pDC->BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCINVERT); //該步驟將源位圖與目標區(qū)域進行異或運算 pDC->BitBlt(0, 0, nWidth, nHeight, &dcTrans, 0, 0, SRCAND); //通過dcTrans與目標區(qū)域進行“與”操作,,建立了與主要圖像輪廓一致的黑框以保護圖片的主要內(nèi)容不會改變 pDC->BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCINVERT); //用黑框位圖與源位圖進行黑色異或運算,,將源碼位圖中有色彩(除背景色以外) //的內(nèi)容還原。在這步,,背景色經(jīng)過與目標區(qū)域進行了兩次異或運算,,背景色已經(jīng)被過濾 } } 其中CreateBitmap不明白,怎么利用它就創(chuàng)建出白底黑面的位圖了,,又看了瘋狐先生的文章,,一切都明白了:
函數(shù)功能:該函數(shù)創(chuàng)建一個帶有特定寬度、高度和顏色格式的位圖,。 函數(shù)原型:HBITMAP CreateBitmap(int nWidth, int nHeight, UINT cPlanes, UINT cBitsPerPel, CONST VOID *lpvBits);
參數(shù):
nWidth:指定位圖寬度,,單位為像素。
nHeight:指定位圖高度,單位為像素,。
cPlanes:指定該設備使用的顏色位面數(shù)目,。
cBitsPerPel:指定用來區(qū)分單個像素點顏色的位數(shù)(比特數(shù)目)。
lpvBits:指向顏色數(shù)據(jù)數(shù)組指針,。這些顏色數(shù)據(jù)用來置矩形區(qū)域內(nèi)像素的顏色,。矩形區(qū)域中的每一掃描線必須是雙字節(jié)整數(shù)倍(不足部分以0填充)。如果該參數(shù)為NULL,,那么就表示沒有定義新位圖,。
備注:在創(chuàng)建完位圖后,可以通過使用SelectObject函數(shù)把它選入到設備環(huán)境中,。盡管函數(shù)CreateBitmap可以用來創(chuàng)建彩色位圖,, 但由于性能方面的原因,應用程序使用CreateBitmap函數(shù)來創(chuàng)建單色位圖,,創(chuàng)建彩色位圖應該使用函數(shù) CreateCompatibleBitmap,。當由CreateBitmap創(chuàng)建而返回的彩色位圖被選入到設備環(huán)境時,,系統(tǒng)必須確保選入進去的設備環(huán)境 格式與位圖匹配,。由于函數(shù)CreateComapatibleBitmap獲取設備環(huán)境,所以它返回的位圖與指定的設備環(huán)境有相同的格式,。所以對Select的后續(xù)調(diào)用都要比從CreateBitmap函數(shù)創(chuàng)建返回的彩色位圖調(diào)用快,。
如果位圖是單色的,那么對于目標設備環(huán)境而言,,0代表前景顏色,,而1表示背景顏色。
如果應用程序?qū)Width或nHeight參數(shù)設為0,,那么函數(shù)CreateBitmap返回的是只有一個像素的單色位圖句柄,。當不再需要位圖時,可調(diào)用DeleteObject函數(shù)刪除它,。
Windows CE:參數(shù)cPlanes必須是1,。
|
|