程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> vc教程 >> VC++大數據量繪圖時無閃爍刷屏技術實現

VC++大數據量繪圖時無閃爍刷屏技術實現

編輯:vc教程

  引言

  當我們需要在用戶區顯示一些圖形時,先把圖形在客戶區畫上,雖然已經畫好但此時我們還無法看到,還要通過程序主動地刷新用戶區,強制Windows發送一條WM_PAINT消息,這將引發視類OnDraw函數簡單地將所有的圖形對象重畫,這樣才完成了圖形的顯示工作,但在刷新的同時會引起較明顯的閃爍尤其是當畫面面積較大、圖像元素過多時尤為明顯甚至達到無法正常工作的地步。因此,我們需要做相應的處理。本文介紹了采用先在內存中繪制圖形,然後再把繪好的圖形以位圖方式從內存拷貝到窗口客戶的消除刷屏閃爍的一種方法。

  WM_PAINT消息和無效區

  ·在用戶移動窗口或顯示窗口時,窗口中先前被隱藏的區域重新可見。

  ·用戶改變窗口的大小。

  ·滾動窗口用戶區。

  ·程序調用InvalidateRect或InvalidateRgn函數顯式地發送一條WM_PAINT消息。

  當上面情況之一發生時,就要求應用程序一定刷新其用戶區的一部分或全部,Windows會向窗口函數發送一條WM_PAINT消息。另外,當Windows刪除覆蓋窗口部分區域的對話框或消息框時和菜單下拉出來又被釋放時窗口用戶區被臨時覆蓋,系統會試圖保存顯示區域,但是不一定能成功,可能向窗口函數發送一條WM_PAINT消息,要求應用程序刷新其用戶區。需要說明的是:光標或圖符穿過窗口用戶區時,也可能覆蓋顯示內容,但這種情況下,系統一定能保留並恢復被覆蓋的區域,所以此時並不會發送WM_PAINT消息來要求應用程序去刷新其顯示區。在Windows 應用程序的窗口函數中,對WM_PAINT消息的處理就是刷新其用戶區,這是一種固定的程序結構。

  為提高刷新效率,我們可以只刷新用戶區的一小部分,其余沒有發生變化的我們可以不予刷新,窗口函數可以通過調用函數InvalidateRect顯式地使用戶區內的一個矩形無效。而且只有當窗口客戶區的某一部分失效時,其窗口函數才會收到WM_PAINT消息。

  刷屏閃爍的產生原因與解決方法

  當客戶區有所改動,而又要將改動顯示出來,就必然要強制Windows發送一條WM_PAINT消息,從而引發OnDraw函數的重畫,這樣雖完成了圖形的顯示,卻也會引起較明顯的閃爍,當畫面上數據不是很多時尚不明顯,當客戶區有成千上萬個點的時候刷新一次會引起整幅畫面的劇烈跳動,尤其是對於許多實時監控軟件和矢量電子地圖軟件,此類軟件通常在屏幕上都會動辄幾千、幾萬個要素點,很明顯單靠發送WM_PAINT 消息引發OnDraw 的重畫根本滿足不了實際需求。

  為了解決上述問題,我們需要做一些相應的處理。首先要先檢取無效區,然後創建一個與原設備環境句柄pDC相兼容的內存設備環境,之後就可以采用在內存中繪制圖形並把繪好的圖形以位圖方式從內存拷貝到窗口客戶的方法來消除刷屏時引起的閃爍。這還需要創建一個與原設備環境句柄pDC相兼容的、大小為整個客戶區的位圖。然後再使新的設備環境dc與pDC具有同樣的映射關系,將位圖選入內存環境。再使dc的整個客戶區都成無效區,再“與上”所檢取的無效區,使內存環境與pDC檢取的無效區相等。之後便可以進行繪圖工作了,繪圖完畢之後應當釋放所獲取的設備環境句柄pDC。否則會造成系統資源的浪費。

  程序示例

  本示例程序通過打開任意存檔文件,將其ASCII碼碼值當作要顯示的數據,並通過一圖畫控件將其數據以圖形的形式依次顯示出來。本程序要處理的數據量較大,如不采用本文所述方法將會有很明顯的閃爍。
首先新建一基於CFormVIEw的單文檔應用程序WaveShower並在Form上添加一"picture"控件,設置其ID為IDC_SCREEN、Type為Rectangle、Color為Black。在"Extended Styles"屬性頁裡選中Modal Frame檢查框。繼續添加一菜單“打開數據文件”,並生成其響應函數OnOpenData()。同時在視類中添加如下成員變量:

int m_BufLen; //數據長度
unsigned char* buffer; //數據緩存
int m_dx; //數據偏移量
int m_DY; //數據顯示區的幅度
CPoint* value; //將要顯示的數值
int m_DX; //數據顯示區的寬度
int m_Y0; //數據顯示區參照點位置
CRect rect; //數據顯示區矩形

  然後在視類中添加函數GetScreenRect()用以獲取數據顯示區的大小及其他參數;添加函數CleanScreen()完成清除數據顯示區的功能;添加函數DrawPoint()以便在數據顯示區畫點:

void CWaveShowerVIEw::GetScreenRect()
{
 CWnd* pStatic = GetDlgItem(IDC_SCREEN);
 pStatic->GetWindowRect(&rect);
 ScreenToClIEnt(&rect);
 rect.top+=4;
 rect.left+=4;
 rect.bottom-=4;
 rect.right-=4;
 m_Y0=(rect.bottom-rect.top)/2+rect.top;
 m_DX=rect.Width();
 m_DY=rect.Height()/2;
 value=new CPoint[m_DX];
}
void CWaveShowerVIEw::CleanScreen()
{
 CDC* pDC=GetDC();
 CPen pen1(PS_SOLID,1,RGB(0,0,0));
 CPen* oldPen1=pDC->SelectObject(&pen1);
 for(int i=rect.top;i<rect.bottom;i++)
 {
  pDC->MoveTo(rect.left,i);
  pDC->LineTo(rect.right,i);
 }
 pDC->SelectObject(&oldPen1);
 CPen pen2(PS_SOLID,1,RGB(0,0,255));
 CPen* oldPen2=pDC->SelectObject(&pen2);
 pDC->MoveTo(rect.left,m_Y0);
 pDC->LineTo(rect.right,m_Y0);
 pDC->SelectObject(&oldPen2);
 ReleaseDC(pDC);
}
void CWaveShowerVIEw::DrawPoint(CPoint pt, COLORREF color)
{
 CDC* pDC=GetDC();
 pDC->SetPixel(rect.left+pt.x,m_Y0-pt.y,color);
 ReleaseDC(pDC);

[1] [2] [3] 下一頁

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved