MFC 雙緩衝 繪圖時屏幕閃爍問題

繪圖時屏幕閃爍的原因分析

很長一段時間我一直認爲繪圖過程中出現的屏幕閃爍是圖形刷新速度過快而造成的(相信有很多朋友也跟我一樣有這樣的想法),但是通過編寫一些繪圖程序,我發現事情並非如此,至少刷新速度快不會是造成屏幕閃爍的根本原因。這個問題可以通過下面的兩個試驗進行說明。
1、編寫一個刷新速度很慢的應用程序,可以設計爲通過點擊鼠標來進行屏幕刷新。通過該試驗可以發現即使屏幕的刷新速度很慢,但是在每次刷新的時候仍然存在屏幕的問題,只是閃爍不是很明顯。
2、編寫一個刷新速度很快的應用程序,並在程序中應用雙緩衝圖形刷新技術。通過該試驗可以發現雖然屏幕刷新速度很快,但是採用了雙緩衝圖新刷新技術以後,屏幕不存在閃爍。
屏幕閃爍的根本原因是相鄰兩幀圖像之間存在的巨大差異造成的,而windows的圖形刷新方式使得任何兩幀圖像之間都存在着巨大的差異,因爲windows在進行刷新之前都會首先將整個屏幕刷成白色,就相當於在電影膠片的相鄰兩幀之間都插入了一個白色的幀,這也就是爲什麼屏幕閃爍時總是看到一個隱約的白色窗口在閃爍而不是一個紅色的窗口在閃爍。雙緩衝圖形刷新避免了windows刷新的問題,其沒有在連續的兩幀之間插入白色的幀,從而解決了屏幕閃爍的問題。


雙緩衝圖形刷新的原理

雙緩衝圖形刷新顧名思義是採用雙緩存實現的。傳統的繪圖方式實際上是一種單緩衝。在windows中每一種設備都在內存中有一個設備描述表與其對應,這個設備描述表實際上就是一個內存緩衝區。傳統的繪圖中我們是將圖形繪製在設備描述表緩衝區中,然後由gdi自動的將設備描述表中的圖像拷貝到顯存中進行顯示。這樣一個自動的拷貝過程屏蔽了傳統的繪圖方式是單緩衝的實質,使我們感覺到我們是在直接操縱顯存一樣。雙緩衝圖形刷新技術在內存中有兩片緩存,除了設備描述表以外還有一個需要手動建立的與設備描述表緩衝區(前端緩衝區)相兼容的後備緩衝區。繪圖過程中,首先將圖形繪製在後備緩衝區中,然後在手動的將後備緩衝區中的圖像拷貝到前端緩衝區中,再由gdi自動將前端緩衝區中的圖像拷貝到顯存完成圖形的顯示過程。
雙緩衝圖形刷新的實現步驟
1、創建與窗口設備描述表(前端緩衝區)兼容的內存設備描述表(後端緩衝區)
2、創建與內存設備描述表相兼容的位圖並將該位圖選入內存設備描述表中(沒有位圖的設備描述表是不能繪圖的)
3、將圖形繪製在內存設備描述表中
4、將內存設備描述表中的內容拷貝到窗口設備描述表
5、釋放設備描述表句柄、位圖等資源


在vc 6.0中結合mfc實現雙緩衝圖形刷新技術

1、首先在OnDraw()或者OnPaint()中添加下列代碼
  1. void OnDraw(CDC *pDC)  
  2. {  
  3. //定義一個內存設備描述表對象(即後備緩衝區)  
  4. CDC MemDC;   
  5. //定義一個位圖對象  
  6. CBitmap MemBitmap;  
  7. //建立與屏幕設備描述表(前端緩衝區)兼容的內存設備描述表句柄(後備緩衝區)  
  8. MemDC.CreateCompatibleDC(NULL);  
  9. //這時還不能繪圖,因爲沒有位圖的設備描述表是不能繪圖的  
  10. //下面建立一個與屏幕設備描述表(或者內存設備描述表)兼容的位圖  
  11. MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);  
  12. //將位圖選入到內存設備描述表  
  13. //只有選入了位圖的設備描述表纔有地方繪圖,畫到指定的位圖上  
  14. CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);  
  15. //先用背景色將位圖清除乾淨,這裏我用的是白色作爲背景  
  16. //你也可以用自己應該用的顏色  
  17. MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));  
  18. //繪圖  
  19. MemDC.MoveTo(……);  
  20. MemDC.LineTo(……);  
  21. //將後備緩衝區中的圖形拷貝到前端緩衝區  
  22. pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);  
  23. //繪圖完成後的清理  
  24. MemBitmap.DeleteObject();  
  25. MemDC.DeleteDC();  
  26. }  
  27. 2、添加WM_ERASEBKGND響應函數,並清除響應函數的生成代碼在其中添加如下代碼  
  28. BOOL OnEraseBkgnd(CDC* pDC)  
  29. {  
  30. // TODO: 在此添加消息處理程序代碼和/或調用默認值  
  31. //return CDialog::OnEraseBkgnd(pDC);  
  32. return FALSE;  
  33. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章