| 一種給窗口添加陰影的方法 華南理工大學(xué)微軟技術(shù)俱樂部 |
|
因?yàn)樽约汉芟矚g那些界面做得很漂亮的軟件或者使用各種美化界面的軟件,如avedesk,samurize等等。其中美化界面的一個(gè)重要的方面就是給窗口添加上陰影。雖然OS X已經(jīng)原生的支持窗口陰影,但是windows要到Longhorn才開始支持原生的窗口陰影。現(xiàn)在如果想實(shí)現(xiàn)窗口陰影,一般都會(huì)借助第三方的軟件,例如windowFX或者YzShadow。其中YzShadow是一個(gè)免費(fèi)軟件,我自己也在使用。但是這個(gè)軟件有個(gè)弱點(diǎn),就是無法為L(zhǎng)ayered Window添加陰影。而我自己編寫的一個(gè)簡(jiǎn)易便條程序Stickies(功能類似OneNote,但是功能簡(jiǎn)單,小巧。)就是運(yùn)用了Layered Window來作為軟件的界面,于是便自己嘗試添加窗口陰影。以下便是添加陰影的方法,寫下來與大家討論一下。 我的程序是在Visual Studio.NET 2003下編寫的MFC應(yīng)用程序。我為了實(shí)現(xiàn)窗口陰影創(chuàng)建了一個(gè)Shadow的類。首先我們看看各類之間的關(guān)系:
ShadowCastingWindow成員函數(shù): 該函數(shù)用于創(chuàng)建應(yīng)用程序的窗口并創(chuàng)建陰影。請(qǐng)留意CreateEx中窗口屬性WS_EX_...和WS_...的取值,這使得該應(yīng)用程序的窗口是一個(gè)沒有標(biāo)題欄的Layered Windows。是否有標(biāo)題欄對(duì)于下文Shadow類中求遮擋窗口的大小會(huì)有所不同,這必須通過一個(gè)判斷邏輯或者根據(jù)程序的應(yīng)用不同編寫好代碼。對(duì)于Layered Windows會(huì)有兩種刷新模式,一種就是傳統(tǒng)的消息機(jī)制,就是操作系統(tǒng)自動(dòng)地在適當(dāng)?shù)臅r(shí)候發(fā)送WM_PAINT的消息給應(yīng)用程序窗口,應(yīng)用程序窗口則相應(yīng)該消息,對(duì)窗口進(jìn)行刷新;另一種方式則是在Windows2000以后才支持的UpdateLayeredWindow的機(jī)制,在這種機(jī)制下,應(yīng)用程序不再處理WM_PAINT消息,所有的刷新均由用戶在內(nèi)存中的一個(gè)繪圖上下文中繪制好圖像之后再通過UpdateLayeredWindow繪制到屏幕上,只要經(jīng)過一次繪制,窗口的圖像便會(huì)保存在一塊預(yù)訂好的內(nèi)存區(qū)域內(nèi),如果窗口的圖像沒有改變那么操作系統(tǒng)便會(huì)自動(dòng)地處理刷新。 void ShadowCastingWindow::OnSizing(UINT fwSide, LPRECT pRect) void ShadowCastingWindow::OnSize(UINT nType, int cx, int cy) void ShadowCastingWindow::OnMoving(UINT fwSide, LPRECT pRect) void ShadowCastingWindow::OnMove(int x, int y) 這四個(gè)事件函數(shù)都是處理應(yīng)用程序窗口大小或者位置變化的。只需要在其中調(diào)用Shadow類中相應(yīng)的處理函數(shù)即可,Shadow便會(huì)自動(dòng)地更改大小或者移動(dòng)位置。可能有人會(huì)問為什么需要顯式地調(diào)整Shadow的位置和大小?因?yàn)閺南挛目梢钥吹絊hadow其實(shí)也是一個(gè)Layered Window,沒有父窗口,所以操作系統(tǒng)不可以自動(dòng)地保持兩者的相對(duì)位置。 void ShadowCastingWindow::SetOpacity( int alpha ) 處理應(yīng)用程序窗口透明度變化的函數(shù),其中調(diào)用了陰影Shadow對(duì)于透明度變化的處理函數(shù)。并刷新應(yīng)用程序窗口。 2. Class Shadow
Shadow成員函數(shù): m_IsCreated = true; 創(chuàng)建陰影,由于陰影必須是沒有標(biāo)題欄的,而且因?yàn)橐L制半透明的像素所以必須使用Layered Window。 void Shadow::CustomizedPaint(void) BLENDFUNCTION blendPixelFunction= {AC_SRC_OVER, 0, m_Alpha, AC_SRC_ALPHA}; CDC * dcScreen = GetDesktopWindow()->GetDC(); //----------------------------------- UpdateLayeredWindow( dcScreen, &ptWindowScreenPosition, &szWindow, &dcMemory, &ptSrc, 0, &blendPixelFunction, ULW_ALPHA); GetDesktopWindow()->ReleaseDC(dcScreen); BOOL Shadow::PreCreateWindow(CREATESTRUCT& cs) void Shadow::OnShadowCastingWndNewSize( int x, int y ) 提供該投射陰影的窗口的接口函數(shù),當(dāng)投射陰影的窗口大小改變的時(shí)候便調(diào)用這個(gè)函數(shù)把新的窗口位置傳給Shadow,Shadow便會(huì)改變自己的大小,并重繪窗口。 void Shadow::OnShadowCastingWndNewPos( int x, int y ) 提供該投射陰影的窗口的接口函數(shù),當(dāng)投射陰影的窗口位置改變的時(shí)候便調(diào)用這個(gè)函數(shù)把新的窗口位置傳給Shadow,Shadow便會(huì)改變自己的位置。注意了,這里并不需要重繪窗口,因?yàn)榇翱诘膬?nèi)容并沒有改變。 void Shadow::SetAlpha( int alpha ) 提供該投射陰影的窗口的接口函數(shù),當(dāng)投射陰影的窗口的透明度改變的時(shí)候便調(diào)用這個(gè)函數(shù)把新透明度傳給Shadow,Shadow便會(huì)改變自己的透明度,并重繪窗口。 下面給出一個(gè)我自己寫的程序的效果圖:
3. 結(jié)論: 上面就簡(jiǎn)單地介紹了一個(gè)繪制窗口陰影的方法。這種方法基本上可以適用于各種類型的窗口,其中需要注意一下幾點(diǎn): 1. 在于Shadow::CreateShadow中如何正確取得投射陰影窗口m_pShadowCastingWindow的大小然后計(jì)算出陰影窗口的大小。 2. Shadow::CustomizedPaint中如何更高效的繪制陰影,例如剪裁掉投射陰影窗口遮擋住的窗口內(nèi)容,避免繪制時(shí)出現(xiàn)閃爍。同時(shí)如何正確使用好UpdateLayeredWindow這個(gè)系統(tǒng)調(diào)用會(huì)是實(shí)現(xiàn)繪制陰影的關(guān)鍵。當(dāng)然在當(dāng)前的設(shè)計(jì)下,我們可以在CustomizePaint中繪制任何的東西,而不一定是陰影。? 大家可以在這里發(fā)揮想象力,讓窗口更加絢麗多彩。 其實(shí)這個(gè)程序只要讓他通過鉤子函數(shù)與特定的Win32API掛鉤,完全可以寫出一個(gè)可以給系統(tǒng)中所有窗口加上陰影效果的小軟件。大家不妨試試。如果做出來了,記得給我一份。 注:文章中的代碼都是示意性的,都是通過我自己寫的程序刪減后得到,未必能通過測(cè)試。旨在說明一些關(guān)鍵步驟需要注意的地方。如果問題歡迎email討論。 |





