使用透明/半透明窗口/圖片時遇到的一些問題



  最近兩周使用透明、半透明窗口比較多,在此之前我寫代碼都借助封裝好了的皮膚庫,而現在都是“手寫”的——石器時代大冒險,遇到了一些困難,在此做總結。
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消息(很莫名其妙),導致錯誤。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM