最近两周使用透明、半透明窗口比较多,在此之前我写代码都借助封装好了的皮肤库,而现在都是“手写”的——石器时代大冒险,遇到了一些困难,在此做总结。
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消息(很莫名其妙),导致错误。
posted on 2013-05-28 01:18  烛秋  阅读(7303)  评论(0编辑  收藏  举报