为GDI函数增加透明度处理
用户对客户端的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的半透明。
class DCBuffer
{
public:
DCBuffer(HDC hdc,LPCRECT pRect,BYTE byAlpha,BOOL bCopyBits=TRUE)
:m_hdc(hdc)
,m_byAlpha(byAlpha)
,m_pRc(pRect)
,m_bCopyBits(bCopyBits)
{
m_nWid = pRect->right-pRect->left;
m_nHei = pRect->bottom-pRect->top;
m_hBmp = SBitmap_GDI::CreateGDIBitmap(m_nWid,m_nHei,(void**)&m_pBits);
m_hMemDC = ::CreateCompatibleDC(hdc);
::SetBkMode(m_hMemDC,TRANSPARENT);
::SelectObject(m_hMemDC,m_hBmp);
::SetViewportOrgEx(m_hMemDC,-pRect->left,-pRect->top,NULL);
//从原DC中获得画笔,画刷,字体,颜色等
m_hCurPen = ::SelectObject(hdc,GetStockObject(BLACK_PEN));
m_hCurBrush = ::SelectObject(hdc,GetStockObject(BLACK_BRUSH));
m_hCurFont = ::SelectObject(hdc,GetStockObject(DEFAULT_GUI_FONT));
COLORREF crCur = ::GetTextColor(hdc); //将画笔,画刷,字体设置到memdc里
::SelectObject(m_hMemDC,m_hCurPen);
::SelectObject(m_hMemDC,m_hCurBrush);
::SelectObject(m_hMemDC,m_hCurFont);
::SetTextColor(m_hMemDC,crCur); if(m_bCopyBits) ::BitBlt(m_hMemDC,pRect->left,pRect->top,m_nWid,m_nHei,m_hdc,pRect->left,pRect->top,SRCCOPY);
//将alpha全部强制修改为0xFF。
BYTE * p= m_pBits+;
for(int i=;i<m_nHei;i++)for(int j=;j<m_nWid;j++,p+=) *p=0xFF;
} ~DCBuffer()
{
//将alpha为0xFF的改为0,为0的改为0xFF
BYTE * p= m_pBits+;
for(int i=;i<m_nHei;i++)for(int j=;j<m_nWid;j++,p+=) *p=~(*p); BLENDFUNCTION bf={AC_SRC_OVER,,m_byAlpha,AC_SRC_ALPHA };
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);
::DeleteDC(m_hMemDC);
::DeleteObject(m_hBmp); //恢复原DC的画笔,画刷,字体
::SelectObject(m_hdc,m_hCurPen);
::SelectObject(m_hdc,m_hCurBrush);
::SelectObject(m_hdc,m_hCurFont);
} operator HDC()
{
return m_hMemDC;
} protected:
HDC m_hdc;
HDC m_hMemDC;
HBITMAP m_hBmp;
LPBYTE m_pBits;
BYTE m_byAlpha;
LPCRECT m_pRc;
int m_nWid,m_nHei;
BOOL m_bCopyBits; HGDIOBJ m_hCurPen;
HGDIOBJ m_hCurBrush;
HGDIOBJ m_hCurFont;
};
下面以实现DrawText的半透明为例来分析如何实现GDI函数的半透明。
HRESULT SRenderTarget_GDI::DrawText( LPCTSTR pszText,int cchLen,LPRECT pRc,UINT uFormat)
{
if(uFormat & DT_CALCRECT)
{
::DrawText(m_hdc,pszText,cchLen,pRc,uFormat);
return S_OK;
} if(cchLen == ) return S_OK; {
DCBuffer dcBuf(m_hdc,pRc,m_curColor.a);
::DrawText(dcBuf,pszText,cchLen,pRc,uFormat);
} return S_OK;
}
首先来看如何解决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函数增加透明度处理的更多相关文章
- JS---最终版本--封装缓动(变速)动画函数---增加任意多个属性&回调函数&层级&透明度
封装缓动(变速)动画函数---增加任意多个属性&回调函数&层级&透明度 相较之前的,增加了2个判断,第一个判断是不是透明度,第二个判断是不是zindex, 都不是,就只是普通属 ...
- destoon手机端mobileurl函数增加城市分类参数
mobileurl函数在include/global.func.php 858行 共四个参数,moduleid-模型id,catid-分类id,itemid -文章id,page-页码 functio ...
- 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 ...
- JS---封装缓动(变速)动画函数---增加任意多个属性&增加回调函数
封装缓动(变速)动画函数---增加任意多个属性&增加回调函数 回掉函数fn,在所有元素到达目的位置后,判断是否传入一个函数,有就调用 if(fn){fn()}; 这样一次点击,产生多个动画 & ...
- JS---封装缓动(变速)动画函数---增加多个任意多个属性
封装缓动动画函数---增加多个任意多个属性 在原来缓动动画函数,增加任意一个属性的基础上,做了如下改变 1. 原来function animate(element, attr, target),三个变 ...
- C++异常机制的实现方式和开销分析 (大图,编译器会为每个函数增加EHDL结构,组成一个单向链表,非常著名的“内存访问违例”出错对话框就是该机制的一种体现)
白杨 http://baiy.cn 在我几年前开始写<C++编码规范与指导>一文时,就已经规划着要加入这样一篇讨论 C++ 异常机制的文章了.没想到时隔几年以后才有机会把这个尾巴补完 :- ...
- JS---封装缓动(变速)动画函数---增加任意一个属性
封装缓动(变速)动画---增加任意一个属性 1. 本来的变速动画函数,是获取特定的属性(之前案例是向右移动,所以获取的是left属性) 2. 现在改变为,获取任意一个属性,使其移动到指定的target ...
- sap中用函数增加断点(break point)
如果在增强程序中,每次调试都要去程序里面设置断点很麻烦,为了解决这个问题,可以用下面的两个方法: 1: if sy-uname eq 'XXXX' "XXX 为账号名字 break ...
- Flask如何给多个视图函数增加装饰器
这几天在学习Flask, 遇到了些小问题,比如说怎么给多个视图函数加相同的装饰器 给单独一个视图函数加装饰器的话很简单,写一个装饰器,然后直接加在原装饰器下面即可,多个的话,会报这样一个错误: 这个异 ...
随机推荐
- SharePoint2010母版页想要的定制
查找<div id="s4-ribbonrow" class="s4-pr s4-ribbonrowhidetitle"用style="disp ...
- Debian安装python-rrdtool
... sudo apt-get install rrdtool sudo apt-get install librrd-dev sudo apt-get install python-dev pip ...
- cf592d
题意:给出一个无根树,点数为10^5,所有边的长度为1.给定其中有一些点是受到攻击的. 现在要求一个人选定一个点作为起点,走遍所有的受攻击点(不用再回到起点). 需要的最短距离是多少,选定的起点是哪个 ...
- hdu2196
基本的树形dp,需要dfs三次,第一次求每个点最远的后代,第二次和第三次每个点的孩子分别从左到右和从右到左遍历. #include <cstdio> #include <vector ...
- hnu10104
AC自动机+DFS #include <cstdio> #include <queue> #include <cstring> using namespace st ...
- linux日常小坑
一.权限 1.文件权限 改动文件权限和所有权的命令有如下两个: chmod -更改权限 chown -更改所有权 不过,只有用户是当前所有者或者根用户,才能实际更改文件的权限或所有权,这一点大家要注意 ...
- string literals may have at most 255 elements
一行中的字符超出 255 时候发生这个错误 解决方式: 一行的字符串分成两行 并 + 连接 **** 将字符串分成两个用"+"连接起来
- ACM/ICPC 之 "嵌套"队列 -插队(POJ2259)
这里插队的意思就是排队时遇到熟人则插到其后,否则排到队尾.(这个习惯不太好)(题意) 题目要求我们模拟“插队模型”和队列的入队和出队完成此算法. 由于题目的输入输出很多,此题的查找操作(找到熟人)需要 ...
- ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构
★PART1:32位的x86处理器执行方式和架构 1. 寄存器的拓展(IA-32) 从80386开始,处理器内的寄存器从16位拓展到32位,命名其实就是在前面加上e(Extend)就好了,8个通用寄存 ...
- Android 启动白屏或者黑屏闪现解决
1.设置Style //1.设置背景图Theme <style name="Theme.AppStartLoad" parent="android:Theme&qu ...