OnEraseBkgnd、OnPaint與畫面重繪

問題背景:
窗體在重繪時,由於過頻的刷新會引起閃爍現象——窗體在刷新時,總要有一個擦除原來圖象的過程OnEraseBkgnd,它利用背景色填充窗體繪圖區,然後在調用新的繪圖代碼進行重繪,這樣一擦一寫造成了圖象顏色的反差。當WM_PAINT的響應很頻繁的時候,這種反差也就越發明顯。
繪製過程:
當窗口整體需要重繪時,系統依次發送WM_NCPAINT、WM_ERASEBKGND、WM_PAINT三個獨立的消息,即先繪製框架部分,再繪製客戶區背景,最後繪製客戶區,根據需要也可能只產生其中某一個消息響應。
任何一個window組件的繪圖,都是放在OnPaint、OnEraseBkgnd中。在設定上 OnEraseBkgnd是用來畫底圖的而OnPaint是用來畫主要對象的。舉例說明:一個按鈕是灰色的,上面還有文字,則OnEraseBkgnd所做的事就是把按鈕畫成灰色,而OnPaint()所做的事就是畫上文字。
產生原因:
在OnEraseBkGnd中,如果不調用原來缺省的OnEraseBkGnd只是重畫背景則不會有閃爍。
而在OnPaint裏面,由於它隱含的調用了OnEraseBkGnd,如果沒有處理OnEraseBkGnd 函數,這時就和缺省的背景刷相關了.缺省的 OnEraseBkGnd操作使用窗口的缺省背景刷刷新背景(一般情況下是白刷),而隨後又自己重畫背景造成屏幕閃動。另外一個問題是OnEraseBkGnd不是每次都會被調用。如果調用Invalidate的時候參數爲TRUE,那麼在OnPaint裏面隱含調用BeginPaint時產生WM_ERASEBKGND消息,如果參數是FALSE,則不會重刷背景。 
解決思路:
1.用OnEraseBkGnd實現,不要調用基類的OnEraseBkGnd函數。
2.用OnPaint實現,同時重載OnEraseBkGnd,其中直接返回。
3.用OnPaint實現,創建窗口時設置背景刷爲空(不能將先前的圖像擦除)。
4.用OnPaint實現,但是要求刷新時用Invalidate(FALSE)這樣的函數(這種情況下,窗口覆蓋等造成的刷新還是要閃一下,所以不是徹底的解決方法)。
思路解決歸納:
比較OnPaintOnEraseBkgnd
1、 OnEraseBkgnd的要求是快速,最好不要太耗時間。每當window組件有任何小變動,都用OnEraseBkgnd。
2、 OnPaint是只有在程序有空閒的時候纔會被調用。
3、 OnEraseBkgnd是在OnPaint之前調用。
在OnPaint之前,可能調用OnEraseBkgnd好幾次,所以比較好的方法是OnPaint的方法由OnEraseBkgnd實現。
WM_PAINT消息響應的頻度太高,最小化最大化,移動窗體,覆蓋等等都引起重繪,很是消耗性能。
故使用雙緩存:可以把要顯示的圖形先在內存中繪製好,然後再一次性的將內存中的圖形按照一個點一個點地覆蓋到屏幕上去。
綜上,採用重載OnEraseBkgnd,通過雙緩存來解決畫面閃爍問題。
示例代碼:
BOOL CSDRClientDlg::OnEraseBkgnd(CDC* pDC) 
{
// TODO: Add your message handler code here and/or call default
CRect rcClient;
GetClientRect(&rcClient);
BITMAP bmpBKG;
m_cBKGndBmp.GetBitmap(&bmpBKG);
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap *pOldBmp=memDC.SelectObject(&m_cBKGndBmp);
pDC->StretchBlt(0,0,rcClient.Width(),rcClient.Height(),&memDC,0,0,
                        bmpBKG.bmWidth,bmpBKG.bmHeight,SRCCOPY);
memDC.SelectObject(pOldBmp);
memDC.DeleteDC();
return TRUE;
        //return CDialog::OnEraseBkgnd(pDC);
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章