版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、VC 繪圖,使用雙緩沖技術(shù)實(shí)現(xiàn)*所有的 GDI 繪圖函數(shù)使用的都是邏輯坐標(biāo) (邏輯范圍 * *系統(tǒng)默認(rèn)情況下 物理范圍和邏輯范圍 是 1:1 的對(duì)應(yīng)關(guān)系*1. 首先定義類成員:CDC *m_pDC;CDC MemDC;CBitmap MemBitmap;CBitmap *pOldbitmap;LONG xRange; / 邏輯范圍, x 方向?qū)挾萀ONG yRange; / 邏輯范圍, y 方向高度LONG nWidht; / 物理范圍, x 方向?qū)挾萀ONG nHeight; / 物理范圍, y 方向高度2. 在類初始化函數(shù)中:m_pDC = this-GetDC(; / 獲取設(shè)備上下文句柄
2、CWnd *wnd = GetDlgItem(IDC_SHOWGRAPH; / 獲取界面上顯示圖形的 ID 控件的句 柄wnd-GetWindowRect(▭ / 獲取顯示 /畫圖區(qū)域大小 (物理范圍 ScreenToClient(▭ / 轉(zhuǎn)換為客戶區(qū)坐標(biāo)nWidth = rect.Width(; / 顯示 /畫圖區(qū)域 x 方向物理寬度nHeight = rect.Height(; / 顯示 /畫圖區(qū)域 y 方向物理高度3. 在自定義函數(shù)中, 設(shè)置視口與窗口的比例關(guān)系 :m_pDC-SetMapMode(MM_ANISOTROPIC; / 注意 MM_ANISOTROPIC
3、和MM_ISOTROPIC的區(qū)別m_pDC-SetWindowExt(XRange,-yRange ; / 設(shè)定窗口尺寸范圍, 畫圖使用的邏輯范圍, 實(shí)現(xiàn)放大或是縮小,坐標(biāo)方向 和 為正向m_pDC-SetViewportExt(nWidth,nHeight ; / 設(shè)定視口尺寸范圍,客戶區(qū)實(shí)際圖形顯示的 區(qū)域范圍,大小固定4. 在自定義函數(shù)中, 雙緩沖技術(shù)的使用 :MemDC.CreateCompatibleDC(m_pDC; / 創(chuàng)建內(nèi)存兼容設(shè)備上下文 MemBitmap.CreateCompatibleBitmap(m_pDC,xRange,yRange ; / 創(chuàng)建內(nèi)存兼容畫布, 大小
4、由邏輯范圍決定pOldbitmap = MemDC.SelectObject(&MemBitmap; / 將畫布選入內(nèi)存設(shè)備上下 文MemDC.FillSolidRect(0,0,xRange,yRange ,RGB(123,213,132; / 對(duì)內(nèi)存中的畫布填充背 景顏色,否則是默認(rèn)的黑色/ 畫圖操作,如畫一條對(duì)角直線MemDC.MoveTo(0,0;MemDC.LineTo(xRange*0.9,yRange*0.9;/ 將內(nèi)存中的畫圖區(qū)域拷貝到界面的控件區(qū)域上去/ 第 1和第 2個(gè)參數(shù)若是 0時(shí),則從物理坐標(biāo)的 (rect.left,rect.bottom點(diǎn)上開始按上述指定 的方向貼圖
5、m_pDC-BitBlt(0,0,xRange,yRange ,&MemDC,0,0,SRCCOPY;5. 在類的析構(gòu)函數(shù)中:MemDC.SelectObject(pOldbitmap;bitmap.DeleteObject(;this-ReleaseDC(m_pDC;6. 至此,就完成了 雙緩沖 及 坐標(biāo)縮放 繪圖的功能* *用 VC 做的畫圖程序, 當(dāng)所畫的圖形大于屏幕時(shí), 在拖動(dòng)滾動(dòng)條時(shí)屏幕就會(huì)出現(xiàn)嚴(yán)重的閃爍, 為了解決這一問(wèn)題, 就得使用雙緩沖來(lái)解決。 程序產(chǎn)生嚴(yán)重的閃爍問(wèn)題是因?yàn)楫媹D過(guò)程中前 后兩次的畫面反差很大造成的人的視覺(jué)的閃爍。因?yàn)樵?VC 中每次在調(diào)用 OnDraw 時(shí)系統(tǒng)
6、都是先用背景畫刷將畫布清除再執(zhí)行畫圖命令,這樣在你每次移動(dòng)滾動(dòng)條時(shí)每執(zhí)行一次 OnDraw 就會(huì)有一個(gè)空白頁(yè), 這樣和你的最終結(jié)果圖象之間有一個(gè)很大的反差, 因而看起來(lái) 閃爍,而且滾動(dòng)條滾動(dòng)越快閃爍越嚴(yán)重。當(dāng)然,你可以將背景畫刷設(shè)為 NULL ,這樣可以解 決閃爍問(wèn)題,但是不能將先前的圖象擦除,這樣整個(gè)屏幕就顯得很亂。下面將利用雙緩沖來(lái)解決這一問(wèn)題的思路給大家作一下簡(jiǎn)單的介紹。我先來(lái)解釋一下在 MFC 里面很關(guān)鍵的設(shè)備環(huán)境描述符, 也就是所謂的 DC (device context 。在 dos 時(shí)代, 我們?nèi)绻L圖, 必須通過(guò)一系列系統(tǒng)函數(shù)來(lái)啟動(dòng)圖形環(huán)境 (用過(guò) turbo pascal或
7、者 turbo c的人該還有印象吧 , 這之間對(duì)各種硬件的初始化參數(shù)都不相同, 非常的煩人, 常常還要查閱硬件手冊(cè), 那時(shí)的程序智能針對(duì)最流行的硬件來(lái)編寫, 對(duì)不流行的就沒(méi)有辦法 了。 windows 操作系統(tǒng)為了屏蔽不同的硬件環(huán)境,讓編程時(shí)候不考慮具體的硬件差別,采 取了一系列辦法, 設(shè)備環(huán)境描述符就是這樣產(chǎn)生的。 簡(jiǎn)單地說(shuō), 設(shè)備描述符抽象了不同的硬 件環(huán)境為標(biāo)準(zhǔn)環(huán)境, 用戶編寫時(shí)使用的是這個(gè)虛擬的標(biāo)準(zhǔn)環(huán)境, 而不是真實(shí)的硬件, 與真實(shí) 硬件打交道的工作一般交給系統(tǒng)和驅(qū)動(dòng)程序去完成 (這同樣解釋了為什么我們需要經(jīng)常更新 驅(qū)動(dòng)程序的問(wèn)題。 使用在 windows 圖形系統(tǒng)(gdi ,而不包括
8、 direct x上面,就體現(xiàn)在一系列的圖形 DC 上面, 我們?nèi)绻?gdi 上面繪圖, 就必須先得到圖形 DC 的句柄 (handle , 然后在指定句柄的基礎(chǔ)上進(jìn)行圖形操作。那么我們?cè)趺丛?sdk 環(huán)境下面繪圖的呢,我想這個(gè)大家都不太清楚,但是確實(shí)很基礎(chǔ)。在 windows 的 sdk 環(huán)境下面,我們用傳統(tǒng)的 c 編寫程序,在需要的繪圖地方(比如響應(yīng) WM_PAINT消息的分支這樣做:hdc = GetDC( hwnd ;oldGdiObject = SelectObject( hdc,newGdiObject ;. 繪圖操作 .SelectObject( hdc,oldGdiObje
9、ct ;DeleteObject( newGdiObject ;ReleaseDC( hdc;或者這樣BeginPaint( hwnd,&ps ; /PAINTSTRUCT ps - ps is a paint struct. 繪圖操作 .EndPaint( hwnd 這就是大概的過(guò)程, 我們看到了 hdc (圖形 DC 句柄的應(yīng)用。在繪圖的部分,每一個(gè)繪圖 函數(shù)基本上也要用到這個(gè)句柄, 最后我們還必須釋放它, 否則將嚴(yán)重影響性能。 每次我們都 必須調(diào)用 GetDC 這個(gè) api 函數(shù)得到(不能用全局變量保存結(jié)果重復(fù)使用,我在后面將做解 釋。 這些是最最基本的 windows 圖形操作的方式,
10、相比 dos 時(shí)代簡(jiǎn)單了些,但是有些概 念也難理解了些。 vb 里面的簡(jiǎn)單的 point 函數(shù)其實(shí)最后也是被轉(zhuǎn)化為這樣的方式來(lái)執(zhí)行, 系 統(tǒng)幫助做了很多事情。到了 MFC 里面,由于有了封裝,所有的 hdc 被隱藏在對(duì)象中做為隱藏參數(shù)來(lái)傳遞(就是 DC 類的 this 啦 , 所以我們的關(guān)鍵話題就轉(zhuǎn)變?yōu)榱嗽鯓拥玫较胍?DC 類而已。 這個(gè) 過(guò)程其實(shí)大同小異。 在消息響應(yīng)的過(guò)程中, WM_PAINT被轉(zhuǎn)變?yōu)?OnDraw(或是 OnPaint(之類的一系列函數(shù)來(lái)響應(yīng),這些函數(shù)一般都有個(gè)參數(shù) CDC *pDC傳入進(jìn)來(lái),因此在這些函 數(shù)里面,我們只需直接畫圖就可以了 ,和以前 sdk 的方式一樣。
11、但是 WM_PAINT消息響應(yīng)的頻度太高了,比如最小化最大化,移動(dòng)窗體,覆蓋等等都引起 重繪, 經(jīng)常的這樣畫圖, 很是消耗性能; 在有些場(chǎng)合, 比如隨機(jī)作圖的場(chǎng)合, 每一次就改變, 還導(dǎo)致了程序的無(wú)法實(shí)現(xiàn)。怎么解決后一種問(wèn)題呢。ms 在 msdn 的例子里面交給我們 document/view的經(jīng)典解決辦法,將圖形的數(shù)據(jù)存儲(chǔ)在 document 類里面, view 類只是根據(jù)這些數(shù)據(jù)繪圖。比如你要畫個(gè)圓, 只是將圓心和半徑存 在 document 里面, view 類根據(jù)這個(gè)里面的數(shù)據(jù)在屏幕上面重新繪制。那么,我們只需要 隨機(jī)產(chǎn)生一次數(shù)據(jù)就可以了。這樣還是存在性能的問(wèn)題, 于是我們開始考慮另外
12、的解決方法。 我們知道, 將內(nèi)存中的圖片 原樣輸出到屏幕是很快的,這也是我們?cè)?dos 時(shí)代經(jīng)常做的事情,能不能在 windows 也重新利用呢?答案就是內(nèi)存緩沖繪圖。 這就是我們今天的主題。我們還是回到 DC 上來(lái), 既然 DC 是繪圖對(duì)象, 我們也就可以自己在內(nèi)存里面造一個(gè), 讓它 等于我們想要的繪圖對(duì)象,圖(CBitmap 可以存儲(chǔ)在 document 類里面,每一次刷新屏 幕都只需將這個(gè)圖輸出到屏幕上面,每一次作圖都是在內(nèi)存里面繪制,保存在 document 的圖里面, 必要時(shí)還可以將圖輸出到外存保存。 這樣既保證了速度, 也解決了隨機(jī)的問(wèn)題, 在復(fù)雜作圖的情況下對(duì)內(nèi)存的開銷也不大(總
13、是一副圖片的大小。 這是一個(gè)很好的解決 辦法,現(xiàn)在讓我們來(lái)實(shí)現(xiàn)它們。1. 我們首先在 document 類里面保存一個(gè)圖片CBitmap m_bmpBuf; /這里面保存了我們做的圖,存在于內(nèi)存中2. 其次在 view 類里面,我們需要將這個(gè)圖拷貝到屏幕上去,于 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
14、-m_bmpBuf.GetBitmap(&bmpinfo;pDC-BitBlt(0,0,bmpinfo.bmWidth,bmpinfo.bmHeight,&dcMem,0,0,SRCCOPY; dcMem.SelectObject(pOldBitmap;dcMem.DeleteDC(;3. 在我們需要畫圖的函數(shù)里,完成繪圖工作CBmpDrawDoc *pDoc = GetDocument(; / 得到 document 中的 bitmap 對(duì)象CDC *pDC = GetDC(;CDC dcMem;dcMem.CreateCompatibleDC(NULL; / 這里我們就在內(nèi)存中虛擬建造了
15、DCpDoc-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; / 這些是繪圖操作,隨便你 _ dcMem.TextOut(0,0,Hello,world!;dcMem.Rectangle(20,20,40,40;dcM
16、em.FillSolidRect(40,40,50,50,RGB(255,0,0;pDC-BitBlt(0,0,100,100,&dcMem,0,0,SRCCOPY; / 拷貝到屏幕 dcMem.SelectObject(pOldBitmap;dcMem.DeleteDC(;全部的過(guò)程就是這樣,很簡(jiǎn)單吧。以此為例子還可以實(shí)現(xiàn) 2個(gè)緩沖或者多個(gè)緩沖等等,視 具體情況而定。 當(dāng)然在緩沖區(qū)還可以實(shí)現(xiàn)很多高級(jí)的圖形操作,比如透明, 合成等等, 取決 于具體的算法,需要對(duì)內(nèi)存直接操作(其實(shí)就是當(dāng)年 dos 怎么做,現(xiàn)在還怎么做。再來(lái)解釋一下前面說(shuō)的 為什么不能用全局變量保存 DC 問(wèn)題 :其實(shí) DC 也
17、是用句柄來(lái)標(biāo)識(shí)的, 所以也具有句柄的不確定性, 就是只能隨用隨取, 不同時(shí)間兩次取得的是不同的 (使用過(guò)文 件句柄地話,應(yīng)該很容易理解的。那么我們用全局變量保存的 DC 就沒(méi)什么意義了,下次 使用只是什么也畫不出來(lái)。(這一點(diǎn)的理解可以這樣:DC 需要占用一定的內(nèi)存,那么在頻 繁的頁(yè)面調(diào)度中,位置難免改變,于是用來(lái)標(biāo)志指針的句柄也就不同了。* *顯示圖形如何避免閃爍顯示圖形如何避免閃爍,如何提高顯示效率是問(wèn)得比較多的問(wèn)題。而且多數(shù)人認(rèn)為 MFC 的 繪圖函數(shù)效率很低,總是想尋求其它的解決方案。 MFC 的繪圖效率的確不高但也不差,而 且它的繪圖函數(shù)使用非常簡(jiǎn)單,只要使用方法得當(dāng),再加上一些技巧,
18、用 MFC 可以得到效 率很高的繪圖程序。 我想就我長(zhǎng)期(呵呵當(dāng)然也只有 2年多使用 MFC 繪圖的經(jīng)驗(yàn)談?wù)?我的一些觀點(diǎn)。1、顯示的圖形為什么會(huì)閃爍?我們的繪圖過(guò)程大多放在 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),使得顯示窗口看起來(lái)在閃。如果將背景刷設(shè)置成 NULL ,這樣無(wú)論怎樣重繪圖 形都不會(huì)閃了。 當(dāng)然,這樣做會(huì)使得窗口的顯示亂成一團(tuán),因?yàn)橹乩L時(shí)沒(méi)有
19、背景色對(duì)原來(lái) 繪制的圖形進(jìn)行清除,而又疊加上了新的圖形。有的人會(huì)說(shuō), 閃爍是因?yàn)槔L圖的速度太慢或者顯示的圖形太復(fù)雜造成的, 其實(shí)這樣說(shuō)并不對(duì), 繪圖的顯示速度對(duì)閃爍的影響不是根本性的。例如在 OnDraw(CDC *pDC中這樣寫:pDC-MoveTo(0,0;pDC-LineTo(100,100;這個(gè)繪圖過(guò)程應(yīng)該是非常簡(jiǎn)單、 非??炝税? 但是拉動(dòng)窗口變化時(shí)還是會(huì)看見(jiàn)閃爍。 其實(shí)從 道理上講, 畫圖的過(guò)程越復(fù)雜越慢閃爍應(yīng)該越少, 因?yàn)槔L圖用的時(shí)間與用背景清除屏幕所花 的時(shí)間的比例越大人對(duì)閃爍的感覺(jué)會(huì)越不明顯。比如:清楚屏幕時(shí)間為 1s 繪圖時(shí)間也是為 1s ,這樣在 10s 內(nèi)的連續(xù)重畫中就
20、要閃爍 5次;如果清楚屏幕時(shí)間為 1s 不變,而繪圖時(shí)間 為 9s ,這樣 10s 內(nèi)的連續(xù)重畫只會(huì)閃爍一次。這個(gè)也可以試驗(yàn),在 OnDraw(CDC *pDC中這樣寫:for(int i=0;iMoveTo(0,i;pDC-LineTo(1000,i;呵呵,程序有點(diǎn)變態(tài),但是能說(shuō)明問(wèn)題。說(shuō)到這里可能又有人要說(shuō)了, 為什么一個(gè)簡(jiǎn)單圖形看起來(lái)沒(méi)有復(fù)雜圖形那么閃呢?這是 因?yàn)閺?fù)雜圖形占的面積大, 重畫時(shí)造成的反差比較大, 所以感覺(jué)上要閃得厲害一些, 但是閃 爍頻率要低。那為什么動(dòng)畫的重畫頻率高,而看起來(lái)卻不閃?這里,我就要再次強(qiáng)調(diào)了,閃 爍是什么? 閃爍就是反差, 反差越大, 閃爍越厲害 。 因?yàn)?/p>
21、動(dòng)畫的連續(xù)兩個(gè)幀之間的差異很小 所以看起來(lái)不閃。如果不信,可以在動(dòng)畫的每一幀中間加一張純白的幀,不閃才怪呢。2、如何避免閃爍在知道圖形顯示閃爍的原因之后,對(duì)癥下藥就好辦了。(1. 首先是去掉 MFC 提供的背景繪制過(guò)程 。實(shí)現(xiàn)的方法很多:* 可以在窗口形成時(shí)給窗口的注冊(cè)類的背景刷賦 NULL* 也可以在形成以后修改背景static CBrush brush(RGB(255,0,0;SetClassLong(this-m_hWnd,GCL_HBRBACKGROUND,(LONG(HBRUSHbrush; * 要簡(jiǎn)單也可以重載 OnEraseBkgnd(CDC* pDC直接返回 TRUE這樣背景沒(méi)
22、有了,結(jié)果圖形顯示的確不閃了,但是顯示也象前面所說(shuō)的一樣,變得一團(tuán) 亂。怎么辦?(2. 這就要用到 雙緩存的方法 了。 雙緩沖就是除了在屏幕上有圖形進(jìn)行顯示以外,在內(nèi) 存中也有圖形在繪制。 我們可以把要顯示的圖形先在內(nèi)存中繪制好, 然后再一次性的將內(nèi)存 中的圖形按照一個(gè)點(diǎn)一個(gè)點(diǎn)地覆蓋到屏幕上去 (這個(gè)過(guò)程非??? 因?yàn)槭欠浅R?guī)整的內(nèi)存拷 貝。這樣在內(nèi)存中繪圖時(shí),隨便用什么反差大的背景色進(jìn)行清除都不會(huì)閃,因?yàn)榭床灰?jiàn)。 當(dāng)貼到屏幕上時(shí), 因?yàn)閮?nèi)存中最終的圖形與屏幕顯示圖形差別很小 (如果沒(méi)有運(yùn)動(dòng), 當(dāng)然就 沒(méi)有差別,這樣看起來(lái)就不會(huì)閃。3、如何實(shí)現(xiàn)雙緩沖首先給出實(shí)現(xiàn)的程序,然后再解釋,同樣是在 O
23、nDraw(CDC *pDC中:CRect rc; / 定義一個(gè)矩形區(qū)域變量GetClientRect(rc;int nWidth = rc.Width(;int nHeight = rc.Height(;CDC *pDC = GetDC(; / 定義設(shè)備上下文CDC MemDC; / 定義一個(gè)內(nèi)存顯示設(shè)備對(duì)象CBitmap MemBitmap; / 定義一個(gè)位圖對(duì)象/建立與屏幕顯示兼容的內(nèi)存顯示設(shè)備MemDC.CreateCompatibleDC(pDC;/建立一個(gè)與屏幕顯示兼容的位圖,位圖的大小可選用窗口客戶區(qū)的大小MemBitmap.CreateCompatibleBitmap(pDC,
24、nWidth,nHeight ;/將位圖選入到內(nèi)存顯示設(shè)備中,只有選入了位圖的內(nèi)存顯示設(shè)備才有地方繪圖,畫到指 定的位圖上CBitmap *pOldBit = MemDC.SelectObject(&MemBitmap;/先用背景色將位圖清除干凈,否則是黑色。這里用的是白色作為背景 MemDC.FillSolidRect(0,0,nWidth,nHeight ,RGB(255,255,255;/繪圖操作等在這里實(shí)現(xiàn)MemDC.MoveTo(;MemDC.LineTo(;MemDC.Ellipse(;/將內(nèi)存中的圖拷貝到屏幕上進(jìn)行顯示pDC-BitBlt(0,0,nWidth,nHeight ,
25、&MemDC,0,0,SRCCOPY;/繪圖完成后的清理MemDC.SelectObject(pOldbitmap;MemBitmap.DeleteObject(;上面的注釋應(yīng)該很詳盡了,廢話就不多說(shuō)了。4、如何提高繪圖的效率我主要做的是電力系統(tǒng)的網(wǎng)絡(luò)圖形的 CAD 軟件,在一個(gè)窗口中往往要顯示成千上萬(wàn)個(gè)電力 元件, 而每個(gè)元件又是由點(diǎn)、線、圓等基本圖形構(gòu)成。如果真要在一次重繪過(guò)程重畫這么多 元件, 可想而知這個(gè)過(guò)程是非常漫長(zhǎng)的。 如果加上了圖形的瀏覽功能, 鼠標(biāo)拖動(dòng)圖形滾動(dòng)時(shí) 需要進(jìn)行大量的重繪,速度會(huì)慢得讓用戶將無(wú)法忍受。怎么辦?只有再研究研究 MFC 的繪 圖過(guò)程了。實(shí)際上,在 OnDr
26、aw(CDC *pDC中繪制的圖并不是所有都顯示了的,例如:你在 OnDraw 中畫了兩個(gè)矩形, 在一次重繪中雖然兩個(gè)矩形的繪制函數(shù)都有執(zhí)行, 但是很有可能只有一個(gè) 顯示了,這是因?yàn)?MFC 本身為了提高重繪的效率設(shè)置了裁剪區(qū)。裁剪區(qū)的作用就是:只有 在這個(gè)區(qū)內(nèi)的繪圖過(guò)程才會(huì)真正有效, 在區(qū)外的是無(wú)效的, 即使在區(qū)外執(zhí)行了繪圖函數(shù)也是 不會(huì)顯示的。 因?yàn)槎鄶?shù)情況下窗口重繪的產(chǎn)生大多是因?yàn)榇翱诓糠直徽趽趸蛘叽翱谟袧L動(dòng)發(fā) 生,改變的區(qū)域并不是整個(gè)圖形而只有一小部分,這一部分需要改變的就是 pDC 中的裁剪 區(qū)了。 因?yàn)轱@示 (往內(nèi)存或者顯存都叫顯示 比繪圖過(guò)程的計(jì)算要費(fèi)時(shí)得多,有了裁剪區(qū)后 顯示的
27、就只是應(yīng)該顯示的部分,大大提高了顯示效率。但是這個(gè)裁剪區(qū)是 MFC 設(shè)置的,它 已經(jīng)為我們提高了顯示效率, 在進(jìn)行復(fù)雜圖形的繪制時(shí)如何進(jìn)一步提高效率呢?那就只有去 掉在裁剪區(qū)外的繪圖過(guò)程了。 可以先用 pDC-GetClipBox(得到裁剪區(qū), 然后在繪圖時(shí)判斷 你的圖形是否在這個(gè)區(qū)內(nèi),如果在就畫, 不在就不畫。但如果你的繪圖過(guò)程不復(fù)雜,這樣做 可能對(duì)你的繪圖效率不會(huì)有提高。* *雙緩存即現(xiàn)在內(nèi)存 dc 中作圖,而后一次性地拷貝到屏幕上,所以提高了繪圖的速度。但只 用此方法不能根本解決閃爍的問(wèn)題。而將響應(yīng) WM_ERASEBKGND 的重載函數(shù) OnEraseBkgnd(CDC* pDC 直接
28、返回 TRUE 是最好的辦法。如下:BOOL CMyWin:OnEraseBkgnd(CDC* pDCreturn TRUE;/return CWnd:OnEraseBkgnd(pDC; /把系統(tǒng)原來(lái)的這條語(yǔ)句注釋掉* *如何修改控件的背景模式及控件的字體顏色1. 改變對(duì)話框的背景色在 CApp 類中的 InitInstance(里添加SetDialogBkColor(RGB(0,192,0,RGB(0,0,0;2. 如果想改變靜態(tài)文本或單選按鈕的背景色,首先需要獲得控件 ID ,然后設(shè)置背景色,具 體步驟:(1 響應(yīng)對(duì)話框類的 WM_CTLCOLOR消息,生成 OnCtlColor 函數(shù)(2
29、 為對(duì)話框類添加成員變量 CBrush m_brush;并在初始化函數(shù)中初始化 m_brush.CreateSolidBrush(RGB(0,255,0; /顏色在這里設(shè) 置(3 在 OnCtlColor 函數(shù)中添加代碼,以改變控件的文字顏色和背景色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(
30、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 的聯(lián)系是什么?轉(zhuǎn)自: 者 :Tr0j4n系統(tǒng)重繪時(shí),先調(diào)用 OnEraseBkGn
31、d 擦除窗口的現(xiàn)有內(nèi)容,再調(diào)用 OnPaint 繪制新內(nèi)容。問(wèn)題就產(chǎn)生的:在 OnEraseBkGnd 中,如果你不調(diào)用原來(lái)缺省的 OnEraseBkGnd 只 是重畫背景則不會(huì)有閃爍。而在 OnPaint 里面,由于它隱含的調(diào)用了 OnEraseBkGnd ,而 你又沒(méi)有處理 OnEraseBkGnd 函數(shù),這時(shí)就和窗口缺省的背景刷相關(guān)了。缺省的 OnEraseBkGnd 操作使用窗口的缺省背景刷刷新背景 (一般情況下是白刷 , 而隨后你又自己 重畫背景造成屏幕閃動(dòng)。另外的一個(gè)問(wèn)題是 OnEraseBkGnd 不是每次都會(huì)被調(diào)用的。如果你調(diào)用 Invalidate 的 時(shí)候參數(shù)為 TRUE
32、,那么在 OnPaint 里面隱含調(diào)用 BeginPaint 的時(shí)候就產(chǎn)生WM_ERASEBKGND消息,如果參數(shù)是 FALSE 則不會(huì)重刷背景。解決方法有:1. 用 OnEraseBkGnd 實(shí)現(xiàn),不要調(diào)用原來(lái)的 OnEraseBkGnd 函數(shù)。2. 用 OnPaint 實(shí)現(xiàn),同時(shí)重載 OnEraseBkGnd ,并在其中直接返回 TRUE 。3. 用 OnPaint 實(shí)現(xiàn),創(chuàng)建窗口時(shí)設(shè)置背景刷為空。4. 用 OnPaint 實(shí)現(xiàn), 但是要求刷新時(shí)用 Invalidate(FALSE這樣的函數(shù)。 (不過(guò)這種情況下, 窗口覆蓋等造成的刷新還是要閃一下,所以不是徹底的解決方法 -在 MFC 中任
33、何一個(gè) window 組件的繪圖都是放在這兩個(gè) member function中。在設(shè)定上 OnEraseBkgnd(是用來(lái)畫底圖的 ,而 OnPaint(是用來(lái)畫主要對(duì)象的 。舉例說(shuō)明,一個(gè)按鈕是灰色的,上面還有文字。則 OnEraseBkgnd(所做的事就是把按鈕畫 成灰色,而 OnPaint(所做的事就是畫上文字。既然這兩個(gè) member function都是用來(lái)畫出組件的,那為何還要分 OnPaint( 與 OnEraseBkgnd( 呢?其實(shí) OnPaint( 與 OnEraseBkgnd( 特性是有差別的:1. OnEraseBkgnd(的要求是快速,在里面的繪圖程序最好是不要太耗
34、時(shí)間,因?yàn)槊慨?dāng) window 組件有任何小變動(dòng)都會(huì)馬上呼叫 OnEraseBkgnd( 。2. OnPaint( 是只有在程序有空閑的時(shí)候才會(huì)被呼叫。3. OnEraseBkgnd( 是在 OnPaint( 之前呼叫的。所以 OnPaint( 被呼叫一次之前??赡軙?huì)呼叫 OnEraseBkgnd(好幾次。如果我們是一個(gè)在做圖形化使用者接口的人,常會(huì)需要把一張美美的圖片設(shè)為我們 dialog 的底圖。 把繪圖的程序代碼放在 OnPaint( 之中, 可能會(huì)常碰到一些問(wèn)題。 比方說(shuō)拖曳一個(gè) 窗口在我們做的 dialog 上面一直移動(dòng),則 dialog 會(huì)變成灰色,直到動(dòng)作停止才恢復(fù)。這是 因?yàn)槊看?/p>
35、需要重繪的時(shí)候,程序都會(huì)馬上呼叫 OnEraseBkgnd(。而 OnEraseBkgnd(就把 dialog 畫成灰色,只有在動(dòng)作停止之后,程序才會(huì)呼叫 OnPaint(,這時(shí)才會(huì)把我們要畫的 底圖貼上去。這個(gè)問(wèn)題的解法:1. 比較差點(diǎn)的方法是把 OnEraseBkgnd( 改寫成不做事的 function ,如下所示:BOOL CMyDlg:OnEraseBkgnd(CDC* pDCreturn TRUE;以上本來(lái)是會(huì)呼叫 CDialog:OnEraseBkgnd( ,但是如果我們不呼叫的話,程序便不會(huì)畫 上灰色的底色了。2. 比較好的做法是, 直接將繪圖的程序從 OnPaint(移到 O
36、nEraseBkgnd(來(lái)做, 如下所示 :/ m_bmpBKGND 為一 CBitmap 對(duì)象,且事先早已加載我們的底圖/ 底圖的大小與我們的窗口 client 大小一致BOOL CMyDlg:OnEraseBkgnd(CDC* pDCCRect 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;r
37、eturn TRUE; 特別要注意的是, 特別要注意的是,取得重畫大小是使用 GetUpdateRect( 而不是 GetClientRect(。如果 。 則會(huì)把不該重畫的地方重畫。 使用 GetClientRect( 則會(huì)把不該重畫的地方重畫。 * * 雙緩沖加重載 onpaint,OnEraseBkgnd 解決屏幕閃爍問(wèn)題 轉(zhuǎn)自: 自己實(shí)現(xiàn)了按鈕切換背景功能后,正暗自爽的我發(fā)現(xiàn)了一個(gè)很嚴(yán)重的問(wèn)題.背景切換時(shí)總是 先出現(xiàn) mfc 自帶的灰色難看界面才刷出我用 form image 控件載入的圖片.上網(wǎng) google 了很 久.總算是解決我自己的問(wèn)題. 分三步走: 第一, 在 OnInitDi
38、alog 中寫入 /載入背景圖 if( m_bmp.m_hObject != NULL /判斷 m_bmp.DeleteObject(; /載入圖片 HBITMAP hbmp = (HBITMAP:LoadImage(AfxGetInstanceHandle(, resaaaaa.BMP, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION|LR_LOADFROMFILE; if( hbmp = NULL return FALSE; /該斷程序用來(lái)取得加載的 BMP 的信息/ m_bmp.Attach( hbmp ; DIBSECTION ds; BITMAPINF
39、OHEADER &bminfo = ds.dsBmih; m_bmp.GetObject( sizeof(ds, &ds ; int cx=bminfo.biWidth; /得到圖像寬度 int cy=bminfo.biHeight; /得到圖像高度 /得到了圖像的寬度和高度后,我們就可以對(duì)圖像大小進(jìn)行適應(yīng),即調(diào)整控件的大小,讓它正 好顯示一張圖片/ CRect rect; GetDlgItem(IDC_BAK-GetWindowRect(▭ ScreenToClient(▭ GetDlgItem(IDC_BAK-MoveWindow(rect.left,rect.top,
40、cx,cy,true;/調(diào)整大小 第二,重載 onpaint 函數(shù) /以下三種情況任選一種會(huì)是不同效果(只能一種存在/ /CPaintDC dc(this; /若用此句,得到的是對(duì)話框的 DC,圖片將被繪制在對(duì)話框上. CPaintDC dc(GetDlgItem(IDC_BAK; / 用此句,得到 picture 控件的 DC,圖像將被繪制在控 件上 / CDC dc; / dc.m_hDC=:GetDC(NULL; /若用此兩句,得到的是屏幕的 DC,圖片將被繪制在屏幕上 / CRect rcclient; GetDlgItem(IDC_BAK-GetClientRect(&rcclien
41、t; CDC memdc; / Step 1: 為屏幕 DC 創(chuàng)建兼容的內(nèi)存 DC : CreateCompatibleDC( memdc.CreateCompatibleDC(&dc; CBitmap bitmap; bitmap.CreateCompatibleBitmap(&dc, rcclient.Width(, rcclient.Height(; / Step 2: 把位圖選入設(shè)備環(huán)境:SelectObject(,可以理解為選擇畫布 memdc.SelectObject( &bitmap ; CWnd:DefWindowProc(WM_PAINT, (WPARAMmemdc.m_hD
42、C , 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 第三,重載 O
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- (2024版)農(nóng)業(yè)科技企業(yè)生產(chǎn)與銷售合作框架協(xié)議
- 企業(yè)企業(yè)大數(shù)據(jù)分析技術(shù)轉(zhuǎn)移協(xié)議(2024年版)
- 2024年工程用物資租賃協(xié)議
- 人工智能合作協(xié)議
- 2024年光伏項(xiàng)目安裝勞務(wù)分包合同
- 2024年古建筑修復(fù)工程施工合同
- 2024年工程木工承包簡(jiǎn)易合同樣本
- 2024年發(fā)電廠電力工程承包合同
- 2024年地震災(zāi)區(qū)廢墟清理與拆除合同
- 工業(yè)設(shè)備安裝施工合同要點(diǎn)
- 心電圖的基礎(chǔ)知識(shí)課件.ppt
- 科普知識(shí)講座(火箭)PPT精選課件
- 高三一模動(dòng)員主題班會(huì)-課件(PPT演示)
- 鈦加工工藝方法綜述
- 車轍的形成原因及預(yù)防措施
- 2022年同濟(jì)大學(xué)單獨(dú)考試研究生報(bào)考資格審查表
- 風(fēng)電場(chǎng)升壓站建筑工程主要施工方案
- 施工單位試驗(yàn)室驗(yàn)收方案
- 從PK-PD看抗菌藥物的合理應(yīng)用
- 癃閉中醫(yī)護(hù)理方案解答
- MCGS與1500連接配置說(shuō)明
評(píng)論
0/150
提交評(píng)論