DUI -实现GDI透明效果

实现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

admin
admin管理员

上一篇:成为高级开发者的唯一因素
下一篇:成为高级开发者的唯一因素

留言评论

暂无留言