日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[转载] MFC绘制动态曲线,用双缓冲绘图技术防闪烁

發(fā)布時(shí)間:2023/12/20 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转载] MFC绘制动态曲线,用双缓冲绘图技术防闪烁 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)載的原文地址

先上效果圖


隨著時(shí)間的推移,曲線向右平移,同時(shí)X軸的時(shí)間坐標(biāo)跟著更新。


一、如何繪制動(dòng)態(tài)曲線


  所謂動(dòng)畫,都是一幀一幀的圖像連續(xù)呈現(xiàn)在用戶面前形成的。所以如果你掌握了如何繪制靜態(tài)曲線,那么學(xué)會(huì)繪制動(dòng)態(tài)曲線也不遠(yuǎn)啦,只需要?jiǎng)?chuàng)建一個(gè)定時(shí)器(比如調(diào) 用MFC中的SetTimmer函數(shù)),每隔一定時(shí)間(比如1ms),調(diào)用OnPaint或者OnDraw函數(shù),繪制當(dāng)前幀圖像即可。
  這里需要注意的是,繪制圖像的代碼需要寫在OnPaint或者OnDraw函數(shù)中,因?yàn)楫?dāng)窗口失效(比如最小化)恢復(fù)后,會(huì)重新繪制當(dāng)前窗口,窗口之前的自繪圖像會(huì)丟失。而把繪圖代碼寫在OnPaint或者OnDraw中就是為了讓窗口每次重繪時(shí)也能重繪你自己畫的圖像,避免出現(xiàn)窗口最小化再恢復(fù)后,自己畫的圖像丟失的尷尬情況。
  另外繪制當(dāng)前幀圖像之前,記得用InvalidateRect函數(shù)清除上一幀圖像,不然各幀圖像會(huì)背景的堆疊。
  比如我想清除窗口中(0,0)和(100,100)這兩點(diǎn)確定的矩形中的圖像,代碼如下:

?? ?CRect Rect; ?? ?Rect.top = 0; ?? ?Rect.left = 0; ?? ?Rect.bottom = 100; ?? ?Rect.right = 100; ?? ?InvalidateRect(Rect);


  根據(jù)上面的思路,我們每隔一定時(shí)間繪制一幅圖像,可是如果每次繪制的圖像都是完全相同的,那么圖像看起來也是靜態(tài)的。如何讓曲線動(dòng)起來呢?我們需要為自己繪 圖的代碼設(shè)計(jì)一個(gè)輸入,即在當(dāng)前時(shí)刻曲線上各個(gè)點(diǎn)的坐標(biāo)信息。隨著時(shí)間的推移,令曲線上各個(gè)點(diǎn)的坐標(biāo)隨之變化,這樣每次繪圖都是基于當(dāng)前時(shí)刻的曲線坐標(biāo)繪制的,控制好曲線坐標(biāo)的變化,也就能讓你繪制的曲線乖乖的動(dòng)起來。

  上面提到了曲線上各個(gè)點(diǎn)的坐標(biāo)信息,這個(gè)信息可以用多種數(shù)據(jù)結(jié)構(gòu)儲(chǔ)存,不過筆者推薦使用STL中的deque數(shù)據(jù)結(jié)構(gòu)儲(chǔ)存。為什么呢?需求決定選擇。讓我們先想想在繪制圖像的過程中需要對這個(gè)數(shù)據(jù)進(jìn)行哪些操作。
  1、需要遍歷這個(gè)數(shù)據(jù),獲取各個(gè)點(diǎn)的坐標(biāo)以便繪圖,所以選擇的數(shù)據(jù)結(jié)構(gòu)必須有較高的遍歷效率
  2、當(dāng)曲線上的點(diǎn)橫向上充滿了橫坐標(biāo)軸提供的顯示范圍,需要將曲線最右邊的點(diǎn)的坐標(biāo)移除,然后在曲線最左邊添加下一個(gè)新點(diǎn)的坐標(biāo),以實(shí)現(xiàn)曲線向右平移的效果。所以選擇的數(shù)據(jù)結(jié)構(gòu)需要支持前端和后端元素的添加刪除操作,大家很自然會(huì)想到隊(duì)列。

  STL中的list容器也能很輕松的實(shí)現(xiàn)隊(duì)列功能,但是list還支持任意位置元素的添加和刪除操作,功能上的冗余決定了list需要花費(fèi)更多的時(shí)間來實(shí)現(xiàn)我們的需求,事實(shí)上遍歷一個(gè)deque常常比遍歷一個(gè)list快幾十倍,原因在這里就不贅述啦。
  

  于是,筆者構(gòu)建了這樣的數(shù)據(jù)結(jié)構(gòu)

  deque<pair<TIME, VALUE>> m_dqDisplayData;

  隊(duì)列中的每個(gè)元素是一個(gè)pair,pair中存放坐標(biāo)。維護(hù)這個(gè)數(shù)據(jù)結(jié)構(gòu)的核心代碼如下:

?? ?//如果隊(duì)列長度超過了X軸方向上可繪的所有點(diǎn)的數(shù)量

?? ?if (m_dqDisplayData.size() >= XPointNum) ?? ?{
?? ? ???//將隊(duì)列前端的坐標(biāo)移除 ?? ? ? ?m_dqDisplayData.pop_front();
?? ? ??//在隊(duì)列后端添加新的坐標(biāo) ?? ? ? ?m_dqDisplayData.push_back(make_pair(time, value)); ?? ?} ?? ?else ?? ?{ ?? ? ? ?m_dqDisplayData.push_back(make_pair(tiem, value)); ?? ?}
前面介紹了如何讓靜態(tài)的曲線動(dòng)起來,下面具體介紹繪制靜態(tài)圖像的主要技能:

1、畫圖首先需要找一位畫家,MFC是這樣獲取一位畫家的。
  CDC *pDC = GetDC();
  記得這位畫家畫完本幀圖像之后,打發(fā)他走人,閑人咱們養(yǎng)不起。
  即 必須用ReleaseDC(pDC);釋放資源,否則會(huì)造成內(nèi)存泄漏,因?yàn)镚etDC();函數(shù)中分配了一些資源,這些資源關(guān)聯(lián)在pDC指向的內(nèi)存中,如 果不調(diào)用ReleaseDC,當(dāng)pDC出作用域后,只是pDC這個(gè)32位的指針變量(也可以說它是一個(gè)整數(shù)變量)的內(nèi)存釋放了,pDC指向的內(nèi)存沒有機(jī)會(huì)得到釋放。這里也反映出MFC的一個(gè)原則,Get之后需要Release,這兩個(gè)函數(shù)往往是成對定義好的。

  另外,GetDC和 ReleaseDC都是CWnd的成員函數(shù),我們需要在哪個(gè)窗口上畫圖,就在那個(gè)窗口類的OnPaint或者OnDraw函數(shù)中創(chuàng)建一位會(huì)在該窗口上畫畫 的畫家,其實(shí)GetDC中隱含的操作是,創(chuàng)建一位畫家,將自己所在的窗口的繪圖區(qū)作為畫紙交給這位畫家,然后再把畫家返回給用戶。當(dāng)我們直接建立CDC對 象時(shí)(比如:CDC ?MemDC;),就需要用其他方法(比如:SelectObject函數(shù))為其選擇畫紙了。

2、畫家畫圖之前,首先要準(zhǔn)備好畫圖工具。
     MFC提供了很多畫圖工具,比如畫刷(CBrush),畫筆(CPen)等。(呵呵,其實(shí)筆者也沒用過幾種)
?? ?//下面就實(shí)例化了一個(gè)畫實(shí)線,寬度為1,顏色為RGB(0, 128, 64)的畫筆
?? ?CPen?PenForDrawAxis(PS_SOLID, 1, RGB(0, 128, 64));
?? ?//畫家使用SelectObject技能,將畫筆握入手中 ?? ?pDC->SelectObject(PenForDrawAxis);

  另外說明一點(diǎn):關(guān)于畫筆不再使用后,是否需要調(diào)用PenForDrawAxis.DeleteObject();釋放資源的問題,網(wǎng)上說法不一。各大書籍上,作者們都常常下意識(shí)的顯式地調(diào)用了DeleteObject函數(shù),以體現(xiàn)釋放資源的動(dòng)作

  如果需要及時(shí)釋放內(nèi)存資源,為后面的程序運(yùn)行掃清障礙,那顯式的調(diào)用DeleteObject函數(shù)我覺得沒有問題。但是如果說不調(diào)用DeleteObject函數(shù),CPen對象分配的資源就無法釋放,就會(huì)造成內(nèi)存泄漏,這點(diǎn)我深表懷疑。

  因?yàn)镃Pen對象的資源在構(gòu)造函數(shù)中分配,自然在其析構(gòu)函數(shù)中應(yīng)該有對應(yīng)的釋放函數(shù),因?yàn)樽鳛镸FC用戶來說, 在使用CPen時(shí),根本不知道是否分配了需要顯式釋放的資源。對象應(yīng)該對自己負(fù)責(zé),不應(yīng)該將冗余責(zé)任移交給用戶,這是設(shè)計(jì)C++類的基本原則。通俗的說就 是,自己干了哪些好事自己心理清楚,走人的時(shí)候自己要收拾干凈。微軟在代碼上不會(huì)耍流氓吧(雖然其他地方經(jīng)常流氓)。
  MSDN上的原話 是:When an application no longer requires a given pen, it should call the CGdiObject::DeleteObject member function or destroy the CPen object so the resource is no longer in use. An application should not delete a pen when the pen is selected in a device context.
  要釋放CPen資源,微軟給我們指了兩條明路, 第一是:call the CGdiObject::DeleteObject member function,第二是:destroy the CPen object。何為destroy the CPen object,一種方法就是讓對象出作用域,自動(dòng)調(diào)用析構(gòu)函數(shù)把自己給了結(jié)了。
可見,CPen對象即使不調(diào)用DeleteObject,也能在自己出作用域被C++摧毀時(shí),釋放資源。

  扯遠(yuǎn)啦,扯遠(yuǎn)啦。。。。。。下面繼續(xù)。

3、畫家開始揮筆啦~
?? ?//將筆移動(dòng)到(60,220)這個(gè)坐標(biāo)指示的位置(只是選地方,還沒落筆)
?? ?pDC->MoveTo(60, 220);?
?? ?//將筆在紙上從(60,220)拉到(520,550),一條直線誕生了 ?? ?pDC->LineTo(520, 220);
?? ?//將筆在紙上從(520,220)移動(dòng)到(510,223),另外一條直線躍然紙上 ?? ?pDC->LineTo(510, 223);

  怎么只能畫直線?
  曲線是什么?不過是無數(shù)小段的直線。

  另外,MoveTo和LineTo不必要成對出現(xiàn),一般一條連續(xù)的曲線只需要調(diào)用一次MoveTo。
二、如何使用雙緩沖技術(shù)防止畫面閃爍
  上面介紹了如何繪制動(dòng)態(tài)曲線,但是這樣繪制動(dòng)態(tài)曲線往往會(huì)出現(xiàn)畫面閃爍的問題。
  不管是用什么語言什么構(gòu)架畫圖,出現(xiàn)閃爍的根本原因都在于畫面變化不連貫
  也許你要問,我每次畫的一幀圖像都只是在上幀圖像的基礎(chǔ)上變化了一點(diǎn)點(diǎn),怎么就不連貫了。確實(shí)如此,不過別忘了我們在畫每幀圖像之前,還調(diào)用了 InvalidateRect來清除前一幀圖像,所謂清除,就是用窗口默認(rèn)背景色填充指定矩形區(qū)域,相當(dāng)于在每兩幀圖像之間,實(shí)際還插入了一副大煞風(fēng)景的 純色背景圖。

  終于,大家想到了一種辦法,不使用InvalidateRect來清除前一幀圖像,直接重新請一位會(huì)在內(nèi)存上畫畫的畫家,將該幀圖像畫在內(nèi)存中的一張新的紙上,然后在窗口上畫畫的畫家使用自己的終極技能BOOL?BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop );將在內(nèi)存里面畫畫的老實(shí)畫家手上的畫直接復(fù)制過來(剽竊可恥,但很管用~)。于是,問題解決啦,愛裝B的程序員們給這種方法取了個(gè)很拉風(fēng)的名字 ------ ?雙緩沖技術(shù)。

這個(gè)方法涉及到了以下幾個(gè)主要技能:
1、誰會(huì)在內(nèi)存上畫畫啊?
?? ?//創(chuàng)建一個(gè)會(huì)在內(nèi)存中畫畫的畫家 ?? ?CDC MemDC; ?? ?MemDC.CreateCompatibleDC(NULL);

2、內(nèi)存里面說好給的那種新的紙?jiān)谀陌?#xff1f;
?? ?//創(chuàng)建一個(gè)內(nèi)存中的圖紙 ?? ?CBitmap MemBitmap; ?? ?MemBitmap.CreateCompatibleBitmap(pDC, 800, 600);
  為什么上面要傳入一個(gè)當(dāng)前窗口類中通過GetDC得到的pDC?
  因?yàn)镃reateCompatibleBitmap初始化了一個(gè)與pDC指定的設(shè)備上下文兼容的位圖,位圖與指定的設(shè)備上下文具有相同的顏色位面數(shù)或相同的 每個(gè)像素的位數(shù)。你可以試一試,如果此處傳入&MemDC,完啦完啦,畫家怎么畫,圖上都是灰色的線條,郁悶死啦。至于CreateCompatibleBitmap的后面兩個(gè)參數(shù),指定的是圖紙的大小,具體指你可以根據(jù)自己窗口大小等實(shí)際情況確定,大了無所謂,用不完后面復(fù)制的時(shí)候可以截取指定尺寸。
3、怎么讓畫家在這張紙上畫畫?
?? ?//呵呵,醬紫就搞定啦~ ?? ?MemDC.SelectObject(&MemBitmap);?

4、內(nèi)存中的畫家如何畫畫?
  完全一樣,只不過是MemDC在揮筆。
?? ?MemDC.MoveTo(60, 220);? ?? ?MemDC.LineTo(520, 220); ?? ?MemDC.LineTo(510, 223);
對啦,溫馨提示,大家多半想用一種顏色填充指定矩形區(qū)域,因?yàn)镮nvalidateRect就是干的這事嘛,你把人家擠下來了,自然這事就得自己做啦。
?? ?MemDC.FillSolidRect(0, 0, 580, 250, RGB(1,4,1));
上面這個(gè)函數(shù)表示的是,以圖紙的(0,0)位置(也就是圖紙的最左上角)作為矩形的左上角坐標(biāo),畫一個(gè)顏色為RGB(1,4,1),長為580,寬為250的矩形。尺寸什么的大家不必過于糾結(jié),根據(jù)自己的窗口大小,多試幾種尺寸和坐標(biāo),就能找出最合適的參數(shù)了。
需要注意的是,MemDC是在MemBitmap上畫畫,所以MemDC調(diào)用函數(shù)傳入的坐標(biāo)是MemBitmap這個(gè)圖紙上的坐標(biāo),不是窗口上的坐標(biāo)。

4、如何讓在窗口蹲點(diǎn)的那位畫家直接從內(nèi)存畫家手上復(fù)制圖紙?   //下面函數(shù)的意思是:在MemDC手中的畫紙上,以(0,0)處作為矩形框的左上角坐標(biāo),拉一個(gè)長為580,寬為250的復(fù)制矩形框,這個(gè)框框里面框 中  的圖像復(fù)制到窗口中,復(fù)制矩形框的左上角與窗口的(20,50)處重合。580,250決定了復(fù)制框框的大小,(0,0)決定了復(fù)制框框在 MemBitmap上的位置,(20,50)決定了復(fù)制框框在窗口上的位置。 ????pDC->BitBlt(20, 50, 580, 250, &MemDC, 0, 0, SRCCOPY);?

下面是部分畫圖代碼,刪除了很多周邊功能,如果不小心多刪到了什么還請大家海涵,主要留著看個(gè)思路和框架。

?

1、畫坐標(biāo)軸的函數(shù),你們看,我就是讓 ”內(nèi)存畫家“ --- MemDC 畫畫的,表示用了雙緩沖的哦,呵呵。void CXXXDlg::DrawAxis(CDC &MemDC, LPTSTR TitleForX, LPTSTR TitleForY) {//選擇畫坐標(biāo)軸的畫筆CPen PenForDrawAxis(PS_SOLID, 1, RGB(0, 128, 64));MemDC.SelectObject(PenForDrawAxis);//繪制X軸MemDC.MoveTo(60, 220);MemDC.LineTo(520, 220);//繪制箭頭MemDC.LineTo(510, 223);MemDC.LineTo(510, 217);MemDC.LineTo(520, 220);//繪制Y軸MemDC.MoveTo(60, 220);MemDC.LineTo(60, 30);//繪制箭頭MemDC.LineTo(57, 40);MemDC.LineTo(63, 40);MemDC.LineTo(60, 30);//設(shè)置文本的顏色COLORREF OldColor = MemDC.SetTextColor(RGB(255, 255, 0));//繪制標(biāo)注MemDC.TextOut(480, 230, TitleForX);MemDC.TextOut(40, 10, TitleForY);//還原文本顏色 MemDC.SetTextColor(OldColor); }2、畫圖例的函數(shù) void CXXXDlg::DrawLegend(CDC &MemDC, CPen &PenForDraw, CPen &PenForDrawAB, CPen &PenForDrawBE) {//設(shè)置文本的顏色COLORREF OldColor = MemDC.SetTextColor(RGB(0, 128, 64));//繪制圖例 MemDC.SelectObject(PenForDraw);MemDC.TextOut(530, 30, _T("Global"));MemDC.MoveTo(530, 50);MemDC.LineTo(570, 50);MemDC.SelectObject(PenForDrawAB);MemDC.TextOut(530, 70, _T("AB"));MemDC.MoveTo(530, 90);MemDC.LineTo(570, 90);MemDC.SelectObject(PenForDrawBE);MemDC.TextOut(530, 110, _T("BE"));MemDC.MoveTo(530, 130);MemDC.LineTo(570, 130);//還原文本顏色 MemDC.SetTextColor(OldColor); }3、畫曲線的函數(shù) void CXXXDlg::DrawDynamicCurve(CDC &MemDC, CPen &Pen, deque<pair<TIME, VALUE>> &DisplayData, double Proportion) {//選擇畫筆 MemDC.SelectObject(Pen);//進(jìn)入臨界區(qū)EnterCriticalSection(&(m_cControllingParameters.m_cCriticalSection));//繪制曲線if (DisplayData.size() >= 2){COORDINATE XPos = 60;for (UINT PointIndex = 1; PointIndex != DisplayData.size(); PointIndex++){MemDC.MoveTo(XPos++, 220 - (COORDINATE)((double)(DisplayData[PointIndex - 1].second) / Proportion));MemDC.LineTo(XPos, 220 - (COORDINATE)((double)(DisplayData[PointIndex].second) / Proportion));}}//離開臨界區(qū)LeaveCriticalSection(&(m_cControllingParameters.m_cCriticalSection));//還原文本顏色 MemDC.SetTextColor(OldColor); }4、重點(diǎn)來啦,Onpaint函數(shù),有雙緩沖技術(shù)的主流程 void CXXXDlg::OnPaint() { CDC *pDC = GetDC();//創(chuàng)建一個(gè)內(nèi)存中的顯示設(shè)備 CDC MemDC;MemDC.CreateCompatibleDC(NULL); //創(chuàng)建一個(gè)內(nèi)存中的圖像 CBitmap MemBitmap;MemBitmap.CreateCompatibleBitmap(pDC, 580, 250);//定義各種類型的畫筆CPen PenForDraw(PS_SOLID, 1, RGB(0, 232, 255));CPen PenForDrawAB(PS_SOLID, 1, RGB(0, 98, 0));CPen PenForDrawBE(PS_SOLID, 1, RGB(221, 0, 221));//指定內(nèi)存顯示設(shè)備在內(nèi)存中的圖像上畫圖MemDC.SelectObject(&MemBitmap); //先用一種顏色作為內(nèi)存顯示設(shè)備的背景色MemDC.FillSolidRect(0, 0, 580, 250, RGB(1,4,1));//繪制坐標(biāo)軸DrawAxis(MemDC, _T("time(s)"), _T("length(kbit)"));//繪制圖例 DrawLegend(MemDC, PenForDraw, PenForDrawAB, PenForDrawBE);//繪制曲線DrawDynamicCurve(MemDC, PenForDraw, m_dqDisplayData, 1000);//將內(nèi)存中畫好的圖像直接拷貝到屏幕指定區(qū)域上pDC->BitBlt(20, 50, 580, 250, &MemDC, 0, 0, SRCCOPY); //釋放相關(guān)資源 ReleaseDC(pDC); }

?

沒有整理與歸納的知識(shí),一文不值!高度概括與梳理的知識(shí),才是自己真正的知識(shí)與技能。 永遠(yuǎn)不要讓自己的自由、好奇、充滿創(chuàng)造力的想法被現(xiàn)實(shí)的框架所束縛,讓創(chuàng)造力自由成長吧! 多花時(shí)間,關(guān)心他(她)人,正如別人所關(guān)心你的。理想的騰飛與實(shí)現(xiàn),沒有別人的支持與幫助,是萬萬不能的。





本文轉(zhuǎn)自wenglabs博客園博客,原文鏈接:http://www.cnblogs.com/arxive/p/4911098.html,如需轉(zhuǎn)載請自行聯(lián)系原作者

總結(jié)

以上是生活随笔為你收集整理的[转载] MFC绘制动态曲线,用双缓冲绘图技术防闪烁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。