CPen PenForDrawAxis(PS_SOLID, 1, RGB(0, 128, 64));
//画家使用SelectObject技能,将画笔握入手中
pDC->SelectObject(PenForDrawAxis);
另外说明一点:关于画笔不再使用后,是否需要调用PenForDrawAxis.DeleteObject();释放资源的问题,网上说法不一。各大书籍上,作者们都常常下意识的显式地调用了DeleteObject函数,以体现释放资源的动作。
如果需要及时释放内存资源,为后面的程序运行扫清障碍,那显式的调用DeleteObject函数我觉得没有问题。但是如果说不调用DeleteObject函数,CPen对象分配的资源就无法释放,就会造成内存泄漏,这点我深表怀疑。
因为CPen对象的资源在构造函数中分配,自然在其析构函数中应该有对应的释放函数,因为作为MFC用户来说, 在使用CPen时,根本不知道是否分配了需要显式释放的资源。对象应该对自己负责,不应该将冗余责任移交给用户,这是设计C++类的基本原则。通俗的说就是,自己干了哪些好事自己心理清楚,走人的时候自己要收拾干净。微软在代码上不会耍流氓吧(虽然其他地方经常流氓)。
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,一种方法就是让对象出作用域,自动调用析构函数把自己给了结了。
可见,CPen对象即使不调用DeleteObject,也能在自己出作用域被C++摧毁时,释放资源。
扯远啦,扯远啦。。。。。。下面继续。
3、画家开始挥笔啦~
//将笔移动到(60,220)这个坐标指示的位置(只是选地方,还没落笔)
pDC->MoveTo(60, 220);
//将笔在纸上从(60,220)拉到(520,550),一条直线诞生了
pDC->LineTo(520, 220);
//将笔在纸上从(520,220)移动到(510,223),另外一条直线跃然纸上
pDC->LineTo(510, 223);
怎么只能画直线?
曲线是什么?不过是无数小段的直线。
另外,MoveTo和LineTo不必要成对出现,一般一条连续的曲线只需要调用一次MoveTo。
二、如何使用双缓冲技术防止画面闪烁
上面介绍了如何绘制动态曲线,但是这样绘制动态曲线往往会出现画面闪烁的问题。
不管是用什么语言什么构架画图,出现闪烁的根本原因都在于画面变化不连贯。
也许你要问,我每次画的一帧图像都只是在上帧图像的基础上变化了一点点,怎么就不连贯了。确实如此,不过别忘了我们在画每帧图像之前,还调用了InvalidateRect来清除前一帧图像,所谓清除,就是用窗口默认背景色填充指定矩形区域,相当于在每两帧图像之间,实际还插入了一副大煞风景的纯色背景图。
终于,大家想到了一种办法,不使用InvalidateRect来清除前一帧图像,直接重新请一位会在内存上画画的画家,将该帧图像画在内存中的一张新的纸上,然后在窗口上画画的画家使用自己的终极技能BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop );将在内存里面画画的老实画家手上的画直接复制过来(剽窃可耻,但很管用~)。于是,问题解决啦,爱装B的程序员们给这种方法取了个很拉风的名字 ------ 双缓冲技术。
这个方法涉及到了以下几个主要技能:
1、谁会在内存上画画啊?
MemDC.CreateCompatibleDC(NULL);
2、内存里面说好给的那种新的纸在哪啊?
MemBitmap.CreateCompatibleBitmap(pDC, 800, 600);
为什么上面要传入一个当前窗口类中通过GetDC得到的pDC?
因为CreateCompatibleBitmap初始化了一个与pDC指定的设备上下文兼容的位图,位图与指定的设备上下文具有相同的颜色位面数或相同的每个像素的位数。你可以试一试,如果此处传入&MemDC,完啦完啦,画家怎么画,图上都是灰色的线条,郁闷死啦。
至于CreateCompatibleBitmap的后面两个参数,指定的是图纸的大小,具体指你可以根据自己窗口大小等实际情况确定,大了无所谓,用不完后面复制的时候可以截取指定尺寸。
3、怎么让画家在这张纸上画画?
MemDC.SelectObject(&MemBitmap);
4、内存中的画家如何画画?
完全一样,只不过是MemDC在挥笔。
MemDC.LineTo(510, 223);
对啦,温馨提示,大家多半想用一种颜色填充指定矩形区域,因为InvalidateRect就是干的这事嘛,你把人家挤下来了,自然这事就得自己做啦。
MemDC.FillSolidRect(0, 0, 580, 250, RGB(1,4,1));
上面这个函数表示的是,以图纸的(0,0)位置(也就是图纸的最左上角)作为矩形的左上角坐标,画一个颜色为RGB(1,4,1),长为580,宽为250的矩形。尺寸什么的大家不必过于纠结,根据自己的窗口大小,多试几种尺寸和坐标,就能找出最合适的参数了。
需要注意的是,MemDC是在MemBitmap上画画,所以MemDC调用函数传入的坐标是MemBitmap这个图纸上的坐标,不是窗口上的坐标。
4、如何让在窗口蹲点的那位画家直接从内存画家手上复制图纸?
//下面函数的意思是:在MemDC手中的画纸上,以(0,0)处作为矩形框的左上角坐标,拉一个长为580,宽为250的复制矩形框,这个框框里面框中的图像复制到窗口中,复制矩形框的左上角与窗口的(20,50)处重合。580,250决定了复制框框的大小,(0,0)决定了复制框框在MemBitmap上的位置,(20,50)决定了复制框框在窗口上的位置。
pDC->BitBlt(20, 50, 580, 250, &MemDC, 0, 0, SRCCOPY);
下面是部分画图代码,删除了很多周边功能,如果不小心多删到了什么还请大家海涵,主要留着看个思路和框架。
1、画坐标轴的函数,你们看,我就是让 ”内存画家“ --- MemDC 画画的,表示用了双缓冲的哦,呵呵。
void CXXXDlg::DrawAxis(CDC &MemDC, LPTSTR TitleForX, LPTSTR TitleForY)
CPen PenForDrawAxis(PS_SOLID, 1, RGB(0, 128, 64));
MemDC.SelectObject(PenForDrawAxis);
COLORREF OldColor = MemDC.SetTextColor(RGB(255, 255, 0));
MemDC.TextOut(480, 230, TitleForX);
MemDC.TextOut(40, 10, TitleForY);
MemDC.SetTextColor(OldColor);
2、画图例的函数
void CXXXDlg::DrawLegend(CDC &MemDC, CPen &PenForDraw, CPen &PenForDrawAB, CPen &PenForDrawBE)
COLORREF OldColor = MemDC.SetTextColor(RGB(0, 128, 64));
MemDC.SelectObject(PenForDraw);
MemDC.TextOut(530, 30, _T("Global"));
MemDC.SelectObject(PenForDrawAB);
MemDC.TextOut(530, 70, _T("AB"));
MemDC.SelectObject(PenForDrawBE);
MemDC.TextOut(530, 110, _T("BE"));
MemDC.SetTextColor(OldColor);
}
3、画曲线的函数
void CXXXDlg::DrawDynamicCurve(CDC &MemDC, CPen &Pen, deque<pair<TIME, VALUE>> &DisplayData, double Proportion)
EnterCriticalSection(&(m_cControllingParameters.m_cCriticalSection));
if (DisplayData.size() >= 2)
for (UINT PointIndex = 1; PointIndex != DisplayData.size();
MemDC.MoveTo(XPos++, 220 - (COORDINATE)((double)(DisplayData[PointIndex - 1].second) / Proportion));
MemDC.LineTo(XPos, 220 - (COORDINATE)((double)(DisplayData[PointIndex].second) / Proportion));
LeaveCriticalSection(&(m_cControllingParameters.m_cCriticalSection));
MemDC.SetTextColor(OldColor);
}
4、重点来啦,Onpaint函数,有双缓冲技术的主流程
MemDC.CreateCompatibleDC(NULL);
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));
MemDC.SelectObject(&MemBitmap);
//先用一种颜色作为内存显示设备的背景色
MemDC.FillSolidRect(0, 0, 580, 250, RGB(1,4,1));
DrawAxis(MemDC, _T("time(s)"), _T("length(kbit)"));
DrawLegend(MemDC, PenForDraw, PenForDrawAB, PenForDrawBE);
DrawDynamicCurve(MemDC, PenForDraw, m_dqDisplayData, 1000);
pDC->BitBlt(20, 50, 580, 250, &MemDC, 0, 0, SRCCOPY);