在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);
}
最後顯示結果如下: