为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, 遇到了些小问题,比如说怎么给多个视图函数加相同的装饰器 给单独一个视图函数加装饰器的话很简单,写一个装饰器,然后直接加在原装饰器下面即可,多个的话,会报这样一个错误: 这个异 ...
随机推荐
- Java使用for循环打印乘法口诀(正倒左右三角形)
代码1: public void test1(){ for(int i = 1; i < 10 ; i ++){ for(int k = 1; k < i ; k ++){ System. ...
- iOS self = [super init]
self = [super init] 这个问题一直不太明白,今天研究了一下,在stackoverflow找到了下面的答案: http://stackoverflow.com/questions/29 ...
- redis 中文字符显示
2015年5月20日 09:57:01 星期三 方法一: redis-cli -h --raw 方法二: linux 终端: echo -e "\xe7\xa5\x9e\xe6\xa3\x8 ...
- gtk+-3.21.4 static build step in windows XP
In recent days the weather is very hot Unable to sleep properly Under the state of daze research gtk ...
- Effective C++ -----条款39:明智而审慎地使用private继承
Private继承意味is-implemented-in-terms of(根据某物实现出).它通常比复合(composition)的级别低.但是当derived class需要访问protected ...
- HDU 4966 GGS-DDU(最小树形图)
n个技能,每个技能有0-a[i]的等级,m个课程,每个课程需要前置技能c[i]至少达到lv1[i]等级,效果是技能d[i]达到lv2[i]等级,花费w[i]. 输出最小花费使得全技能满级(初始全技能0 ...
- Divide and conquer:4 Values whose Sum is 0(POJ 2785)
找四个数的和为0 题目大意:给定四个集合,要你每个集合选4个数字,组成和为0 这题是3977的简单版,只要和是0就可以了 #include <iostream> #include < ...
- C Primer Plus_第二章_C语言概述_复习题与编程练习
REVIEW 1.如何称呼C程序的基本模块? ans 它们被称为函数 2.什么是语法错误?给出一个英语例子和一个C语言例子 me C的语法错误是指把正确的C符号放在了错误的位置 likes codin ...
- 【剑指offer】题目20 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出 ...
- 【linux】jdk安装
1.在http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下载最新版的rpm文件,我 ...