1. <var id="fe6gj"></var>

    <rp id="fe6gj"><nav id="fe6gj"></nav></rp>

    <noframes id="fe6gj"><cite id="fe6gj"></cite>

    <ins id="fe6gj"><button id="fe6gj"><p id="fe6gj"></p></button></ins>
    1. <tt id="fe6gj"><i id="fe6gj"><sub id="fe6gj"></sub></i></tt>
        始創于2000年 股票代碼:831685
        咨詢熱線:0371-60135900 注冊有禮 登錄
        • 掛牌上市企業
        • 60秒人工響應
        • 99.99%連通率
        • 7*24h人工
        • 故障100倍補償
        您的位置: 網站首頁 > 幫助中心>文章內容

        VC 繪圖,使用雙緩沖技術實現

        發布時間:  2012/9/11 17:02:16
        1. 首先定義類成員:CDC *m_pDC;CDC MemDC;CBitmap MemBitmap;CBitmap *pOldbitmap;LONG xRange; // 邏輯范圍,x方向寬度LONG yRange; // 邏輯范圍,y方向高度LONG nWidht; // 物理范圍,x方向寬度LONG nHeight; // 物理范圍,y方向高度2. 在類初始化函數中:m_pDC = this->GetDC(); // 獲取設備上下文句柄CWnd *wnd = GetDlgItem(IDC_SHOWGRAPH); // 獲取界面上顯示圖形的ID控件的句柄wnd->GetWindowRect(&rect); // 獲取顯示/畫圖區域大。ㄎ锢矸秶

          ScreenToClient(&rect); // 轉換為客戶區坐標nWidth = rect.Width(); // 顯示/畫圖區域x方向物理寬度nHeight = rect.Height(); // 顯示/畫圖區域y方向物理高度3. 在自定義函數中,設置視口與窗口的比例關系:

          m_pDC->SetMapMode(MM_ANISOTROPIC); // 注意MM_ANISOTROPIC和MM_ISOTROPIC的區別m_pDC->SetWindowExt(XRange,-yRange); // 設定窗口尺寸范圍,畫圖使用的邏輯范圍,實現放大或是縮小,坐標方向↑和→為正向m_pDC->SetViewportExt(nWidth,nHeight); // 設定視口尺寸范圍,客戶區實際圖形顯示的區域范圍,大小固定m_pDC->SetViewportOrg(rect.left,rect.bottom); //設定畫圖的邏輯原點坐標(0,0)在物理坐標的(rect.left,rect.bottom)點上4. 在自定義函數中,雙緩沖技術的使用:MemDC.CreateCompatibleDC(m_pDC); // 創建內存兼容設備上下文MemBitmap.CreateCompatibleBitmap(m_pDC,xRange,yRange); // 創建內存兼容畫布,大小由邏輯范圍決定pOldbitmap = MemDC.SelectObject(&MemBitmap); // 將畫布選入內存設備上下文MemDC.FillSolidRect(0,0,xRange,yRange,RGB(123,213,132)); // 對內存中的畫布填充背景顏色,否則是默認的黑色// 畫圖操作,如畫一條對角直線MemDC.MoveTo(0,0);MemDC.LineTo(xRange*0.9,yRange*0.9);// 將內存中的畫圖區域拷貝到界面的控件區域上去// 第1和第2個參數若是0時,則從物理坐標的(rect.left,rect.bottom)點上開始按上述指定的方向貼圖m_pDC->BitBlt(0,0,xRange,yRange,&MemDC,0,0,SRCCOPY);5. 在類的析構函數中:MemDC.SelectObject(pOldbitmap);bitmap.DeleteObject();this->ReleaseDC(m_pDC);6. 至此,就完成了雙緩沖及坐標縮放繪圖的功能*********************************************************************************************************************************************用VC做的畫圖程序,當所畫的圖形大于屏幕時,在拖動滾動條時屏幕就會出現嚴重的閃爍,為了解決這一問題,就得使用雙緩沖來解決。程序產生嚴重的閃爍問題是因為畫圖過程中前后兩次的畫面反差很大造成的人的視覺的閃爍。因為在VC中每次在調用OnDraw時系統都是先用背景畫刷將畫布清除再執行畫圖命令,這樣在你每次移動滾動條時每執行一次OnDraw就會有一個空白頁,這樣和你的最終結果圖象之間有一個很大的反差,因而看起來閃爍,而且滾動條滾動越快閃爍越嚴重。當然,你可以將背景畫刷設為NULL,這樣可以解決閃爍問題,但是不能將先前的圖象擦除,這樣整個屏幕就顯得很亂。

          下面將利用雙緩沖來解決這一問題的思路給大家作一下簡單的介紹。

          我先來解釋一下在MFC里面很關鍵的設備環境描述符,也就是所謂的 DC(device context)。

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

          那么我們怎么在sdk環境下面繪圖的呢,我想這個大家都不太清楚,但是確實很基礎。在windows的sdk環境下面,我們用傳統的c編寫程序,在需要的繪圖地方(比如響應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句柄)的應用。在繪圖的部分,每一個繪圖函數基本上也要用到這個句柄,最后我們還必須釋放它,否則將嚴重影響性能。每次我們都必須調用GetDC這個api函數得到(不能用全局變量保存結果重復使用,我在后面將做解釋)。這些是最最基本的windows圖形操作的方式,相比dos時代簡單了些,但是有些概念也難理解了些。vb里面的簡單的point函數其實最后也是被轉化為這樣的方式來執行,系統幫助做了很多事情。

          到了MFC里面,由于有了封裝,所有的hdc被隱藏在對象中做為隱藏參數來傳遞(就是DC類的this啦~~),所以我們的關鍵話題就轉變為了怎樣得到想要的DC類而已。這個過程其實大同小異。在消息響應的過程中,WM_PAINT被轉變為OnDraw()或是OnPaint()之類的一系列函數來響應,這些函數一般都有個參數CDC *pDC傳入進來,因此在這些函數里面,我們只需直接畫圖就可以了,和以前sdk的方式一樣。

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

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

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

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

          1. 我們首先在document類里面保存一個圖片CBitmap m_bmpBuf; //這里面保存了我們做的圖,存在于內存中

          2. 其次在view類里面,我們需要將這個圖拷貝到屏幕上去,于OnDraw(CDC *pDC)函數中:

          CDC dcMem; // 以下是輸出位圖的標準操作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();

          3. 在我們需要畫圖的函數里,完成繪圖工作

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

          dcMem.FillSolidRect(0,0,100,100,RGB(255,255,255)); // 這些是繪圖操作,隨便你^_^ 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();

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

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

          *********************************************************************************************************************************************顯示圖形如何避免閃爍

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

          1、顯示的圖形為什么會閃爍?

          我們的繪圖過程大多放在OnDraw或者OnPaint函數中,OnDraw在進行屏幕顯示時是由OnPaint進行調用的。當窗口由于任何原因需要重繪時,總是先用背景色將顯示區清除,然后才調用OnPaint,而背景色往往與繪圖內容反差很大,這樣在短時間內背景色與顯示圖形的交替出現,使得顯示窗口看起來在閃。如果將背景刷設置成NULL,這樣無論怎樣重繪圖形都不會閃了。 當然,這樣做會使得窗口的顯示亂成一團,因為重繪時沒有背景色對原來繪制的圖形進行清除,而又疊加上了新的圖形。

          有的人會說,閃爍是因為繪圖的速度太慢或者顯示的圖形太復雜造成的,其實這樣說并不對,繪圖的顯示速度對閃爍的影響不是根本性的。

          例如在OnDraw(CDC *pDC)中這樣寫:pDC->MoveTo(0,0);pDC->LineTo(100,100);這個繪圖過程應該是非常簡單、非?炝税,但是拉動窗口變化時還是會看見閃爍。其實從道理上講,畫圖的過程越復雜越慢閃爍應該越少,因為繪圖用的時間與用背景清除屏幕所花的時間的比例越大人對閃爍的感覺會越不明顯。比如:清楚屏幕時間為1s繪圖時間也是為1s,這樣在10s內的連續重畫中就要閃爍5次;如果清楚屏幕時間為1s不變,而繪圖時間為9s,這樣10s內的連續重畫只會閃爍一次。這個也可以試驗,在OnDraw(CDC *pDC)中這樣寫:for(int i=0;i<100000;i++)

          { pDC->MoveTo(0,i);pDC->LineTo(1000,i);}呵呵,程序有點變態,但是能說明問題。

          說到這里可能又有人要說了,為什么一個簡單圖形看起來沒有復雜圖形那么閃呢?這是因為復雜圖形占的面積大,重畫時造成的反差比較大,所以感覺上要閃得厲害一些,但是閃爍頻率要低。那為什么動畫的重畫頻率高,而看起來卻不閃?這里,我就要再次強調了,閃爍是什么?閃爍就是反差,反差越大,閃爍越厲害。因為動畫的連續兩個幀之間的差異很小所以看起來不閃。如果不信,可以在動畫的每一幀中間加一張純白的幀,不閃才怪呢。

          2、如何避免閃爍在知道圖形顯示閃爍的原因之后,對癥下藥就好辦了。

         。1)。 首先是去掉MFC 提供的背景繪制過程。實現的方法很多:* 可以在窗口形成時給窗口的注冊類的背景刷賦NULL * 也可以在形成以后修改背景static CBrush brush(RGB(255,0,0));SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);* 要簡單也可以重載OnEraseBkgnd(CDC* pDC)直接返回TRUE這樣背景沒有了,結果圖形顯示的確不閃了,但是顯示也象前面所說的一樣,變得一團亂。怎么辦?

         。2)。 這就要用到雙緩存的方法了。雙緩沖就是除了在屏幕上有圖形進行顯示以外,在內存中也有圖形在繪制。我們可以把要顯示的圖形先在內存中繪制好,然后再一次性的將內存中的圖形按照一個點一個點地覆蓋到屏幕上去(這個過程非?,因為是非常規整的內存拷貝)。這樣在內存中繪圖時,隨便用什么反差大的背景色進行清除都不會閃,因為看不見。當貼到屏幕上時,因為內存中最終的圖形與屏幕顯示圖形差別很。ㄈ绻麤]有運動,當然就沒有差別),這樣看起來就不會閃。

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

          CRect rc; // 定義一個矩形區域變量GetClientRect(rc);int nWidth = rc.Width();int nHeight = rc.Height();

          CDC *pDC = GetDC(); // 定義設備上下文CDC MemDC; // 定義一個內存顯示設備對象CBitmap MemBitmap; // 定義一個位圖對象

          //建立與屏幕顯示兼容的內存顯示設備MemDC.CreateCompatibleDC(pDC);//建立一個與屏幕顯示兼容的位圖,位圖的大小可選用窗口客戶區的大小MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);//將位圖選入到內存顯示設備中,只有選入了位圖的內存顯示設備才有地方繪圖,畫到指定的位圖上CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap);//先用背景色將位圖清除干凈,否則是黑色。這里用的是白色作為背景MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));

          //繪圖操作等在這里實現MemDC.MoveTo(……);MemDC.LineTo(……);MemDC.Ellipse(……);

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

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

          上面的注釋應該很詳盡了,廢話就不多說了。

          4、如何提高繪圖的效率我主要做的是電力系統的網絡圖形的CAD軟件,在一個窗口中往往要顯示成千上萬個電力元件,而每個元件又是由點、線、圓等基本圖形構成。如果真要在一次重繪過程重畫這么多元件,可想而知這個過程是非常漫長的。如果加上了圖形的瀏覽功能,鼠標拖動圖形滾動時需要進行大量的重繪,速度會慢得讓用戶將無法忍受。怎么辦?只有再研究研究MFC的繪圖過程了。

          實際上,在OnDraw(CDC *pDC)中繪制的圖并不是所有都顯示了的,例如:你在OnDraw中畫了兩個矩形,在一次重繪中雖然兩個矩形的繪制函數都有執行,但是很有可能只有一個顯示了,這是因為MFC本身為了提高重繪的效率設置了裁剪區。裁剪區的作用就是:只有在這個區內的繪圖過程才會真正有效,在區外的是無效的,即使在區外執行了繪圖函數也是不會顯示的。因為多數情況下窗口重繪的產生大多是因為窗口部分被遮擋或者窗口有滾動發生,改變的區域并不是整個圖形而只有一小部分,這一部分需要改變的就是pDC中的裁剪區了。因為顯示(往內存或者顯存都叫顯示)比繪圖過程的計算要費時得多,有了裁剪區后顯示的就只是應該顯示的部分,大大提高了顯示效率。但是這個裁剪區是MFC設置的,它已經為我們提高了顯示效率,在進行復雜圖形的繪制時如何進一步提高效率呢?那就只有去掉在裁剪區外的繪圖過程了?梢韵扔胮DC->GetClipBox()得到裁剪區,然后在繪圖時判斷你的圖形是否在這個區內,如果在就畫,不在就不畫。但如果你的繪圖過程不復雜,這樣做可能對你的繪圖效率不會有提高。

          *********************************************************************************************************************************************雙緩存即現在內存dc中作圖,而后一次性地拷貝到屏幕上,所以提高了繪圖的速度。但只用此方法不能根本解決閃爍的問題。

          而將響應 WM_ERASEBKGND 的重載函數 OnEraseBkgnd(CDC* pDC) 直接返回TRUE是最好的辦法。

          如下:BOOL CMyWin::OnEraseBkgnd(CDC* pDC)

          { return TRUE;//return CWnd::OnEraseBkgnd(pDC); //把系統原來的這條語句注釋掉} *********************************************************************************************************************************************如何修改控件的背景模式及控件的字體顏色1. 改變對話框的背景色在C…App類中的InitInstance()里添加SetDialogBkColor(RGB(0,192,0),RGB(0,0,0));

          2. 如果想改變靜態文本或單選按鈕的背景色,首先需要獲得控件ID,然后設置背景色,具體步驟:(1) 響應對話框類的WM_CTLCOLOR消息,生成OnCtlColor函數(2) 為對話框類添加成員變量CBrush m_brush;并在初始化函數中初始化m_brush.CreateSolidBrush(RGB(0,255,0)); //顏色在這里設置(3) 在OnCtlColor函數中添加代碼,以改變控件的文字顏色和背景色switch(pWnd->GetDlgCtrlID())

          { case(IDC_INPUT):pDC->SetTextColor(RGB(255,0,192));pDC->SetBkMode(TRANSPARENT);return m_brush;break;case(IDC_EDIT):pDC->SetTextColor(RGB(255,0,0));pDC->SetBkMode(TRANSPARENT);return m_brush;break;case(IDC_CHOICE):pDC->SetTextColor(RGB(255,128,0));pDC->SetBkMode(TRANSPARENT);return m_brush;break;case(IDC_RADIO):pDC->SetTextColor(RGB(255,0,20));pDC->SetBkMode(TRANSPARENT);return m_brush;break;default:break;}

          ******************************************************************************************************************************************* OnEraseBkGnd與OnPaint的聯系是什么?

          轉自:http://topic.csdn.net/u/20091012/14/2b948708-6d7b-498a-9806-a2adbd000c5d.html (作者:Tr0j4n)

          系統重繪時,先調用OnEraseBkGnd擦除窗口的現有內容,再調用OnPaint繪制新內容。

          問題就產生的:在OnEraseBkGnd中,如果你不調用原來缺省的OnEraseBkGnd只是重畫背景則不會有閃爍。而在OnPaint里面,由于它隱含的調用了OnEraseBkGnd,而你又沒有處理OnEraseBkGnd函數,這時就和窗口缺省的背景刷相關了。缺省的OnEraseBkGnd操作使用窗口的缺省背景刷刷新背景(一般情況下是白刷),而隨后你又自己重畫背景造成屏幕閃動。

          另外的一個問題是OnEraseBkGnd不是每次都會被調用的。如果你調用Invalidate的時候參數為TRUE,那么在OnPaint里面隱含調用BeginPaint的時候就產生WM_ERASEBKGND消息,如果參數是FALSE 則不會重刷背景。

          解決方法有:1. 用OnEraseBkGnd實現,不要調用原來的OnEraseBkGnd函數。

          2. 用OnPaint實現,同時重載OnEraseBkGnd,并在其中直接返回TRUE. 3. 用OnPaint實現,創建窗口時設置背景刷為空。

          4. 用OnPaint實現,但是要求刷新時用Invalidate(FALSE)這樣的函數。(不過這種情況下,窗口覆蓋等造成的刷新還是要閃一下,所以不是徹底的解決方法)

        --------------------------------------------------------------------------------------------------------------------------------

          在MFC中任何一個window組件的繪圖都是放在這兩個member function中。在設定上OnEraseBkgnd()是用來畫底圖的,而OnPaint()是用來畫主要對象的。

          舉例說明,一個按鈕是灰色的,上面還有文字。則OnEraseBkgnd()所做的事就是把按鈕畫成灰色,而OnPaint()所做的事就是畫上文字。

          既然這兩個member function都是用來畫出組件的,那為何還要分OnPaint() 與 OnEraseBkgnd() 呢?

          其實OnPaint() 與 OnEraseBkgnd() 特性是有差別的:1. OnEraseBkgnd()的要求是快速,在里面的繪圖程序最好是不要太耗時間,因為每當window組件有任何小變動都會馬上呼叫OnEraseBkgnd() . 2. OnPaint() 是只有在程序有空閑的時候才會被呼叫。

          3. OnEraseBkgnd() 是在 OnPaint() 之前呼叫的。

          所以 OnPaint() 被呼叫一次之前?赡軙艚蠴nEraseBkgnd()好幾次。

          如果我們是一個在做圖形化使用者接口的人,常會需要把一張美美的圖片設為我們dialog的底圖。把繪圖的程序代碼放在OnPaint() 之中,可能會常碰到一些問題。比方說拖曳一個窗口在我們做的dialog上面一直移動,則dialog會變成灰色,直到動作停止才恢復。這是因為每次需要重繪的時候,程序都會馬上呼叫OnEraseBkgnd()。而OnEraseBkgnd()就把dialog畫成灰色,只有在動作停止之后,程序才會呼叫OnPaint(),這時才會把我們要畫的底圖貼上去。

          這個問題的解法:1. 比較差點的方法是把OnEraseBkgnd() 改寫成不做事的function ,如下所示:BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)

          { return TRUE;}以上本來是會呼叫CDialog::OnEraseBkgnd() ,但是如果我們不呼叫的話,程序便不會畫上灰色的底色了。

          2. 比較好的做法是,直接將繪圖的程序從OnPaint()移到OnEraseBkgnd()來做,如下所示 :

          // m_bmpBKGND 為一CBitmap對象,且事先早已加載我們的底圖// 底圖的大小與我們的窗口client大小一致

          BOOL CMyDlg::OnEraseBkgnd(CDC* pDC)

          { CRect rc;GetUpdateRect(&rc);CDC srcDC;srcDC.CreateCompatibleDC(pDC);srcDC.SelectObject(m_bmpBKGND);

          pDC->BitBlt(rc.left,rc.top,rc.GetWidth(), rc.GetHeight(),&srcDC,rc.left,rc.top,SRCCOPY);return TRUE;}

          特別要注意的是,取得重畫大小是使用GetUpdateRect() 而不是GetClientRect()。如果使用GetClientRect() 則會把不該重畫的地方重畫。

          *****************************************************************************************************************************************雙緩沖加重載onpaint,OnEraseBkgnd解決屏幕閃爍問題轉自:http://hi.baidu.com/lovevc2008/blog/item/9bc5a90b2a3eab1894ca6b0e.html自己實現了按鈕切換背景功能后,正暗自爽的我發現了一個很嚴重的問題。背景切換時總是先出現mfc自帶的灰色難看界面才刷出我用form image控件載入的圖片。上網google了很久?偹闶墙鉀Q我自己的問題。

          分三步走:第一, 在OnInitDialog中寫入//////////載入背景圖if( m_bmp.m_hObject != NULL ) //判斷m_bmp.DeleteObject();/////////載入圖片HBITMAP hbmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),"res\\aaaaa.BMP", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION|LR_LOADFROMFILE);if( hbmp == NULL )

          return FALSE;///////////////////////該斷程序用來取得加載的BMP的信息//////////////////////// m_bmp.Attach( hbmp );DIBSECTION ds;BITMAPINFOHEADER &bminfo = ds.dsBmih;m_bmp.GetObject( sizeof(ds), &ds );int cx=bminfo.biWidth; //得到圖像寬度int cy=bminfo.biHeight; //得到圖像高度/////得到了圖像的寬度和高度后,我們就可以對圖像大小進行適應,即調整控件的大小,讓它正好顯示一張圖片///// CRect rect;GetDlgItem(IDC_BAK)->GetWindowRect(&rect);ScreenToClient(&rect);GetDlgItem(IDC_BAK)->MoveWindow(rect.left,rect.top,cx,cy,true);//調整大小第二,重載onpaint函數//////////////以下三種情況任選一種會是不同效果(只能一種存在)/////////// //CPaintDC dc(this); //若用此句,得到的是對話框的DC,圖片將被繪制在對話框上。

          CPaintDC dc(GetDlgItem(IDC_BAK)); // 用此句,得到picture控件的DC,圖像將被繪制在控件上// CDC dc;// dc.m_hDC=::GetDC(NULL); //若用此兩句,得到的是屏幕的DC,圖片將被繪制在屏幕上////////// CRect rcclient;GetDlgItem(IDC_BAK)->GetClientRect(&rcclient);CDC memdc;// Step 1: 為屏幕DC創建兼容的內存DC : CreateCompatibleDC()

          memdc.CreateCompatibleDC(&dc);CBitmap bitmap;bitmap.CreateCompatibleBitmap(&dc, rcclient.Width(), rcclient.Height());// Step 2: 把位圖選入設備環境:SelectObject(),可以理解為選擇畫布memdc.SelectObject( &bitmap );CWnd::DefWindowProc(WM_PAINT, (WPARAM)memdc.m_hDC , 0);CDC maskdc;maskdc.CreateCompatibleDC(&dc);CBitmap maskbitmap;maskbitmap.CreateBitmap(rcclient.Width(), rcclient.Height(), 1, 1, NULL);maskdc.SelectObject( &maskbitmap );maskdc.BitBlt( 0, 0, rcclient.Width(), rcclient.Height(), &memdc, rcclient.left, rcclient.top, SRCCOPY);CBrush brush;brush.CreatePatternBrush(&m_bmp);dc.FillRect(rcclient, &brush);// Step 3: 把繪制好的圖形“拷貝”到屏幕上: BitBlt()

          dc.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(), &memdc, rcclient.left, rcclient.top,SRCPAINT);brush.DeleteObject();// Do not call CDialog::OnPaint() for painting messages第三,重載OnEraseBkgnd改為 return TRUE; // CDialog::OnEraseBkgnd(pDC);*******************************************************************************************************************************************解決Windows 程序界面閃爍問題的一些經驗轉載自:http://blog.joycode.com/yaodong/archive/2004/11/26/39764.joy一般的windows 復雜的界面需要使用多層窗口而且要用貼圖來美化,所以不可避免在窗口移動或者改變大小的時候出現閃爍。

          先來談談閃爍產生的原因原因一:如果熟悉顯卡原理的話,調用GDI函數向屏幕輸出的時候并不是立刻就顯示在屏幕上只是寫到了顯存里,而顯卡每隔一段時間把顯存的內容輸出到屏幕上,這就是刷新周期。

          一般顯卡的刷新周期是1/80秒左右,具體數字可以自己設置的。

          這樣問題就來了,一般畫圖都是先畫背景色,然后再把內容畫上去,如果這兩次操作不在同一個刷新周期內完成,那么給人的視覺感受就是,先看到只有背景色的圖像,然后看到畫上內容的圖像,這樣就會感覺閃爍了。

          解決方法:盡量快的輸出圖像,使輸出在一個刷新周期內完成,如果輸出內容很多比較慢,那么采用內存緩沖的方法,先把要輸出的內容在內存準備好,然后一次輸出到顯存。要知道一次API調用一般可以在一個刷新周期內完成。

          對于GDI,用創建內存DC的方法就可以了。

          原因二:復雜的界面有多層窗口組成,當windows在窗口改變大小的時候是先重畫父窗口,然后重畫子窗口,子父窗口重畫的過程一般無法在一個刷新周期內完成,所以會呈現閃爍。

          我們知道父窗口上被子窗口擋住的部分其實沒必要重畫的。

          解決方法:給窗口加個風格 WS_CLIPCHILDREN ,這樣父窗口上被子窗口擋住的部分就不會重畫了。如果同級窗口之間有重疊,那么需要再加上 WS_CLIPSIBLINGS 風格。

          原因三:有時需要在窗口上使用一些控件,比如IE,當你的窗口改變大小的時候IE會閃爍,即使你有了WS_CLIPCHILDREN也沒用。原因在于窗口的類風格有CS_HREDRAW 或者 CS_VREDRAW,這兩個風格表示窗口在寬度或者高度變化的時候重畫,但是這樣就會引起IE閃爍。

          解決方法:注冊窗口類的時候不要使用這兩個風格,如果窗口需要在改變大小的時候重畫,那么可以在WM_SIZE的時候調用RedrawWindow.原因四:界面上窗口很多,而且改變大小時很多窗口都要移動和改變大小,如果使用MoveWindow或者SetWindowPos兩個API來改變窗口的大小和位置,由于他們是等待窗口重畫完成后才返回,所以過程很慢,這樣視覺效果就可能會閃爍。

          解決方法:使用以下API來處理窗口移動,BeginDeferWindowPos, DeferWindowPos,EndDeferWindowPos.先調用 BeginDeferWindowPos 來設定需要移動的窗口的個數,在使用 DeferWindowPos 來移動窗口,這個API并不真的造成窗口移動,最后用 EndDeferWindowPos 一次性完成所有窗口的大小和位置的改變。

          有個地方要特別注意,要仔細計算清楚要移動多少個窗口,BeginDeferWindowPos設定的個數一定要和實際的個數一致,否則在Win9x下,如果實際移動的窗口數多于調用BeginDeferWindowPos時設定的個數,可能會造成系統崩潰。在Windows NT系列下不會有這樣的問題。


        本文出自:億恩科技【www.endtimedelusion.com】

        服務器租用/服務器托管中國五強!虛擬主機域名注冊頂級提供商!15年品質保障!--億恩科技[ENKJ.COM]

      1. 您可能在找
      2. 億恩北京公司:
      3. 經營性ICP/ISP證:京B2-20150015
      4. 億恩鄭州公司:
      5. 經營性ICP/ISP/IDC證:豫B1.B2-20060070
      6. 億恩南昌公司:
      7. 經營性ICP/ISP證:贛B2-20080012
      8. 服務器/云主機 24小時售后服務電話:0371-60135900
      9. 虛擬主機/智能建站 24小時售后服務電話:0371-60135900
      10. 專注服務器托管17年
        掃掃關注-微信公眾號
        0371-60135900
        Copyright© 1999-2019 ENKJ All Rights Reserved 億恩科技 版權所有  地址:鄭州市高新區翠竹街1號總部企業基地億恩大廈  法律顧問:河南亞太人律師事務所郝建鋒、杜慧月律師   京公網安備41019702002023號
          0
         
         
         
         

        0371-60135900
        7*24小時客服服務熱線

         
         
        av不卡不卡在线观看_最近2018年中文字幕_亚洲欧美一区二区三区_一级A爱做片免费观看国产_日韩在线中文天天更新_伊人中文无码在线