用户对客户端的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函数增加透明度处理的更多相关文章

  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. Unity3d《Shader篇》Logo闪光特效

    Shader "Custom/Flash" { Properties { _MainTex ("Base (RGB)", 2D) = "white&q ...

  2. 4.nodejs权威指南--TCP和UDP

    1. TCP和UDP 1.1 TCP服务端 var net = require('net'); var server = net.createServer(); server.on('connecti ...

  3. Redis Sentinel高可用架构

    Redis目前高可用的架构非常多,比如keepalived+redis,redis cluster,twemproxy,codis,这些架构各有优劣,今天暂且不说这些架构,今天主要说说redis se ...

  4. Effective C++ -----条款25:考虑写出一个不抛异常的swap函数

    当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者.对于cla ...

  5. uva 401.Palindromes

    题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem ...

  6. hdu 1002.A + B Problem II 解题报告

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1002 题目意思:就是大整数加法. 两年几前做的,纯粹是整理下来的. #include <stdi ...

  7. (2016弱校联盟十一专场10.2) E.Coins

    题目链接 很久之前写的了,好像是对拍打表过的,推一下就行了. #include <bits/stdc++.h> using namespace std; typedef long long ...

  8. 25个增强iOS应用程序性能的提示和技巧(高级篇)(1)

    25个增强iOS应用程序性能的提示和技巧(高级篇)(1) 2013-04-16 14:56 破船之家 beyondvincent 字号:T | T 在开发iOS应用程序时,让程序具有良好的性能是非常关 ...

  9. gcc创建和使用静态库、动态库

    http://www.cnblogs.com/dyllove98/archive/2013/06/25/3155599.html 目录树结构: test/include/hello.h #ifdef ...

  10. 51nod1019逆序数(归并排序/树状数组)

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1019 题意:中文题诶- 思路: 方法1:归并排序- 归并排序过 ...