搞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è)置窗口的分層屬性比較簡(jiǎn)單:
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è)問題的代碼,代碼來源 這里 ,處理還是比較簡(jiǎn)單的:
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)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

