实现GDI透明的关键是
代码语言:javascript复制1、需要把内容画到一个临时位图上,同时保护好alpha通道。
2、在于把临时位图的数据和原位图做混合,而且不能改变镂空部分原位图的alpha通道,比如被DrawText修改过的镂空部分
步骤:
代码语言:javascript复制1.创建临时位图,临时DC,临时DC加载临时位图
2.对临时DC设置相关属性(来自原始DC的相关属性,如字体、画刷、颜色).
3.临时DC是否需要先复制原始DC的数据(很多函数如DrawText需要做反锯齿处理,反锯齿处理的关键是和背景色做混合,因此从原位图复制出数据也是很有必要的。需要做反锯齿处理,反锯齿处理的关键也是和背景色做混合,因此从原位图复制出数据).
4.把临时位图的alpha全置为1(GDI函数只会将那些变色的点的alpha通道清0,所以alpha清0的像素点就是需要保留的像素点)
5.绘制相关内容到临时DC.
6.依据3和4,GDI函数处理后未变化的alpha都是1,变化的alpha都为0,将所有的alpha减1,则未变化的alpha都是0,变化的alpha都为0xff((byte)-1).
7.用AlphaBlend做混合,对于那些需要镂空的点,由于临时位图的alpha为0,混合后根据AlphaBlend的公式,即不会改变原来的RGB值,也不会改变原来的alpha值。
对于那些被GDI函数改变过的点,由于其alpha值都变成了255,其RGB部分,AlphaBlend会根据BLENDFUNCTION中指定的alpha值来和原值混合。
代码如下:
代码语言:javascript复制 HDC DMCanvasImpl::AlphaBlendBackup(LPCRECT lpRect,bool bInherit,bool bCopy)
{
SaveCanvas();
m_RcTemp = lpRect;
int nWid = m_RcTemp.Width();
int nHei = m_RcTemp.Height();
m_DIBTemp.CreateDIBSection(m_hdc,nWid,nHei);
HDC dcMem = ::CreateCompatibleDC(m_hdc);
::SetBkMode(dcMem,TRANSPARENT);
::SelectObject(dcMem,m_DIBTemp.m_hBitmap);
::SetViewportOrgEx(dcMem,-lpRect->left,-lpRect->top,NULL);
// 设置三无环境------------------------------------
if (bInherit)
{
::SelectObject(dcMem, m_pCurPen->GetPen());
::SelectObject(dcMem, m_pCurBrush->GetBrush());
::SelectObject(dcMem, m_pCurFont->GetFont());
::SetTextColor(dcMem, m_CurTextColor.ToCOLORREF());
}
else
{
::SelectObject(dcMem,GetStockObject(NULL_PEN));
::SelectObject(dcMem,GetStockObject(NULL_BRUSH));
::SelectObject(dcMem,GetStockObject(NULL_PEN));
::SetTextColor(dcMem,RGBA(0,0,0,0));
}
m_bCopyTemp = bCopy;
if (bCopy)
{
::BitBlt(dcMem,lpRect->left, lpRect->top, nWid, nHei,m_hdc,lpRect->left, lpRect->top, SRCCOPY);
}
BYTE* p = m_DIBTemp.m_pPixelBits 3;
for (int i=0; i<m_DIBTemp.m_nImageSize; i =4,p =4)
{
*p = 1;
}
return dcMem;
}
代码语言:javascript复制bool DMCanvasImpl::AlphaBlendRestore(HDC dcMem,BYTE alpha)
{
int nWid = m_RcTemp.Width();
int nHei = m_RcTemp.Height();
#if 1
BYTE* p = m_DIBTemp.m_pPixelBits 3;
if (m_bCopyTemp)
{
for (int i=0; i<m_DIBTemp.m_nImageSize; i =4,p =4)
{
*p -= 1;
if (0 == *p)
{
memset(p-3,0,3);// 仅xp,win7-32下需要使用此方式
}
//if(*p==0) *p=0xff;
//else memset(p-3,0,4);
}
}
else
{
for (int i=0; i<m_DIBTemp.m_nImageSize; i =4,p =4)
{
*p -= 1;
}
}
#else// MMX只有在大图片时才会更高效,暂时不启用
byte* p = (byte*)m_DIBTemp.m_pPixelBits;
const UINT c_01000000 = 0x01000000;
__asm
{
movd xmm0,c_01000000
pshufd xmm0,xmm0,0
}
int mmxsize = m_DIBTemp.m_nImageSize - m_DIBTemp.m_nImageSize;
for (int i=0; i<mmxsize; i =16)
{
__asm
{
pushad
mov eax,dword ptr [i]
mov ecx,dword ptr [p]
lea edi,[ecx eax]
movdqu xmm1,xmmword ptr [edi]
psubd xmm1,xmm0
movdqu xmmword ptr [edi],xmm1
popad
}
}
__asm
{
emms ;必要的!Empty MMX Status
}
p = m_DIBTemp.m_pPixelBits mmxsize 3;
for (int j=mmxsize;j<m_DIBTemp.m_nImageSize;j =4,p =4)// 余下部分不足16位了,直接for循环
{
*p -= 0x1;
}
#endif
//CRect rcDest = m_RcTemp;OffsetRect(rcDest,m_ptOrg.x,m_ptOrg.y);
//bool bRet = DMDIBHelper::AlphaBlend32(&m_pCurBitmap->m_DibHelper,rcDest.left, rcDest.top, nWid, nWid,&m_DIBTemp,0,0,nWid, nHei,alpha);
BLENDFUNCTION bf = {AC_SRC_OVER,0,alpha,AC_SRC_ALPHA};
BOOL bRet = ::AlphaBlend(m_hdc,m_RcTemp.left,m_RcTemp.top, nWid, nHei,
dcMem,m_RcTemp.left,m_RcTemp.top,nWid, nHei, bf);
::DeleteDC(dcMem);
m_DIBTemp.DIBRelease();
RestoreCanvas();
return TRUE == bRet;
}
调用如下:
代码语言:javascript复制HDC dcMem = AlphaBlendBackup(rcDest,true,true);
DMColor TextClr = m_CurTextColor;
TextClr.PreMultiply(alpha);
::SetTextColor(dcMem, TextClr.ToCOLORREF());
::DrawTextW(dcMem,lpString,nCount,rcDest,uFormat);
AlphaBlendRestore(dcMem,TextClr.a);
再贴一个参考代码
代码语言:javascript复制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 3;
for(int i=0;i<m_nHei;i )for(int j=0;j<m_nWid;j ,p =4) *p=0xFF;
}
~DCBuffer()
{
//将alpha为0xFF的改为0,为0的改为0xFF
BYTE * p= m_pBits 3;
for(int i=0;i<m_nHei;i )for(int j=0;j<m_nWid;j ,p =4) *p=~(*p);
BLENDFUNCTION bf={AC_SRC_OVER,0,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;
};
使用:
代码语言:javascript复制 DCBuffer dcBuf(m_hdc,pRc,m_curColor.a);
::DrawText(dcBuf,pszText,cchLen,pRc,uFormat);