MFC 透明內存DC

在MFC中繪製比較複雜圖形,通常採用雙緩衝技術來繪圖,的確可以大大加快繪製速度和減少閃爍,但是有些情況也不盡然。

我最近遇到了一個問題,採用的也是雙緩衝來加快繪圖,但是繪製效果還是不盡人意。A對象裏大約有幾百個可以繪畫的對象,每個對象都沒有填充背景,他們的背景是另一對象B。A和B在一個窗口中可能有N個,繪畫時,先繪製B然後在繪製A,只有2、3個A對象的時候,繪畫已經比較慢了,DEBUG下可以明顯感覺到延遲,原因是我可能只改變了對象B或一個A對象,但是需要把所有的對象重新繪畫一邊,效率非常低,即使是用上雙緩衝也不行,PS大家都用過,它裏面有一個叫做透明層的概念,在透明層上畫任何東西不影響下面的層,那麼我們能不能將A對象繪畫在一個"透明層"1上,將B對象繪畫在另一"透明層"2上。改變A對象時只需要重新在"透明層"1重新繪製A對象,而"透明層"2不需要重新繪製,最後先畫透明層1然後再畫透明層2,這樣效率就可以大大提高了。

問題的關鍵之處是創建一個透明位圖,然後在這個透明的位圖上繪製圖形。

注意:演示代碼使用了GDI+,因爲GDI沒有使用ARGB,不會改寫Alpha的值,即使畫了也顯示不出來。

 1、首先寫一個CPngMem類。

class CPngMemDC
{
public:
	CPngMemDC() : m_hBmp(NULL)
	{
	}
	~CPngMemDC()
	{
		if (m_hBmp)
			::DeleteObject(m_hBmp);
	}

	//創建內存DC
	void CreateMemDC(CDC *pDC)
	{
		ASSERT(pDC);

		if (m_MemDC.GetSafeHdc())
			m_MemDC.DeleteDC();
		m_MemDC.CreateCompatibleDC(pDC);
	}

	//創建位圖,並將位圖選進內存DC
	void CreateBitmap(int nWidth, int nHeight)
	{
		BITMAPINFO bi;
		bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bi.bmiHeader.biBitCount = 32;
		bi.bmiHeader.biHeight = nHeight;
		bi.bmiHeader.biWidth  = nWidth;
		bi.bmiHeader.biPlanes = 1;
		bi.bmiHeader.biCompression = BI_RGB;
		bi.bmiHeader.biXPelsPerMeter = 0;
		bi.bmiHeader.biYPelsPerMeter = 0;
		bi.bmiHeader.biClrUsed = 0;
		bi.bmiHeader.biSizeImage = 0;
		bi.bmiHeader.biSizeImage = nWidth * nHeight * bi.bmiHeader.biBitCount / 8;

		if (m_hBmp)
			::DeleteObject(m_hBmp);
		m_hBmp = ::CreateDIBSection(m_MemDC, &bi, 0, NULL, 0, 0);//創建32位位圖

		m_MemDC.SelectObject(m_hBmp);

		m_nWidth = nWidth;
		m_nHeight = nHeight;
	}

	void Draw(CDC *pDC)
	{
		BLENDFUNCTION bf;
		bf.AlphaFormat = AC_SRC_ALPHA;
		bf.BlendFlags = 0;
		bf.BlendOp = AC_SRC_OVER;
		bf.SourceConstantAlpha = 255;
		
		BOOL bRet = pDC->AlphaBlend(0, 0, m_nWidth, m_nHeight,
&m_MemDC, 0, 0, m_nWidth, m_nHeight, bf);
		VERIFY(bRet);
	}

	operator HDC()//重載HDC類型轉換
	{
		return m_MemDC.GetSafeHdc();
	}

private:
	CDC m_MemDC;
	HBITMAP m_hBmp;

	int m_nWidth;
	int m_nHeight;
};

1、在對話框類中添加兩個成員變量:

private:
	CPngMemDC m_pngMem1;
	CPngMemDC m_pngMen2;

2、在OnInitDialog()函數中創建內存DC和位圖:

	CClientDC dc(this);
	CRect rcClient;
	GetClientRect(rcClient);

	m_pngMem1.CreateMemDC(&dc);
	m_pngMem1.CreateBitmap(rcClient.Width(), rcClient.Height());

	m_pngMen2.CreateMemDC(&dc);
	m_pngMen2.CreateBitmap(rcClient.Width(), rcClient.Height());

3、添加OnBnClickedOk()按鈕響應函數:

void CDlg::OnBnClickedOk()
{
	Graphics g1(m_pngMem1);
	Pen pen1(Color(255, 255, 0, 0), 5);//紅色
	g1.DrawLine(&pen1, Point(100, 0), Point(100, 300));

	Graphics g2(m_pngMen2);
	Pen pen2(Color(0, 255, 0), 5);//綠色
	g2.DrawLine(&pen2, Point(0, 150), Point(300, 150));

	CClientDC dc(this);
	
	m_pngMem1.Draw(&dc);
	m_pngMen2.Draw(&dc);
}

最後顯示結果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章