• 使用透明/半透明窗口/图片时遇到的一些问题



      最近两周使用透明、半透明窗口比较多,在此之前我写代码都借助封装好了的皮肤库,而现在都是“手写”的——石器时代大冒险,遇到了一些困难,在此做总结。
    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消息(很莫名其妙),导致错误。
  • 相关阅读:
    常见的单链表题目
    SpringBoot Hello
    IDEA 重置
    lombok的用法
    软件测试系列白盒测试覆盖率的问题
    软件测试系列软件测试基础
    Linux常用命令1对文件进行查看、复制、移动和分割
    软件测试系列通用测试用例写作
    Java继承特性
    Linux常用命令3如何设置IP地址?如何更改系统时间?
  • 原文地址:https://www.cnblogs.com/cswuyg/p/3102881.html
Copyright © 2020-2023  润新知