实现GDI透明的关键是
1、需要把内容画到一个临时位图上,同时保护好alpha通道。 2、在于把临时位图的数据和原位图做混合,而且不能改变镂空部分原位图的alpha通道,比如被DrawText修改过的镂空部分
步骤:
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值来和原值混合。
代码如下:
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; }
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%16; 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; }
调用如下:
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);
再贴一个参考代码
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; };
使用:
DCBuffer dcBuf(m_hdc,pRc,m_curColor.a); ::DrawText(dcBuf,pszText,cchLen,pRc,uFormat);合智互联客户成功服务热线:400-1565-661
留言评论
暂无留言