乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      內(nèi)存繪圖

       啟蒙彩魂 2011-01-03
      經(jīng)常出現(xiàn)圖形閃爍的現(xiàn)象。利用先在內(nèi)存繪制,然后
      拷貝到屏幕的辦法可以消除屏幕閃爍,具體的方法是先在內(nèi)存
      中創(chuàng)建一個與設(shè)備兼容的內(nèi)存設(shè)備上下文,也就是開辟一快內(nèi)
      存區(qū)來作為顯示區(qū)域,然后在這個內(nèi)存區(qū)進(jìn)行繪制圖形。在繪制完成后利用
      BitBlt函數(shù)把內(nèi)存的圖形直接拷貝到屏幕上即可。
      具體的代碼實(shí)現(xiàn)為:

      (1)創(chuàng)建內(nèi)存區(qū)域
      CDC* pMem=new CDC;  
      CBitmap* pBmp=new CBitmap;
      CBitmap* pOldBmp;
      CDC* pDC=GetDC();
      CRect rectTemp;為繪圖區(qū)域
      pMem->CreateCompatibleDC(pDC);
      pBmp->CreateCompatibleBitmap(pDC, rectTemp.Width(), rectTemp.Height());
      pOldBmp=pMem->SelectObject(pBmp);
      (2)進(jìn)行圖形繪制
      pMem->LineTo(...);  進(jìn)行繪圖處理
      (3)拷貝到屏幕                     

      pDC->BitBlt(rectTemp.left,rectTemp.top,rectTemp.Width(),rectTemp.Height(),pMem,0,0,SRCCOPY);
      pMem->SelectObject(pOldBmp);
      pBmp->DeleteObject() ;
      pMem->DeleteDC();


      ================================================================================================


      雙緩存機(jī)制解決VC++繪圖時(shí)的閃爍問題

      顯示圖形如何避免閃爍,如何提高顯示效率是問得比較多的問題。
      而且多數(shù)人認(rèn)為MFC的繪圖函數(shù)效率很低,總是想尋求其它的解決方案。
      MFC的繪圖效率的確不高但也不差,而且它的繪圖函數(shù)使用非常簡單,
      只要使用方法得當(dāng),再加上一些技巧,用MFC可以得到效率很高的繪圖程序。
      我想就我長期(呵呵當(dāng)然也只有2年多)使用MFC繪圖的經(jīng)驗(yàn)談?wù)?br>我的一些觀點(diǎn)。

      1、顯示的圖形為什么會閃爍?
      我們的繪圖過程大多放在OnDraw或者OnPaint函數(shù)中,OnDraw在進(jìn)行屏
      幕顯示時(shí)是由OnPaint進(jìn)行調(diào)用的。當(dāng)窗口由于任何原因需要重繪時(shí),
      總是先用背景色將顯示區(qū)清除,然后才調(diào)用OnPaint,而背景色往往與繪圖內(nèi)容
      反差很大,這樣在短時(shí)間內(nèi)背景色與顯示圖形的交替出現(xiàn),使得顯示窗口看起來
      在閃。如果將背景刷設(shè)置成NULL,這樣無論怎樣重繪圖形都不會閃了。
      當(dāng)然,這樣做會使得窗口的顯示亂成一團(tuán),因?yàn)橹乩L時(shí)沒有背景色對原來
      繪制的圖形進(jìn)行清除,而又疊加上了新的圖形。
      有的人會說,閃爍是因?yàn)槔L圖的速度太慢或者顯示的圖形太復(fù)雜造成的,
      其實(shí)這樣說并不對,繪圖的顯示速度對閃爍的影響不是根本性的。
      例如在OnDraw(CDC *pDC)中這樣寫:
      pDC->MoveTo(0,0);
      pDC->LineTo(100,100);
      這個繪圖過程應(yīng)該是非常簡單、非常快了吧,但是拉動窗口變化時(shí)還是會看見
      閃爍。其實(shí)從道理上講,畫圖的過程越復(fù)雜越慢閃爍應(yīng)該越少,因?yàn)槔L圖用的
      時(shí)間與用背景清除屏幕所花的時(shí)間的比例越大人對閃爍的感覺會越不明顯。
      比如:清楚屏幕時(shí)間為1s繪圖時(shí)間也是為1s,這樣在10s內(nèi)的連續(xù)重畫中就要閃
      爍5次;如果清楚屏幕時(shí)間為1s不變,而繪圖時(shí)間為9s,這樣10s內(nèi)的連續(xù)重畫
      只會閃爍一次。這個也可以試驗(yàn),在OnDraw(CDC *pDC)中這樣寫:
      for(int i=0;i<100000;i++)
      {
      pDC->MoveTo(0,i);
      pDC->LineTo(1000,i);
      }
      呵呵,程序有點(diǎn)變態(tài),但是能說明問題。
      說到這里可能又有人要說了,為什么一個簡單圖形看起來沒有復(fù)雜圖形那么
      閃呢?這是因?yàn)閺?fù)雜圖形占的面積大,重畫時(shí)造成的反差比較大,所以感覺上要
      閃得厲害一些,但是閃爍頻率要低。
      那為什么動畫的重畫頻率高,而看起來卻不閃?這里,我就要再次強(qiáng)調(diào)了,
      閃爍是什么?閃爍就是反差,反差越大,閃爍越厲害。因?yàn)閯赢嫷倪B續(xù)兩個幀之間
      的差異很小所以看起來不閃。如果不信,可以在動畫的每一幀中間加一張純白的幀,
      不閃才怪呢。


      2、如何避免閃爍
      在知道圖形顯示閃爍的原因之后,對癥下藥就好辦了。首先當(dāng)然是去掉MFC
      提供的背景繪制過程了。實(shí)現(xiàn)的方法很多,
      * 可以在窗口形成時(shí)給窗口的注冊類的背景刷付NULL
      * 也可以在形成以后修改背景
      static CBrush brush(RGB(255,0,0));
      SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);
      * 要簡單也可以重載OnEraseBkgnd(CDC* pDC)直接返回TRUE
      這樣背景沒有了,結(jié)果圖形顯示的確不閃了,但是顯示也象前面所說的一樣,
      變得一團(tuán)亂。怎么辦?這就要用到雙緩存的方法了。雙緩沖就是除了在屏幕上有
      圖形進(jìn)行顯示以外,在內(nèi)存中也有圖形在繪制。我們可以把要顯示的圖形先在內(nèi)存中
      繪制好,然后再一次性的將內(nèi)存中的圖形按照一個點(diǎn)一個點(diǎn)地覆蓋到屏幕上去(這個
      過程非???,因?yàn)槭欠浅R?guī)整的內(nèi)存拷貝)。這樣在內(nèi)存中繪圖時(shí),隨便用什么反差
      大的背景色進(jìn)行清除都不會閃,因?yàn)榭床灰?。?dāng)貼到屏幕上時(shí),因?yàn)閮?nèi)存中最終的圖形
      與屏幕顯示圖形差別很?。ㄈ绻麤]有運(yùn)動,當(dāng)然就沒有差別),這樣看起來就不會閃。


      3、如何實(shí)現(xiàn)雙緩沖
      首先給出實(shí)現(xiàn)的程序,然后再解釋,同樣是在OnDraw(CDC *pDC)中:

      CDC MemDC; //首先定義一個顯示設(shè)備對象
      CBitmap MemBitmap;//定義一個位圖對象

      //隨后建立與屏幕顯示兼容的內(nèi)存顯示設(shè)備
      MemDC.CreateCompatibleDC(NULL);
      //這時(shí)還不能繪圖,因?yàn)闆]有地方畫 ^_^
      //下面建立一個與屏幕顯示兼容的位圖,至于位圖的大小嘛,可以用窗口的大小
      MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);

      //將位圖選入到內(nèi)存顯示設(shè)備中
      //只有選入了位圖的內(nèi)存顯示設(shè)備才有地方繪圖,畫到指定的位圖上
      CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

      //先用背景色將位圖清除干凈,這里我用的是白色作為背景
      //你也可以用自己應(yīng)該用的顏色
      MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));

      //繪圖
      MemDC.MoveTo(……);
      MemDC.LineTo(……);

      //將內(nèi)存中的圖拷貝到屏幕上進(jìn)行顯示
      pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);

      //繪圖完成后的清理
      MemBitmap.DeleteObject();
      MemDC.DeleteDC();

      上面的注釋應(yīng)該很詳盡了,廢話就不多說了。


      4、如何提高繪圖的效率
      我主要做的是電力系統(tǒng)的網(wǎng)絡(luò)圖形的CAD軟件,在一個窗口中往往要顯示成千上萬個電力元件,而每個元件又是由點(diǎn)、線、圓等基本圖形構(gòu)成。如果真要在一次重繪過程重畫這么多元件,可想而知這個過程是非常漫長的。如果加上了圖形的瀏覽功能,鼠標(biāo)拖動圖形滾動時(shí)需要進(jìn)行大量的重繪,速度會慢得讓用戶將無法忍受。怎么辦?只有再研究研究MFC 的繪圖過程了。
      實(shí)際上,在OnDraw(CDC *pDC)中繪制的圖并不是所有都顯示了的,例如:你
      在OnDraw中畫了兩個矩形,在一次重繪中雖然兩個矩形的繪制函數(shù)都有執(zhí)行,但是很有可能只有一個顯示了,這是因?yàn)镸FC本身為了提高重繪的效率設(shè)置了裁剪區(qū)。裁剪區(qū)的作用就是:只有在這個區(qū)內(nèi)的繪圖過程才會真正有效,在區(qū)外的是無效的,即使在區(qū)外執(zhí)行了繪圖函數(shù)也是不會顯示的。因?yàn)槎鄶?shù)情況下窗口重繪的產(chǎn)生大多是因?yàn)榇翱诓糠直徽趽趸蛘叽翱谟袧L動發(fā)生,改變的區(qū)域并不是整個圖形而只有一小部分,這一部分需要改變的就是pDC中的裁剪區(qū)了。因?yàn)轱@示(往內(nèi)存或者顯存都叫顯示)比繪圖過程的計(jì)算要費(fèi)時(shí)得多,有了裁剪區(qū)后顯示的就只是應(yīng)該顯示的部分,大大提高了顯示效率。但是這個裁剪區(qū)是MFC設(shè)置的,它已經(jīng)為我們提高了顯示效率,在進(jìn)行復(fù)雜圖形的繪制時(shí)如何進(jìn)一步提高效率呢?那就只有去掉在裁剪區(qū)外的繪圖過程了??梢韵扔?pDC->GetClipBox()得到裁剪區(qū),然后在繪圖時(shí)判斷你的圖形是否在這個區(qū)內(nèi),如果在就畫,不在就不畫。
      如果你的繪圖過程不復(fù)雜,這樣做可能對你的繪圖效率不會有提高。

       


      =================================================================================================

       


      =======================================================================================================================
      VC++大數(shù)據(jù)量繪圖時(shí)無閃爍刷屏技術(shù)實(shí)現(xiàn)

       


      引言

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

      WM_PAINT消息和無效區(qū)

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

      ·用戶改變窗口的大小。

      ·滾動窗口用戶區(qū)。

      ·程序調(diào)用InvalidateRect或InvalidateRgn函數(shù)顯式地發(fā)送一條WM_PAINT消息。

      當(dāng)上面情況之一發(fā)生時(shí),就要求應(yīng)用程序一定刷新其用戶區(qū)的一部分或全部,Windows會向窗口函數(shù)發(fā)送一條WM_PAINT消息。另外,當(dāng) Windows刪除覆蓋窗口部分區(qū)域的對話框或消息框時(shí)和菜單下拉出來又被釋放時(shí)窗口用戶區(qū)被臨時(shí)覆蓋,系統(tǒng)會試圖保存顯示區(qū)域,但是不一定能成功,可能向窗口函數(shù)發(fā)送一條WM_PAINT消息,要求應(yīng)用程序刷新其用戶區(qū)。需要說明的是:光標(biāo)或圖符穿過窗口用戶區(qū)時(shí),也可能覆蓋顯示內(nèi)容,但這種情況下,系統(tǒng)一定能保留并恢復(fù)被覆蓋的區(qū)域,所以此時(shí)并不會發(fā)送WM_PAINT消息來要求應(yīng)用程序去刷新其顯示區(qū)。在Windows 應(yīng)用程序的窗口函數(shù)中,對WM_PAINT消息的處理就是刷新其用戶區(qū),這是一種固定的程序結(jié)構(gòu)。

      為提高刷新效率,我們可以只刷新用戶區(qū)的一小部分,其余沒有發(fā)生變化的我們可以不予刷新,窗口函數(shù)可以通過調(diào)用函數(shù)InvalidateRect顯式地使用戶區(qū)內(nèi)的一個矩形無效。而且只有當(dāng)窗口客戶區(qū)的某一部分失效時(shí),其窗口函數(shù)才會收到WM_PAINT消息。

      刷屏閃爍的產(chǎn)生原因與解決方法

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

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

      程序示例

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

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

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

      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);
      }

      接下來,在視類的OnInitialUpdate()初始化函數(shù)中添加代碼以進(jìn)行數(shù)據(jù)顯示的各項(xiàng)前期準(zhǔn)備工作,并在“打開數(shù)據(jù)文件”菜單的響應(yīng)函數(shù)中添加代碼以讀取文件的內(nèi)碼。

      void CWaveShowerView::OnInitialUpdate()
      {
      CFormView::OnInitialUpdate();
      GetParentFrame()->RecalcLayout();
      ResizeParentToFit();
      GetScreenRect();
      for(int i=0;i<m_DX;i++)
      value[i].x=value[i].y=0;
      SetTimer(0,10,NULL);
      }
      void CWaveShowerView::OnOpenData()
      {
      CString FileName="";
      CFile file;
      CFileDialog dlg(TRUE,"*","*.*",
      OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"所有文件(*.*)|*.*||",NULL);
      if(dlg.DoModal()==IDOK)
      {
      KillTimer(1);
      FileName=dlg.GetPathName();
      file.Open(FileName,CFile::modeReadWrite);
      m_BufLen=file.GetLength();
      buffer= new unsigned char[m_BufLen+m_DX+10];
      file.Read(buffer,m_BufLen);
      file.Close();
      SetTimer(1,10,NULL);
      }
      }


      下面將要添加的定時(shí)器響應(yīng)函數(shù)正是本文的重點(diǎn),為方便對比起見,筆者寫了兩個OnTimer響應(yīng)函數(shù),前一個是采用常規(guī)的普通方法描點(diǎn)的,運(yùn)行起來可以很明顯地看到畫面的閃爍跳動。而后一種則是采用本文所述方法采用的內(nèi)存畫圖的方法,運(yùn)行后幾乎畫面無閃爍。下面便是兩段對比代碼的原碼部分:

      //代碼一:有閃爍的代碼
      void CWaveShowerView::OnTimer(UINT nIDEvent)
      {
      if(nIDEvent==0)
      {
      CleanScreen();
      for(int i=0;i<m_DX;i++)
      DrawPoint(value[i],RGB(0,255,0));
      }
      if(nIDEvent==1)
      {
      m_dx+=2;
      for(int i=0;i<m_DX;i++)
      {
      value[i].x=i;
      if(m_dx+i<0)
      buffer[m_dx+i]=128;
      if(m_dx+i<-m_DX)
      m_dx-=2;
      if(m_dx+i>m_BufLen)
      buffer[m_dx+i]=128;
      if(m_dx+i>m_BufLen+m_DX)
      m_dx-=2;
      value[i].y=m_DY*(buffer[m_dx+i]-128)/256;
      }
      }
      CFormView::OnTimer(nIDEvent);
      }
      //代碼二:無閃爍的代碼
      void CWaveShowerView::OnTimer(UINT nIDEvent)
      {
      if(nIDEvent==0)
      {
      CDC* pDC=GetDC();
      CDC dc;
      CBitmap bitmap;
      CBitmap* pOldBitmap;
      CRect client;
      pDC->GetClipBox(client); //檢取無效區(qū)
      //創(chuàng)建一個與pDC兼容的內(nèi)存設(shè)備環(huán)境
      if(dc.CreateCompatibleDC(pDC))
      {
      //創(chuàng)建一與pDC兼容的位圖,大小為整個客戶區(qū)
      if(bitmap.CreateCompatibleBitmap(pDC,rect.Width(), rect.Height()))
      {
      //使dc與pDC具有同樣的映射關(guān)系
      OnPrepareDC(&dc,NULL);
      //將位圖選入內(nèi)存環(huán)境
      pOldBitmap=dc.SelectObject(&bitmap);
      //使dc的整個客戶區(qū)都成無效區(qū)
      dc.SelectClipRgn(NULL);
      //再“與上”檢取的無效區(qū),使內(nèi)存環(huán)境與
      //pDC檢取的無效區(qū)相等
      dc.IntersectClipRect(client);
      }
      }
      CleanScreen();
      for(int i=0;i<m_DX;i++)
      DrawPoint(value[i],RGB(0,255,0));
      dc.SelectObject(pOldBitmap);
      ReleaseDC(pDC);
      }
      if(nIDEvent==1)
      {
      m_dx+=2;
      for(int i=0;i<m_DX;i++)
      {
      value[i].x=i;
      if(m_dx+i<0)
      buffer[m_dx+i]=128;
      if(m_dx+i<-m_DX)
      m_dx-=2;
      if(m_dx+i>m_BufLen)
      buffer[m_dx+i]=128;
      if(m_dx+i>m_BufLen+m_DX)
      m_dx-=2;
      value[i].y=m_DY*(buffer[m_dx+i]-128)/256;
      }
      }
      CFormView::OnTimer(nIDEvent);
      }

      雖然通過上述幾步可以實(shí)現(xiàn)所有的功能,但為了防止內(nèi)存泄露和養(yǎng)成良好的編程習(xí)慣,我們還須做些工作,在視類的構(gòu)造函數(shù)中釋放我們曾經(jīng)申請過的內(nèi)存以及定時(shí)器:

      CWaveShowerView::~CWaveShowerView()
      {
      delete[] value;
      KillTimer(0);
      KillTimer(1);
      }

      小結(jié)

      編譯運(yùn)行此程序,通過菜單選取需要顯示的文件(任意文件均可),如在定時(shí)器響應(yīng)代碼中采用的是第一種代碼,則會看到數(shù)據(jù)顯示的同時(shí)伴隨著明顯的閃爍而采用后一種代碼編碼則會很平穩(wěn)的將數(shù)據(jù)顯示出來。本文介紹的這種方法適用于各種牽扯到數(shù)組數(shù)據(jù)圖形顯示的程序,比如監(jiān)控軟件、數(shù)據(jù)分析軟件、測量軟件等等,具有廣泛的應(yīng)用前景。本文所述程度代碼在Windows 2000 Professional + SP4下由Microsoft Visual C++ 6.0編譯通過。

       


      =============================================================================================

       


      用雙緩存技術(shù),和重載OnEraseBKgnd(),

      怎么重載OnEraseBkgnd()函數(shù)??- -

       

       

       

      選擇View->ClassWizard->classinfo選項(xiàng)

      卡:message filter下拉框: 選擇window,然后再選擇

      message maps選項(xiàng)卡,在messages下拉框應(yīng)該可以找到

      wm_erasebkgnd.雙擊添加.

       

      ======================================================================================================

       

      如何實(shí)現(xiàn)雙緩沖
      首先給出實(shí)現(xiàn)的程序,然后再解釋,同樣是在OnDraw(CDC *pDC)中:
      CDC MemDC; //首先定義一個顯示設(shè)備對象
      CBitmap MemBitmap;//定義一個位圖對象
      //隨后建立與屏幕顯示兼容的內(nèi)存顯示設(shè)備
      MemDC.CreateCompatibleDC(NULL);
      //這時(shí)還不能繪圖,因?yàn)闆]有地方畫 ^_^
      //下面建立一個與屏幕顯示兼容的位圖,至于位圖的大小嘛,可以用窗口的大小,也可以自己定義(如:有滾動條時(shí)就要大于當(dāng)前窗口的大小,在BitBlt時(shí)決定拷貝內(nèi)存的哪部分到屏幕上)
      MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);

      //將位圖選入到內(nèi)存顯示設(shè)備中
      //只有選入了位圖的內(nèi)存顯示設(shè)備才有地方繪圖,畫到指定的位圖上
      CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);
      //先用背景色將位圖清除干凈,這里我用的是白色作為背景
      //你也可以用自己應(yīng)該用的顏色
      MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));
      //繪圖
      MemDC.MoveTo(……);
      MemDC.LineTo(……);

      //將內(nèi)存中的圖拷貝到屏幕上進(jìn)行顯示
      pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);
      //繪圖完成后的清理
      MemBitmap.DeleteObject();
      MemDC.DeleteDC();

       

      ==================================================================================================

       

      用vc 做程序,如何畫圖是一個大家都很關(guān)心,但是卻感到很難以理解的問題,因?yàn)樵趍fc的封裝之下,沒有現(xiàn)成的畫圖函數(shù)供你直接調(diào)用,像vb等等里面直接來個 point之類的,常常讓人感到無從下手。這兩天幫人解決了一個用內(nèi)存緩沖畫圖的問題,順便也就談?wù)勥@些東西,也算是總結(jié)。

      我先來解釋一下在mfc里面很關(guān)鍵的設(shè)備環(huán)境描述符,也就是所謂的DC(device context)。

      還是從歷史來看吧,dos時(shí)代,我們?nèi)绻L圖,必須通過一系列系統(tǒng)函數(shù)來啟動圖形環(huán)境(用過turbo pascal或者turbo c的人該還有印象吧),這之間對各種硬件的初始化參數(shù)都不相同,非常的煩人,常常還要查閱硬件手冊,那時(shí)的程序智能針對最流行的硬件來編寫,對不流行的就沒有辦法了。windows操作系統(tǒng)為了屏蔽不同的硬件環(huán)境,讓編程時(shí)候不考慮具體的硬件差別,采取了一系列辦法,設(shè)備環(huán)境描述符就是這樣產(chǎn)生的。簡單的說,設(shè)備描述符抽象了不同的硬件環(huán)境為標(biāo)準(zhǔn)環(huán)境,用戶編寫時(shí)使用的是這個虛擬的標(biāo)準(zhǔn)環(huán)境,而不是真實(shí)的硬件,與真實(shí)硬件打交道的工作一般交給了系統(tǒng)和驅(qū)動程序完成(這同樣解釋了為什么我們需要經(jīng)常更新驅(qū)動程序的問題)。使用在windows圖形系統(tǒng)(gdi,而不包括direct x)上面,就體現(xiàn)在一系列的圖形DC上面,我們?nèi)绻趃di上面繪圖,就必須先得到圖形DC的句柄(handle),然后指定句柄的基礎(chǔ)上進(jìn)行圖形操作。

      再來回憶一下,我們怎么在sdk的環(huán)境下面繪圖呢,我想這個大家都不太清楚吧,但是確實(shí)很基礎(chǔ)。在windows的sdk環(huán)境下面,我們用傳統(tǒng)的c編寫程序,在需要的繪圖地方(比如響應(yīng)WM_PAINT消息的分支)這樣做:

      hdc = GetDC( hwnd );
      oldGdiObject = SelectObject( hdc,newGdiObject );
      ...繪圖操作...
      SelectObject( hdc,oldGdiObject );
      DeleteObject( newGdiObject );
      ReleaseDC( hdc);

      或者是這樣

      BeginPaint( hwnd,&ps );//PAINTSTRUCT ps -- ps is a paint struct
      ...繪圖操作...
      EndPaint( hwnd )

      這就是大概的過程,我們看到了hdc(圖形DC句柄)的應(yīng)用,在繪圖的部分,每一個繪圖函數(shù)基本上也要用到這個句柄,最后我們還必須釋放它,否則將嚴(yán)重影響性能。每次我們都必須調(diào)用GetDC這個api函數(shù)得到(不能用全局變量保存結(jié)果重復(fù)使用,我在后面解釋)。這些是最最基本的windows圖形操作的方式,相比dos時(shí)代簡單了些,但是有些概念也難理解了些。vb里面的簡單的point函數(shù)其實(shí)最后也是被轉(zhuǎn)化為這樣的方式來執(zhí)行,系統(tǒng)幫助做了很多事情。

      到了mfc里面,由于有了封裝,所有的hdc被隱藏在對象中做為隱藏參數(shù)傳遞(就是DC類的this啦~~),所以我們的關(guān)鍵話題就轉(zhuǎn)變?yōu)榱嗽鯓拥玫较胍?DC類而已,這個過程其實(shí)大同小異的。在消息響應(yīng)的過程中,WM_PAINT被轉(zhuǎn)變?yōu)镺nDraw(),OnPaint()一系列函數(shù)來響應(yīng),這些函數(shù)一般都有個參數(shù)CDC *pDC傳入進(jìn)來,因此在這些函數(shù)里面,我們就只需要直接畫圖就可以了,和以前sdk的方式一樣。

      但是WM_PAINT消息響應(yīng)的頻度太高了,比如最小化最大化,移動窗體,覆蓋等等都引起重繪,經(jīng)常的這樣畫圖,很是消耗性能;在有些場合,比如隨機(jī)作圖的場合,每一次就改變,還導(dǎo)致了程序的無法實(shí)現(xiàn)。怎么解決后一種問題呢。

      ms 在msdn的例子里面交給我們document/view的經(jīng)典解決辦法,將圖形的數(shù)據(jù)存儲在document類里面,view類只是根據(jù)這些數(shù)據(jù)繪圖。比如你要畫個圓,只是將圓心和半徑存在document里面,view類根據(jù)這個里面的數(shù)據(jù)在屏幕上面重新繪制。那么,我們只需要隨機(jī)產(chǎn)生一次數(shù)據(jù)就可以了。

      這樣還是存在性能的問題,于是我們開始考慮另外的解決方法。我們知道,將內(nèi)存中的圖片原樣輸出到屏幕是很快的,這也是我們在dos時(shí)代經(jīng)常做的事情,能不能在windows也重新利用呢?答案就是內(nèi)存緩沖繪圖,我們今天的主題。

      我們還是回到DC上來,既然DC是繪圖對象,我們也就可以自己來在內(nèi)存里面造一個,讓它等于我們想要的圖,圖(CBitmap)可以存儲在 document 類里面,每一次刷新屏幕都只是將這個圖輸出到屏幕上面,每一次作圖都是在內(nèi)存里面繪制,保存在document的圖里面,必要時(shí)還可以將圖輸出到外存保存。這樣既保證了速度,也解決了隨機(jī)的問題,在復(fù)雜作圖的情況下對內(nèi)存的開銷也不大(總是一副圖片的大?。?。這是一個很好的解決辦法,現(xiàn)在讓我們來實(shí)現(xiàn)它們。

      我們在document類里面保存一個圖片

      CBitmap m_bmpBuf;//這里面保存了我們做的圖,存在于內(nèi)存中

      在view類里面我們需要將這個圖拷貝到屏幕上去
      位于OnDraw(CDC *pDC)函數(shù)中:

      CDC dcMem;//以下是輸出位圖的標(biāo)準(zhǔn)操作
      CBitmap *pOldBitmap = NULL;
      dcMem.CreateCompatibleDC(NULL);
      pOldBitmap = dcMem.SelectObject(&pDoc->m_bmpBuf);
      BITMAP bmpinfo;
      pDoc->m_bmpBuf.GetBitmap(&bmpinfo);
      pDC->BitBlt(0,0,bmpinfo.bmWidth,bmpinfo.bmHeight,&dcMem,0,0,SRCCOPY);
      dcMem.SelectObject(pOldBitmap);
      dcMem.DeleteDC();

      在我們需要畫圖的函數(shù)里面,我們完成繪圖工作

      CBmpDrawDoc *pDoc = GetDocument(); //得到document中的bitmap對象
      CDC *pDC = GetDC();
      CDC dcMem;
      dcMem.CreateCompatibleDC(NULL);//這里我們就在內(nèi)存中虛擬建造了DC
      pDoc->m_bmpBuf.DeleteObject();
      pDoc->m_bmpBuf.CreateCompatibleBitmap(pDC,100,100);//依附DC創(chuàng)建bitmap
      CBitmap *pOldBitmap = dcMem.SelectObject(&pDoc->m_bmpBuf);//我們調(diào)入了我們bitmap目標(biāo)

      dcMem.FillSolidRect(0,0,100,100,RGB(255,255,255));//這些時(shí)繪圖操作,隨便你^_^
      dcMem.TextOut(0,0,"Hello,world!");
      dcMem.Rectangle(20,20,40,40);
      dcMem.FillSolidRect(40,40,50,50,RGB(255,0,0));

      pDC->BitBlt(0,0,100,100,&dcMem,0,0,SRCCOPY);//第一次拷貝到屏幕
      dcMem.SelectObject(pOldBitmap);
      dcMem.DeleteDC();


      全部的過程就是這樣,很簡單吧。以此為例子還可以實(shí)現(xiàn)2個緩沖或者多個緩沖等等,視具體情況而定。當(dāng)然在緩沖區(qū)還可以實(shí)現(xiàn)很多高級的圖形操作,比如透明,合成等等,取決于具體的算法,需要對內(nèi)存直接操作(其實(shí)就是當(dāng)年dos怎么做,現(xiàn)在還怎么做)。

      再來解釋一下前面說的為什么不能用全局變量保存DC問題。其實(shí)DC也是用句柄來標(biāo)識的,所以也具有句柄的不確定性,就是只能隨用隨取,不同時(shí)間兩次取得的是不同的(使用過文件句柄地話,應(yīng)該很容易理解的)。那么我們用全局變量保存的DC就沒什么意義了,下次使用只是什么也畫不出來。(這一點(diǎn)的理解可以這樣: DC需要占用一定的內(nèi)存,那么在頻繁的頁面調(diào)度中,位置難免改變,于是用來標(biāo)志指針的句柄也就不同了)。

       

      =================================================================================

       


      VC 雙緩沖繪圖

      以下內(nèi)容來自:http://dev./VC/Problem/2008/12/1919491244642.shtml

      在圖形圖象處理編程過程中,雙緩沖是一種基本的技術(shù)。我們知道,如果窗體在響應(yīng)WM_PAINT消息的時(shí)候要進(jìn)行復(fù)雜的圖形處理,那么窗體在重繪時(shí)由于過頻的刷新而引起閃爍現(xiàn)象。解決這一問題的有效方法就是雙緩沖技術(shù)。

      因?yàn)榇绑w在刷新時(shí),總要有一個擦除原來圖象的過程OnEraseBkgnd,它利用背景色填充窗體繪圖區(qū),然后在調(diào)用新的繪圖代碼進(jìn)行重繪,這樣一擦一寫造成了圖象顏色的反差。當(dāng)WM_PAINT的響應(yīng)很頻繁的時(shí)候,這種反差也就越發(fā)明顯。于是我們就看到了閃爍現(xiàn)象。

      我們會很自然的想到,避免背景色的填充是最直接的辦法。但是那樣的話,窗體上會變的一團(tuán)糟。因?yàn)槊看卫L制圖象的時(shí)候都沒有將原來的圖象清除,造成了圖象的殘留,于是窗體重繪時(shí),畫面往往會變的亂七八糟。所以單純的禁止背景重繪是不夠的。我們還要進(jìn)行重新繪圖,但要求速度很快,于是我們想到了使用 BitBlt函數(shù)。它可以支持圖形塊的復(fù)制,速度很快。我們可以先在內(nèi)存中作圖,然后用此函數(shù)將做好的圖復(fù)制到前臺,同時(shí)禁止背景刷新,這樣就消除了閃爍。以上也就是雙緩沖繪圖的基本的思路。

      一、普通方法:

      先按普通做圖的方法進(jìn)行編程。即在視類的OnDraw函數(shù)中添加繪圖代碼。在此我們繪制若干同心圓,代碼如下:
      CBCDoc* pDoc = GetDocument();

      ASSERT_VALID(pDoc);

      CPoint ptCenter;

      CRect rect,ellipseRect;

      GetClientRect(&rect);

      ptCenter = rect.CenterPoint();

      for(int i=20;i>0;i--)

      {

      ellipseRect.SetRect(ptCenter,ptCenter);

      ellipseRect.InflateRect(i*10,i*10);

      pDC->Ellipse(ellipseRect);

      }


      編譯運(yùn)行程序,嘗試改變窗口大小,可以發(fā)現(xiàn)閃爍現(xiàn)象。

      二、雙緩沖方法:

      在雙緩沖方法中,首先要做的是屏蔽背景刷新。背景刷新其實(shí)是在響應(yīng)WM_ERASEBKGND消息。我們在視類中添加對這個消息的響應(yīng),可以看到缺省的代碼如下:
      BOOL CMYView::OnEraseBkgnd(CDC* pDC)

      {

      return CView::OnEraseBkgnd(pDC);

      }


      是調(diào)用父類的OnEraseBkgnd函數(shù),我們屏蔽此調(diào)用,只須直接return TRUE;即可。

      下面是內(nèi)存緩沖作圖的步驟。
      CPoint ptCenter;

      CRect rect,ellipseRect;

      GetClientRect(&rect);

      ptCenter = rect.CenterPoint();

      CDC dcMem; //用于緩沖作圖的內(nèi)存DC

      CBitmap bmp; //內(nèi)存中承載臨時(shí)圖象的位圖

      dcMem.CreateCompatibleDC(pDC); //依附窗口DC創(chuàng)建兼容內(nèi)存DC

      bmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//創(chuàng)建兼容位圖

      dcMem.SelectObject(&bmp); //將位圖選擇進(jìn)內(nèi)存DC

      //按原來背景填充客戶區(qū),不然會是黑色

      dcMem.FillSolidRect(rect,pDC->GetBkColor());

      for(int i=20;i>0;i--) //在內(nèi)存DC上做同樣的同心圓圖象

      {

      ellipseRect.SetRect(ptCenter,ptCenter);

      ellipseRect.InflateRect(i*10,i*10);

      dcMem.Ellipse(ellipseRect);

      }

      pDC->BitBlt(0,0,rect.Width(),rect.Height(),

      &dcMem,0,0,SRCCOPY);//將內(nèi)存DC上的圖象拷貝到前臺

      dcMem.DeleteDC(); //刪除DC

      bm.DeleteObject(); //刪除位圖


      由于復(fù)雜的畫圖操作轉(zhuǎn)入后臺,我們看到的是速度很快的復(fù)制操作,自然也就消除了閃爍現(xiàn)象。
      注意:bmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());

      這里面CreateCompatibleBitmap第一個參數(shù)不能用dcMem,這樣的話創(chuàng)建的是黑白位圖。如果你要創(chuàng)建彩色位圖,需要用pDC,它用來創(chuàng)建了內(nèi)存DC. 詳細(xì)請見下面的MSDN:
      When a memory device context is created, it initially has a 1-by-1 monochrome bitmap selected into it. If this memory device context is used in CreateCompatibleBitmap, the bitmap that is created is a monochrome bitmap. To create a color bitmap, use the hDC that was used to create the memory device context, as shown in the following code:

      HDC memDC = CreateCompatibleDC ( hDC );

      HBITMAP memBM = CreateCompatibleBitmap ( hDC, nWidth, nHeight );

      SelectObject ( memDC, memBM );

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多