最近兩周使用透明、視窗半透明效果比較多,在此之前我寫代碼都藉助封裝好了的皮膚庫,而現在都是“手寫”的——石器時代大冒險,遇到了一些困難,在此做總結。
1、GDI函數對Alpha值的忽視。
GDI函數只有AlphaBlend api可以提供alpha通道的繪製,使用AlphaBlend可以實現32位位元影像的繪製。因為只有這個api能識別alpha通道,如果在一個MemDC上用DrawText繪製文本,這些文字地區的alpha值都為0,之後再使用AlphaBlend api把MemDC的位元影像拷貝到實際DC,就會導致文字地區異常,要麼是透明了,要麼是變成純白色了。解決辦法是:1、在能達到目的的前提下,從MemDC拷貝到實際DC時,不用AlphaBlend混合拷貝,而是使用Bitblt簡單拷貝;2、1可能無法達到想要的半透明效果,那麼就只能在混合拷貝之前遍曆文字地區的每一個像素,把它的alpha值修改為255[可參考http://bbs.csdn.net/topics/360021944];3、最方便的做法是引入GDI+,使用CImage,GDI+的文本繪製帶有Alpha屬性值。
2、AlphaBlend API和透明度
Alpha通道值。0表示完全透明,255表示完全不透明。
AlphaBlend函數裡的BLENDFUNCTION結構體裡的SourceConstantAlpha成員,指示的是整個圖片的Alpha值。0,表示源圖片完全透明,255,表示使用源圖片的每個像素自己的Alpha值。
AlphaBlend函數產生的圖片讓源、目標、SourceConstantAlpha混合。有一套複雜的計算:http://msdn.microsoft.com/en-us/library/windows/desktop/dd183393(v=vs.85).aspx
計算涉及到這麼幾種情況:
1、沒有設定AC_SRC_ALPHA 屬性,只有SourceConstantAlpha做混合;
2、設定了AC_SRC_ALPHA 屬性,同時把SourceConstantAlpha設定為0xFF,這時候,只使用源圖片每個像素的Alpha值做混合;
3、設定了AC_SRC_ALPHA屬性,同時把SourceConstantAlpha設定為非0xFF,這時候既使用SourceConstantAlpha的值也使用源圖片每個像素的Alpha值做混合。
按照MSDN公式的介紹,當SourceConstantAlpha的值為0的時候,則源DC的圖片在目標DC上完全不顯示了,源DC的RGB值都丟失了。當SourceConstantAlpha為255時,則使用源DC的圖片的Alpha值。
當使用AlphaBlend的時候,只是把資料從一個DC拷貝到另一個DC。所以,如果是要把一個圖片貼到目標DC,必須是先把圖片選入到MemDC,然後再使用AlphaBlend函數把圖片從MemDC拷貝到實際DC。
使用這個API,可以實現圖片切換時的平緩過渡效果,上一個圖片慢慢的消失,下一個圖片慢慢的呈現。3、AlphaBlend失敗的可能原因
參數錯誤失敗原因1: 如果位元影像的bmBitsPixel為24,那麼是無法使用該函數的。
所以對於來源未知的圖片,安全的做法是
BITMAP bitmapInfo;
:: GetObject(hBitMap, sizeof( BITMAP), &bitmapInfo);
if (bitmapInfo. bmBitsPixel == 24) //24位的位元影像就直接使用Bitblt/StretchBlt
{...........}
參數錯誤失敗原因2:可能參數使用錯了,源dc的位元影像跟參數裡面指示源位元影像高度、寬度的值不相符。
繪製效果很奇怪:之前以為使用了AlphaBlend之後就沒法使用Bitblt了,實際上是可以的,並不會導致alpha通道丟失。倒是這樣子會有問題:通過alphablend往memdc上繪製圖片,然後使用DrawText往memdc上繪製文字,最後使用alphablend把memdc的內容拷貝到實際DC,這就會導致文字變成白色的。所以,最後一步不能使用alphablend,必須使用bitblt這類函數。
4、GDI+繪製文本
GDI+的DrawString可以繪製文本,如果用GDI+繪製文本再加上使用WS_EX_LAYER屬性,那麼GDI+繪製的文本必須使用UpdateLayeredWindow拷貝到目的地區域。不能直接在有WS_EX_LAYER屬性的視窗上使用GDI+繪製,必須先繪製然後使用UpdateLayeredWindow函數拷貝。
5、使用GDI+
VS2008需要加上標頭檔:#include <objbase.h> GDI+初始化:ULONG_PTR m_GdiToken; GdiplusStartupInput m_GdiInput; GdiplusStartup(&m_GdiToken ,&m_GdiInput ,NULL ); GDI+銷毀GdiplusShutdown(m_GdiToken );
6、WS_EX_LAYERED視窗無法顯示
只有WS_EX_LAYERED屬性,而沒有調用SetLayeredWindowAttribute或者UpdateLayeredWindow函數,那將導致視窗無法顯示。
7、如何繪製部分透明的視窗
1)切出部分透明的圖;
2)建立有WS_EX_LAYERED屬性的視窗;
3)把圖片選進MemDC,使用AlphaBlend函數把圖片混合拷貝到另一個MemDC;
4)由於GDI的DrawText函數會把該地區像素的alpha值清0,導致透明,所以建議使用GDI+的DrawString函數;
5)最後使用UpdateLayeredWindow把MemDC拷貝到實際DC。
8、SetLayeredWindowAttributes和UpdateLayeredWindow的區別
兩者在win7以下的版本都要求必須是帶有WS_EX_LAYERED屬性的頂層視窗(非WS_CHILD屬性的視窗),windows 8之後的版本開始支援非頂層視窗。
SetLayeredWindowAttributes函數可以實現:1、對某一個RGB值實現透明;2、可以實現對整個視窗都使用同一個透明度。
UpdateLayeredWindow函數可以實現局部半透明。而且這個函數是GDI+的函數,使用時需要初始化GDI+。
9、 UpdateLayeredWindow不支援局部更新
UpdateLayeredWindow函數包含了SetWindowPos設定視窗位置、大小的功能。這個函數不支援局部更新繪製地區。
10、不能把MAKEINTRESOURCE之後的值賦值給wstring
因為這不是一個string,如果發生賦值,會導致觸發WM_PAINT訊息(很莫名其妙),導致錯誤。