用户对客户端的UI的要求越来越高,采用alpha通道对前景背景做混合是提高UI质量的重要手段。

UI开发离不开GDI,然后要用传统的GDI函数来处理alpha通道通常是一个恶梦:虽然有AlphaBlend这个API可以做alpha混合,但是前提必须是操作的DC中的位图有alpha通道的数据,问题的关键在于GDI函数在操作的地方会把原来的alpha通道清空。

使用GDI做alpha混合还要增加透明度关键要解决2个问题:

1、需要把内容画到一个临时位图上,同时保护好alpha通道。

2、在于把临时位图的数据和原位图做混合,而且不能改变镂空部分原位图的alpha通道的值。

在SOUI的render-gdi中我采用下面的类来实现GDI的半透明。

  1. class DCBuffer
  2. {
  3. public:
  4. DCBuffer(HDC hdc,LPCRECT pRect,BYTE byAlpha,BOOL bCopyBits=TRUE)
  5. :m_hdc(hdc)
  6. ,m_byAlpha(byAlpha)
  7. ,m_pRc(pRect)
  8. ,m_bCopyBits(bCopyBits)
  9. {
  10. m_nWid = pRect->right-pRect->left;
  11. m_nHei = pRect->bottom-pRect->top;
  12. m_hBmp = SBitmap_GDI::CreateGDIBitmap(m_nWid,m_nHei,(void**)&m_pBits);
  13. m_hMemDC = ::CreateCompatibleDC(hdc);
  14. ::SetBkMode(m_hMemDC,TRANSPARENT);
  15. ::SelectObject(m_hMemDC,m_hBmp);
  16. ::SetViewportOrgEx(m_hMemDC,-pRect->left,-pRect->top,NULL);
  17. //从原DC中获得画笔,画刷,字体,颜色等
  18. m_hCurPen = ::SelectObject(hdc,GetStockObject(BLACK_PEN));
  19. m_hCurBrush = ::SelectObject(hdc,GetStockObject(BLACK_BRUSH));
  20. m_hCurFont = ::SelectObject(hdc,GetStockObject(DEFAULT_GUI_FONT));
  21. COLORREF crCur = ::GetTextColor(hdc);
  22.  
  23. //将画笔,画刷,字体设置到memdc里
  24. ::SelectObject(m_hMemDC,m_hCurPen);
  25. ::SelectObject(m_hMemDC,m_hCurBrush);
  26. ::SelectObject(m_hMemDC,m_hCurFont);
  27. ::SetTextColor(m_hMemDC,crCur);
  28.  
  29. if(m_bCopyBits) ::BitBlt(m_hMemDC,pRect->left,pRect->top,m_nWid,m_nHei,m_hdc,pRect->left,pRect->top,SRCCOPY);
  30. //将alpha全部强制修改为0xFF。
  31. BYTE * p= m_pBits+;
  32. for(int i=;i<m_nHei;i++)for(int j=;j<m_nWid;j++,p+=) *p=0xFF;
  33. }
  34.  
  35. ~DCBuffer()
  36. {
  37. //将alpha为0xFF的改为0,为0的改为0xFF
  38. BYTE * p= m_pBits+;
  39. for(int i=;i<m_nHei;i++)for(int j=;j<m_nWid;j++,p+=) *p=~(*p);
  40.  
  41. BLENDFUNCTION bf={AC_SRC_OVER,,m_byAlpha,AC_SRC_ALPHA };
  42. BOOL bRet=::AlphaBlend(m_hdc,m_pRc->left,m_pRc->top,m_nWid,m_nHei,m_hMemDC,m_pRc->left,m_pRc->top,m_nWid,m_nHei,bf);
  43. ::DeleteDC(m_hMemDC);
  44. ::DeleteObject(m_hBmp);
  45.  
  46. //恢复原DC的画笔,画刷,字体
  47. ::SelectObject(m_hdc,m_hCurPen);
  48. ::SelectObject(m_hdc,m_hCurBrush);
  49. ::SelectObject(m_hdc,m_hCurFont);
  50. }
  51.  
  52. operator HDC()
  53. {
  54. return m_hMemDC;
  55. }
  56.  
  57. protected:
  58. HDC m_hdc;
  59. HDC m_hMemDC;
  60. HBITMAP m_hBmp;
  61. LPBYTE m_pBits;
  62. BYTE m_byAlpha;
  63. LPCRECT m_pRc;
  64. int m_nWid,m_nHei;
  65. BOOL m_bCopyBits;
  66.  
  67. HGDIOBJ m_hCurPen;
  68. HGDIOBJ m_hCurBrush;
  69. HGDIOBJ m_hCurFont;
  70. };

下面以实现DrawText的半透明为例来分析如何实现GDI函数的半透明。

  1. HRESULT SRenderTarget_GDI::DrawText( LPCTSTR pszText,int cchLen,LPRECT pRc,UINT uFormat)
  2. {
  3. if(uFormat & DT_CALCRECT)
  4. {
  5. ::DrawText(m_hdc,pszText,cchLen,pRc,uFormat);
  6. return S_OK;
  7. }
  8.  
  9. if(cchLen == ) return S_OK;
  10.  
  11. {
  12. DCBuffer dcBuf(m_hdc,pRc,m_curColor.a);
  13. ::DrawText(dcBuf,pszText,cchLen,pRc,uFormat);
  14. }
  15.  
  16. return S_OK;
  17. }

首先来看如何解决alpha通道的保护问题。

为了在目标HDC上调用DrawText绘制文字,先声明一个DCBuffer对象:dcBuf。

DCBuffer的构造函数中,我们会创建一个临时的32位位图。

再将原DC中的数据复制到临时位图中(注意,原位图也是32位的)。

一个非常重要的工作在于在调用GDI的DrawText之前,DCBuffer会先把临时位图中alpha通道置为255。这样做的目的在于标识哪些像素被DrawText修改过。

在调用了::DrawText后,SRenderTarget_GDI::DrawText会进入DCBuffer的析构函数。

在析构函数中,首先对alpha通道中的值取反,经过这一步操作,被::DrawText清空的点的alpha通道值被修改成255,而那些需要透明的点的alpha值则变成了0。

到这里已经实现了对alpha通道的保护。

有了前面的基础,做第二步的alphablend就简单了,这里只需要直接调用API:AlphaBlend,注意BLENDFUNCTION中几个参数的设置。

下面解释一下为什么需要做上面的处理就可以实现GDI函数的半透明。

首先如DrawText这样的GDI函数通常会产生透明效果:即矩形中的一部分点变色,而其它点不变色。

GDI函数只会将那些变色的点的alpha通道清0。我们的目标则是将变色的点的RGB值与目标做alpha混合。

通过将临时位图中的alpha值做取反处理,被GDI函数修改过的点的alpha变为255,而需要镂空的点的alpha则变为了0。

此时再调用用AlphaBlend做混合,对于那些需要镂空的点,由于临时位图的alpha为0,混合后根据AlphaBlend的公式,即不会改变原来的RGB值,也不会改变原来的alpha值。

对于那些被GDI函数改变过的点,由于其alpha值都变成了255,其RGB部分,AlphaBlend会根据BLENDFUNCTION中指定的alpha值来和原值混合,而alpha部分则被修改为255。

最终达到半透明效果。

注:DCBuffer中CopyBits这一步有时候不是必须的。不过很多函数如DrawText需要做反锯齿处理,反锯齿处理的关键也是和背景色做混合,因此从原位图复制出数据也是很有必要的。

如果用GDI+也可以达到相同的效果,但是GDI+出了名的效率低,不知道GDI函数经过如此处理后效率会不会比GDI+慢,从我目前简单的测试来看,效果还是很好的,效率也很高,有兴趣的朋友可以比较一下。

为GDI函数增加透明度处理的更多相关文章

  1. JS---最终版本--封装缓动(变速)动画函数---增加任意多个属性&回调函数&层级&透明度

    封装缓动(变速)动画函数---增加任意多个属性&回调函数&层级&透明度 相较之前的,增加了2个判断,第一个判断是不是透明度,第二个判断是不是zindex, 都不是,就只是普通属 ...

  2. destoon手机端mobileurl函数增加城市分类参数

    mobileurl函数在include/global.func.php 858行 共四个参数,moduleid-模型id,catid-分类id,itemid -文章id,page-页码 functio ...

  3. Flask框架实现给视图函数增加装饰器操作示例

    在@app.route的情况下增加装饰器的写法: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 2 ...

  4. JS---封装缓动(变速)动画函数---增加任意多个属性&增加回调函数

    封装缓动(变速)动画函数---增加任意多个属性&增加回调函数 回掉函数fn,在所有元素到达目的位置后,判断是否传入一个函数,有就调用 if(fn){fn()}; 这样一次点击,产生多个动画 & ...

  5. JS---封装缓动(变速)动画函数---增加多个任意多个属性

    封装缓动动画函数---增加多个任意多个属性 在原来缓动动画函数,增加任意一个属性的基础上,做了如下改变 1. 原来function animate(element, attr, target),三个变 ...

  6. C++异常机制的实现方式和开销分析 (大图,编译器会为每个函数增加EHDL结构,组成一个单向链表,非常著名的“内存访问违例”出错对话框就是该机制的一种体现)

    白杨 http://baiy.cn 在我几年前开始写<C++编码规范与指导>一文时,就已经规划着要加入这样一篇讨论 C++ 异常机制的文章了.没想到时隔几年以后才有机会把这个尾巴补完 :- ...

  7. JS---封装缓动(变速)动画函数---增加任意一个属性

    封装缓动(变速)动画---增加任意一个属性 1. 本来的变速动画函数,是获取特定的属性(之前案例是向右移动,所以获取的是left属性) 2. 现在改变为,获取任意一个属性,使其移动到指定的target ...

  8. sap中用函数增加断点(break point)

    如果在增强程序中,每次调试都要去程序里面设置断点很麻烦,为了解决这个问题,可以用下面的两个方法: 1: if sy-uname eq 'XXXX'      "XXX 为账号名字 break ...

  9. Flask如何给多个视图函数增加装饰器

    这几天在学习Flask, 遇到了些小问题,比如说怎么给多个视图函数加相同的装饰器 给单独一个视图函数加装饰器的话很简单,写一个装饰器,然后直接加在原装饰器下面即可,多个的话,会报这样一个错误: 这个异 ...

随机推荐

  1. JAVA调用动态链接库DLL之JNative学习

    package com.ehfscliax; import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import ...

  2. Expression Add Operators

    Given a string that contains only digits 0-9 and a target value, return all possibilities to add bin ...

  3. KendoUI之kendoGrid服务端分页

    parameterMap:设定传递给服务器的当前页数与每页大小,django下用get方法有效,post方法无法取得这2个参数shema.total:设定总行数serverPaging: true / ...

  4. jenkins集成ansible注意事项Failed to connect to the host via ssh.

    在集成jenkins和ansible实现自动化部署时,root用户下执行ansible命令时可以正常运行.由于是通过jenkins用户去执行ansible命令,而jenkins用户却报如下异常: XX ...

  5. Google Code Jam 2015 R2 C

    题意:给出若干个句子,每个句子包含多个单词.确定第一句是英文,第二句是法文.后面的句子两者都有可能.两个语种会有重复单词. 现在要找出一种分配方法(给每个句子指定其文种),使得既是英文也是法文的单词数 ...

  6. 6. javacript高级程序设计-面向对象设计

    1. 面向对象设计 1.1 理解对象 1.1.1 属性类型 (1). 数据属性:相当于对象的字段,包含一个数据值的位置,在这个位置可以读取和写入值.数据属性中有4个描述其行为的特性: l [[Conf ...

  7. C Primer Plus_第四章_字符串和格式化输入输出_编程练习

    Practice 1.输入名字和姓氏,以"名字,姓氏"的格式输出打印. #include int main(void) { char name[20]; char family[2 ...

  8. android中判断网络连接是否可用

    一.判断网络连接是否可用 public static boolean isNetworkAvailable(Context context) { ConnectivityManager cm = (C ...

  9. python数据库(mysql)操作

    http://fantefei.blog.51cto.com/2229719/1282443

  10. August 11th 2016, Week 33rd Thursday

    A particular fine spring came around. 转眼又是一番分外明媚的春光. Hey, it is hot outside, sometimes even unbearab ...