转自:http://topic.csdn.net/t/20030601/16/1862242.html
1 引言
在最近的编程实践中发现,虽然TeeChart ActiveX Pro5.0控件能够很方便的实现画图线等操作【1】,但是对于大数据量的文件读取和显示,例如一个64M数据文件的显示,速度将非常的慢。本文描述了一种采用CDib类【2】作为“画布”,将所有的画图操作在此CDib对象中完成后,再映射到TeeChart ActiveX Pro5.0控件的Canvas上完成画图操作。对比试验证明,此方法可使画图操作获得上百倍速度的提高。(CDib类是David Kruglinski在《Inside Visual C++》一书中提供的。它是免费使用和发布的,在使用时,应该提到作者以示尊重。)
2 实现步骤
2.1 在CFormView中声明私有对象m_Dib
使用MFC AppWizard 创建一个新的SDI程序CMyApp,选择View的类型为CFormView。在CFormView中插入TeeChart ActiveX Pro 5.0控件,并在CFormView类中声明私有变量m_Dib。
//CMyView.h
private:
CDib m_Dib;
2.2 在CMyDoc中声明并使用内存映射文件
在CMyDoc中声明内存映射文件
//CMyDoc.h
public:
HANDLE m_hMapFile; //内存映射文件的HANDLE
LPVOID m_lpMapFile; //指向内存映射文件数据的指针,可用于
//访问文件
并在CMyDoc的Serialize函数中加入以下代码:
//以下代码实现用户打开数据文件时,为所需打开的文件进行内存映射;
void CMyDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
}
else
{
// TODO: add loading code here
//首先检查m_lpMapFile和m_hMapFile是否为空,如果是,首先取消原有
//的映射;
if (m_lpMapFile!=NULL) {
UnmapViewOfFile(m_lpMapFile);
}
if (m_hMapFile!=NULL) {
CloseHandle(m_hMapFile);
}
//创建内存映射文件HANDLE;
m_hMapFile=CreateFileMapping((HANDLE)ar.GetFile()->m_hFile,
NULL,
PAGE_READONLY,
0,
ar.GetFile()->GetLength(),
"Test");
//创建内存映射文件的数据指针,可用于访问文件内的数据;
m_lpMapFile=MapViewOfFile(m_hMapFile,
FILE_MAP_READ,
0,
0,
ar.GetFile()->GetLength());
}
}
2.3 程序关闭或者文件关闭时,在CMyDoc的析构函数中,确保取消数据文件的内存映射;
CMyDoc::~ CMyDoc ()
{
//关闭内存映射文件的HANDLE
if (m_hMapFile!=NULL) {
CloseHandle(m_hMapFile);
}
//取消对内存映射文件视图的映射;
if (m_lpMapFile!=NULL) {
UnmapViewOfFile(m_lpMapFile);
}
}
2.4 加上菜单Test,在它的事件响应函数中加入以下对CDib画图的代码
void CMyView::OnTest()
{
// TODO: Add your command handler code here
//获取文件指针pDoc;
CMyDoc* pDoc=GetDocument();
//利用pDoc->m_lpMapFile映射文件视图数据指针,访问数据文件;
//m_lpMapFile的类型是LPVOID,需要对它进行类型强制转换为所需类型
int* pMapFile=(int*)pDoc->m_lpMapFile;
//以下是特定的数据文件访问代码,用户可根据自己需要进行访问;
int nChlNum=*( pMapFile);
int nLayerCount=(nChlNum-6);
int nChlIndex=23;
int nChlLen=*( pMapFile +1+nChlIndex);
int nFrmNums=*( pMapFile +1+nChlNum);
int nHeadOffset=(nChlNum+2)*sizeof(int);
int nFrmCount=0;
for(int i=0;i<nChlNum;i++)
nFrmCount=nFrmCount+*((int*)pDoc->m_lpMapFile+i+1);
int nFrmOffset=0;
for(i=0;i<nChlIndex;i++)
nFrmOffset=nFrmOffset+*((int*)pDoc->m_lpMapFile+i+1);
//Create函数是我参考构造函数m_Dib::m_Dib(CSize size,int nBitCount)修改
//得来的,请参考demo项目;它可以方便清空m_Dib内存,重新设置m_Dib
//的大小
m_Dib.Create(CSize(nChlLen,nFrmNums),24);
//引用Chart控件的Canvas,
CDC DC;
GETCHART(pChart)
DC.Attach((HDC)pChart->GetCanvas().GetHandleDC());
//把m_Dib与DC关联起来,并为m_Dib分配内存;
m_Dib.CreateSection(&DC);
//CDib的方便之处在于可以使用它的m_lpImage指针访问图中的每个点,由于
//它本身也采用了Windows的画图优化算法,所以速度更快;
BYTE* pDibByte=m_Dib.m_lpImage;
//这个nOffset是m_Dib行与行之间m_lpImage必须加上的偏移量,否则访问的
//位置不对
int nOffset=nChlLen%4;
BYTE val;
for (i=0; i<nFrmNums; i++) //每一行
{
for (int j=0; j<nChlLen; j++) //每一列
{
val=*((BYTE*)pDoc->m_lpMapFile + nHeadOffset + nFrmOffset+ i*nFrmCount + j);
*(pDibByte++)=val; //蓝色值Blue;
*(pDibByte++)=val; //绿色值Green
*(pDibByte++)=val; //红色值Red
}
pDibByte+=Offset; //行与行之间必须加上的偏移量;
}
//在使用完DC后,必须解除引用;
DC.Detach();
}
2.5 在TChart控件的OnBeforeDrawSeriesChart()的事件响应函数中将m_Dib画到Chart的Canvas上
void CMyView::OnOnBeforeDrawSeriesChart()
{
// TODO: Add your control notification handler code here
CDC DC;
CTChart* pChart=(CTChart*)GetDlgItem(IDC_CHART);
//引用Chart控件的Canvas;
DC.Attach((HDC)pChart->GetCanvas().GetHandleDC());
//获取Chart控件的左上角位置;
m_ptOrigin=CPoint(pChart->GetAxis().GetBottom().CalcXPosValue(pChart->GetSeries(0).GetXValues().GetMinimum()),pChart->GetAxis().GetLeft().CalcYPosValue(pChart->GetSeries(0).GetYValues().GetMinimum()));
//获取Chart控件的左右、上下坐标轴所围成的矩形的大小;
m_szDraw=CSize(pChart->GetAxis().GetBottom().CalcXPosValue(pChart->GetSeries(0).GetXValues().GetMaximum())-m_ptOrigin.x,pChart->GetAxis().GetLeft().CalcYPosValue(pChart->GetSeries(0).GetYValues().GetMaximum())-m_ptOrigin.y);
//将m_Dib画到Chart的Canvas上;
m_Dib.Draw(&DC,m_ptOrigin,m_szDraw);
//解除Chart的Canvas的引用;
DC.Detach();
}
3 小结
3.1 内存映射文件的使用,方便了数据文件的读取并加快了读取的速度;
//创建内存映射文件HANDLE;
m_hMapFile=CreateFileMapping((HANDLE)ar.GetFile()->m_hFile,
NULL,
PAGE_READONLY,
0,
ar.GetFile()->GetLength(),
"Test");
//创建内存映射文件的数据指针,可用于访问文件内的数据;
m_lpMapFile=MapViewOfFile(m_hMapFile,
FILE_MAP_READ,
0,
0,
ar.GetFile()->GetLength());
//关闭内存映射文件的HANDLE
if (m_hMapFile!=NULL) {
CloseHandle(m_hMapFile);
}
//取消对内存映射文件视图的映射;
if (m_lpMapFile!=NULL) {
UnmapViewOfFile(m_lpMapFile);
}
3.2 CDib类的使用,很容易的实现了内存DC,加快了画图的速度;
//首先设置CDib的大小;
m_Dib.Create(CSize(nChlLen,nFrmNums),24);
//引用Chart控件的Canvas
CDC DC;
GETCHART(pChart)
DC.Attach((HDC)pChart->GetCanvas().GetHandleDC());
//把m_Dib与DC关联起来,并为m_Dib分配内存;
m_Dib.CreateSection(&DC);
//进行画图操作
……
//将m_Dib画到Chart控件的Canvas上;
m_Dib.Draw(&DC,ptOrigin,sizeDraw);
//记得使用完后要取消DC的引用;
DC.Detach();
3.3 以下是采用以上所述技术所实现的画图功能
参考文献:
[1] www.teechart.com
[2] 《Inside Visual C++》, Fifth Edition, Chapter Eleven Bitmaps, David Kruglinski