VC++雙緩沖保持背景不擦除之實現


幾天前,我終於克服了C++窗體重繪時的閃爍問題,用到的技巧就是雙緩沖。但是怎樣保持住已經繪制的圖形呢?也就是仿照Windows自帶的畫圖程序一般,動態的做出一條直線。最容易想到的方法是在MouseMove過程中,不斷擦除上次所畫的線,然后再畫出新的直線,只須增件變量保存開始的點和上次的點即可。這樣做的確可以實現動態畫線的功能,但是有兩點不足之處。一是代碼沒有放在OnDraw過程中,窗體重繪時,先前所有的圖形將被擦除;二是擦除上次直線的時候,難免同之前畫好的線相交,將上次的線恢復為背景色的時候,很有可能也切斷了之前已經做好的線。

  解決這一問題的辦法依然是使用雙緩沖,和上次不同的上,用於緩沖的內存DC和位圖要保持住,而非隨用隨建。為了解決第一點不足,需要建立一個內存DC和位圖,並在OnDraw過程中將其拷貝到前台。這樣歐文們作圖的時候將圖做在內存DC上,然后使窗口刷新,便看到了所做的圖了。當窗體移動或被遮擋時,窗口需要重繪,只是把我們的緩沖重新繪制一遍,因此已經畫好的線不會被擦除。解決第二的問題的方法主要有兩種,一種是設置畫筆的模式為異或模式,這樣的話在同意位置畫兩次的話等同於什么都沒畫。但是這樣會犧牲線條本身的顏色屬性,如果背景是單一的顏色,自然是看不出來的,但是如果背景顏色豐富,那我們的線條也就隨着多姿多彩了;第二種方法是比較好用的臟矩形法,臟矩形法的主要內容就是每次畫面的刷新只更新需要更新的那一塊區域,這正是Flash采用的方式,效率比較高。在本程序中為簡單起見,沒有采用完全的臟矩形法,臟矩形始終定義為整個客戶區大小。為此,需要重新定義一個新的緩沖區,用來保存即將變臟的矩形,以備之后恢復所用。在響應WM_MOUSEMOVE消息的過程中,先將新緩沖區上的圖形還原到舊緩沖區上,這樣可以遮擋住上次的線條。再在舊緩沖區上作圖,然后刷新窗口,我們便看到了動態效果。不知道這是不是傳說中的三緩沖技術呢?

  以下是代碼的分析:

CBitmap memBakBMP; //新緩沖區內存DC
CDC memBakDC;         //新緩沖區用的位圖
BOOL bClicked;           //判斷是否應當作圖
CPoint ptBegin;           //記錄圖象開始的位置
CBitmap memBMP;     //新緩沖區用的位圖
CDC memDC;            //舊緩沖區內存DC

 

響應WM_CREATE消息的時候做初始化工作:

int CNSSDrawerView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
   return -1;
memDC.CreateCompatibleDC(NULL);
memBMP.CreateCompatibleBitmap(&memDC,1024,768);
memDC.SelectObject(&memBMP);
memDC.FillSolidRect(0,0,1024,768,GetDC()->GetBkColor());
memBakDC.CreateCompatibleDC(NULL);
memBakBMP.CreateCompatibleBitmap(&memBakDC,1024,768);
memBakDC.SelectObject(&memBakBMP);
return 0;
}

 

相應的,在響應WM_DESTROY消息的時候,也要做善后工作:

void CNSSDrawerView::OnDestroy()
{
CView::OnDestroy();
memDC.DeleteDC();
memBMP.DeleteObject();
memBakDC.DeleteDC();
memBakBMP.DeleteObject();
}

 

干預WM_ERASEBKGND消息的響應,這里是最容易忽略的地方,要特別注意

BOOL CNSSDrawerView::OnEraseBkgnd(CDC* pDC)
{
return TRUE;                     //CView::OnEraseBkgnd(pDC);
}

 

部分變量在構造函數中初始化:

CNSSDrawerView::CNSSDrawerView()
{
ptBegin = 0;
bClicked = FALSE;
}

 

響應WM_LBUTTONDOWN消息,着是畫線的開始:

void CNSSDrawerView::OnLButtonDown(UINT nFlags, CPoint point)
{
ptBegin = point;
bClicked = TRUE;
CRect rect;
GetClientRect(&rect);
memBakDC.BitBlt(0,0,rect.Width(),rect.Height(),&memDC,0,0,SRCCOPY);
CView::OnLButtonDown(nFlags, point);
}

 

相應的,WM_LBUTTONUP消息的響應是畫線的結束:

void CNSSDrawerView::OnLButtonUp(UINT nFlags, CPoint point)
{
bClicked = FALSE;
CView::OnLButtonUp(nFlags, point);
}

 

最主要的部分在響應WM_MOUSEMOVE消息的模塊中:

void CNSSDrawerView::OnMouseMove(UINT nFlags, CPoint point)
{
if(bClicked)
{
   CRect rect;
   GetClientRect(&rect);
   memDC.BitBlt(0,0,rect.Width(),rect.Height(),&memBakDC,0,0,SRCCOPY);
   memDC.MoveTo(ptBegin);
   memDC.LineTo(point);
   Invalidate();
}
CView::OnMouseMove(nFlags, point);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM