搞Windows程序的人盡皆知分層窗口能夠?qū)崿F(xiàn)很多不錯(cuò)的效果,之前看過一些異形窗口的實(shí)現(xiàn),所以就手癢也想自己搞一個(gè)玩一玩。自己動(dòng)手實(shí)現(xiàn)過程才發(fā)現(xiàn)還是有不少問題的。
基本思路是:
1.將窗口擴(kuò)展屬性設(shè)置為分層屬性WS_EX_LAYERED。
2.選一張透明的png圖片,并將其加載進(jìn)來。
3.創(chuàng)建與窗口兼容的內(nèi)存設(shè)備上下文,以及兼容位圖,將兼容位圖選入兼容設(shè)備上下文。
4.將png圖片繪制到內(nèi)存設(shè)備上下文中。
5.設(shè)置BLENDFUNCTION結(jié)構(gòu),調(diào)用UpdateLayeredWindow。
第一步設(shè)置窗口的分層屬性比較簡單:
windowStyle = GetWindowLong(hWnd, GWL_EXSTYLE); windowStyle = windowStyle | WS_EX_LAYERED; SetWindowLong(hWnd, GWL_EXSTYLE, windowStyle);
第二步是將png圖片加載到程序中,ATL的CImage和GDI+的Image這兩個(gè)類比較常用。使用CImage直接通過Load方法加載絕對(duì)路徑圖片或者內(nèi)存中的圖片,我這里就是使用CImage類實(shí)現(xiàn)。代碼:
// CImage類方式加載圖片 CImage img; img.Load(TEXT( "絕對(duì)路徑 png圖片 " )); // 將圖片與類關(guān)聯(lián)起來
然后是使用Image類方式:
// Image類方式加載圖片 Image* pImage = Image::FromFile(_T( " 絕對(duì)路徑png圖片 " ));
第三步比較繁瑣點(diǎn)。加載了圖片后就需要?jiǎng)?chuàng)建一個(gè)位圖句柄HBITMAP,創(chuàng)建位圖句柄有兩種方式:CreateCompatibleBitmap和CreateDIBSection這兩個(gè)函數(shù)。先介紹下這兩個(gè)函數(shù)。
HBITMAP CreateCompatibleBitmap( HDC hdc, // handle to DC int nWidth, // width of bitmap, in pixels int nHeight // height of bitmap, in pixels);
HBITMAP CreateDIBSection( HDC hdc, // handle to DC CONST BITMAPINFO *pbmi, // bitmap data UINT iUsage, // data type indicator VOID **ppvBits, // bit values HANDLE hSection, // handle to file mapping object DWORD dwOffset // offset to bitmap bit values );
首先看看CreateCompatibleBitmap函數(shù)的代碼:
// CreateCompatibleBitmap函數(shù)創(chuàng)建 hdc = GetDC(hWnd); // hWnd為需要分層窗口的句柄 hdcMem = CreateCompatibleDC(hdc); // 創(chuàng)建與hdc相兼容的內(nèi)存句柄 hBitmap = CreateCompatibleBitmap(hdc, sz.cx, sz.cy); // 創(chuàng)建與hdc相兼容的位圖句柄 SelectObject(hdcMem,(HGDIOBJ)hBitmap); // 將位圖選入內(nèi)存句柄作為畫板
然后是使用CreateDIBSection函數(shù)的代碼:
// CreateDIBSection函數(shù)創(chuàng)建 hdc = GetDC(hWnd); hdcMem = CreateCompatibleDC(hdc); BITMAPINFO bitmapinfo; bitmapinfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bitmapinfo.bmiHeader.biBitCount = 32 ; bitmapinfo.bmiHeader.biHeight = sz.cy; bitmapinfo.bmiHeader.biWidth = sz.cx; bitmapinfo.bmiHeader.biPlanes = 1 ; bitmapinfo.bmiHeader.biCompression = BI_RGB; bitmapinfo.bmiHeader.biXPelsPerMeter = 0 ; bitmapinfo.bmiHeader.biYPelsPerMeter = 0 ; bitmapinfo.bmiHeader.biClrUsed = 0 ; bitmapinfo.bmiHeader.biClrImportant = 0 ; bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biWidth * bitmapinfo.bmiHeader.biHeight * bitmapinfo.bmiHeader.biBitCount / 8 ; hBitmap = ::CreateDIBSection(hdcMem,&bitmapinfo, 0 ,NULL, 0 , 0 ); SelectObject(hdcMem,(HGDIOBJ)hBitmap);
第四步,將png圖片繪制到內(nèi)存設(shè)備上下文中。這一步根據(jù)前面加載圖片的兩種方式對(duì)應(yīng)不同的繪制函數(shù)。
CImage類方式代碼:
// CImage類方式代碼 // 將img關(guān)聯(lián)的png圖片繪制到內(nèi)存句柄中 img.Draw(hdcMem, 0 , 0 , sz.cx, sz.cy, 0 , 0 , sz.cx, sz.cy); // hdcMem為內(nèi)存兼容句柄,之后四個(gè)參數(shù)表明了在內(nèi)存兼容句柄中繪制的位置,最后四個(gè)參數(shù)表示了img關(guān)聯(lián)的png圖片的位置
然后是Image類方式處理代碼:
// Image類方式代碼 Graphics g(hMemDC); g.DrawImage( pImage, 0 , 0 );
第五步:設(shè)置BLENDFUNCTION結(jié)構(gòu),調(diào)用UpdateLayeredWindow。
設(shè)置BLENDFUNCTION結(jié)構(gòu)代碼如下:
BLENDFUNCTION bf; bf.AlphaFormat = AC_SRC_ALPHA; // 源位圖具有Alpha通道 bf.BlendFlags = 0 ; // 必須為0 bf.BlendOp = AC_SRC_OVER; // bf.SourceConstantAlpha = 255 ; // 設(shè)置透明度
然后就是調(diào)用UpdateLayeredWindow函數(shù)了
UpdateLayeredWindow(hWnd, hdc, NULL, &sz, hdcMem, &pt, NULL, &bf, ULW_ALPHA);
看看MSDN對(duì)UpdateLayeredWindow函數(shù)的介紹:
BOOL UpdateLayeredWindow( HWND hwnd, // 需要分層的窗口句柄 HDC hdcDst, // 需要分層窗口設(shè)備句柄 POINT *pptDst, // 窗口位置不發(fā)生變化可以設(shè)置為NULL SIZE *psize, // 窗口大小不發(fā)生變化可以設(shè)置為NULL HDC hdcSrc, // 繪制源的設(shè)備句柄 POINT *pptSrc, // COLORREF crKey, // 指定一個(gè)透明色,使用ULW_COLORKEY標(biāo)志時(shí)有效,也就是說crKey為白色時(shí)候,那么位圖上所有白色的地方均為透明,其他地方不透明 BLENDFUNCTION *pblend, // 之前介紹過了 DWORD dwFlags // ULW_ALPHA使用Alpha通道,ULW_COLORKEY使用crKey作為透明色,ULW_OPAQUE不透明 );
這樣異形窗口就算完成了。
整理以下代碼如下:
LONG windowStyle = GetWindowLong(hWnd, GWL_EXSTYLE); windowStyle = windowStyle | WS_EX_LAYERED; SetWindowLong(hWnd, GWL_EXSTYLE, windowStyle); CImage img; img.Load(TEXT( " png圖片 " )); SIZE sz; // 圖片大小 sz.cx = img.GetWidth(); sz.cy = img.GetHeight(); SetWindowPos(hWnd, NULL, 0 , 0 , sz.cx, sz.cy, SWP_NOREDRAW); // 將窗口大小設(shè)置為圖片大小使之相互合適 HDC hdc = GetDC(hWnd); // 獲取窗口設(shè)備句柄 HDC hdcMem = CreateCompatibleDC(hdc); // 創(chuàng)建一個(gè)與hdc相兼容的內(nèi)存設(shè)備句柄 HBITMAP hBitmap = CreateCompatibleBitmap(hdc, sz.cx, sz.cy); SelectObject(hdcMem,(HGDIOBJ)hBitmap); img.Draw(hdcMem, 0 , 0 , sz.cx, sz.cy, 0 , 0 , sz.cx, sz.cy); POINT pt; pt.x = 0 ; pt.y = 0 ; BLENDFUNCTION bf; bf.AlphaFormat = AC_SRC_ALPHA; bf.BlendFlags = 0 ; bf.BlendOp = AC_SRC_OVER; bf.SourceConstantAlpha = 255 ; UpdateLayeredWindow(hWnd, hdc, NULL, &sz, hdcMem, &pt, NULL, & bf, ULW_ALPHA); ReleaseDC(hWnd, hdc);
說說遇到的問題,最開始就碰到了一個(gè)問題發(fā)現(xiàn)png圖片沒有辦法產(chǎn)生異形的效果,然后才發(fā)現(xiàn)原來我使用的png圖片不是透明png圖片。
當(dāng)png透明圖片問題解決后,發(fā)現(xiàn)窗口出現(xiàn)后,本來那個(gè)應(yīng)該是透明的地方卻是白色不透明的了。這個(gè)問題網(wǎng)上也是有介紹的,我在 這里 得到了答案。下面我把原因重新貼一下:
PNG圖片的透明背景總是一片白色,后來才發(fā)現(xiàn)這其實(shí)是微軟GDI+的設(shè)計(jì)問題,PNG圖片是ARGB,使用GDI+載入圖片的時(shí)候,GDI+會(huì)默認(rèn)已經(jīng)進(jìn)行了預(yù)剩運(yùn)算(PARGB),即每象素的實(shí)際值是已經(jīng)和ALPHA值按比例相乘的結(jié)果,實(shí)際上它根本就沒有做預(yù)乘,在使用透明圖片的象素ALPHA通道的時(shí)候,CImage內(nèi)部正是調(diào)用的AlphaBlend,沒有預(yù)乘的圖當(dāng)作預(yù)乘的圖片處理的結(jié)果就是這相當(dāng)于一張和純白背景進(jìn)行了預(yù)剩,所以圖象總是出現(xiàn)白色背景。
下面給出解決這個(gè)問題的代碼,代碼來源 這里 ,處理還是比較簡單的:
if (pImage->GetBPP() == 32 ) // 確認(rèn)該圖像包含Alpha通道 { for (inti= 0 ; i<pImage->GetWidth();i++ ) { for ( int j= 0 ; j<pImage->GetHeight(); j++ ) { byte *pByte = ( byte *)pImage-> GetPixelAddress(i, j); pByte[ 0 ]= pByte[ 0 ] * pByte[ 3 ]/ 255 ; pByte[ 1 ]= pByte[ 1 ] * pByte[ 3 ]/ 255 ; pByte[ 2 ]= pByte[ 2 ] * pByte[ 3 ]/ 255 ; } } }
這樣異形窗口也算是完成了,但是這樣生成的exe文件需要依賴外部的png圖片,所以我應(yīng)該把那張png圖片放到資源文件中,然后直接生成的exe就包含了那張png圖片。這就涉及到了如何從資源中加載圖片的問題。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
