JPG的显示造成程序耗费内存越来越多
程序功能:对某些参数进行计算,并生成一列JPG图,如1.jpg,2.jpg,3.jpg。然后利用GDI+,将1.jpg读入进来并显示在视图上,点击下一张按钮,依次读入后面的JPG图。问题:随着JPG图片的不断载入,程序所消耗的内存会逐步递增。
首先描述一下我这个程序的设计思路:这个程序通过计算,首先生成一列JPG图片并保存在某个硬盘路径中,然后程序会读取第一个JPG文件并将它显示在视图里,工具栏有个按钮,点击一下,程序会读取下一个JPG文件,显示在视图中。每次需要显示一个JPG时,会调用名为DrawPicture的函数,这是一个文档类的函数,它通过向视图发送消息,传递JPG图片的路径,并响应视图类的OnUpdate函数,JPG的载入和绘图都是在OnUpdate函数中具体执行的,由于涉及到图片的放大缩小和漫游,因此我用的是双缓冲方法来显示图片。
相关代码如下:
首先在试图的OnInitialUpdate中初始化
void CMy2008BAC35B02View::OnInitialUpdate()
{
// CScrollView::OnInitialUpdate();
// 内存画布的初始化设置
m_memDC.CreateCompatibleDC(NULL); // 准备一个内存DC
CDC * pDC = GetDC();
m_memBitmap.CreateCompatibleBitmap(pDC, 5000, 4000); // 准备一个5000*4000的位图画布
ReleaseDC(pDC);
// 滚动条初始化设置
SetScrollSizes();
}
然后自定义一个消息函数,用来接收菜单传入的图片的地址
// 自定义消息函数,当文档类向本视图发送WM_NOTIFY_DRAW消息时,执行该函数,接收来自对话框的
// 图件完整路径信息,并绘图
LRESULT CMy2008BAC35B02View::OnNotifyDraw(WPARAM wParam, LPARAM lParam)
{
CString *p_strGetPicPathName;
// 将lParam参数转换为指向CString的指针,这个指针指向的CString对象携带了图件完整路径信息
p_strGetPicPathName = (CString*)lParam;
m_strPicPathName = *p_strGetPicPathName; // 完整路径赋值给View的成员变量
CMy2008BAC35B02Doc *pDoc = GetDocument();
pDoc->UpdateAllViews(NULL); // 更新视图,调用视图类的OnUpdate函数
SetScrollSizes(); // 设置滚动条
return 1;
}
其中定义了成员变量
CString p_strGetPicPathName;
然后再定义几个成员变量
Image *image;
IStream *pStream;
HGLOBAL hMem;
并在视图类的构造函数中初始化为NULL。
在OnUpdate中执行具体的绘图
void CMy2008BAC35B02View::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
CFile file;
if (!file.Open(m_strPicPathName, CFile::modeRead))
{
MessageBox("无法打开结果图件。", "错误", MB_OK|MB_ICONSTOP);
return;
}
DWORD m_nFileLen;
m_nFileLen = file.GetLength();
// 每执行一次OnUpdate,都先清空内存
if (!hMem)
{
::GlobalFree(hMem);
hMem = NULL;
}
// 动态分配内存
// 分配的是固定内存,不是可移动的(GMEM_MOVEABLE),故不必锁定内存
hMem = ::GlobalAlloc(GMEM_FIXED, m_nFileLen);
if (hMem==NULL)
{
MessageBox("内存分配失败。", "错误", MB_OK|MB_ICONSTOP);
return;
}
if (file.Read(hMem, m_nFileLen)!=m_nFileLen)
{
MessageBox("读结果图件失败", "错误", MB_OK|MB_ICONSTOP);
::GlobalFree(hMem);
return;
}
file.Close();
pStream = NULL;
if (CreateStreamOnHGlobal(hMem, FALSE, &pStream)!=S_OK)
{
MessageBox("创建IStream对象失败。", "错误", MB_OK|MB_ICONSTOP);
::GlobalFree(hMem);
return;
}
if (!image)
{
delete image;
image = NULL;
}
image = Image::FromStream(pStream);
pStream->Release();
// 得到图像的宽和高
m_nPicWidth = image->GetWidth();
m_nPicHeight = image->GetHeight();
// 得到当前视图的宽和高
CRect rcClient;
GetClientRect(&rcClient);
m_nClientWidth = rcClient.Width();
m_nClientHeight = rcClient.Height();
// 得到图像缩放后的宽和高
m_nPicZoomedWidth = (int)(m_nPicWidth*m_nScale);
m_nPicZoomedHeight = (int)(m_nPicHeight*m_nScale);
// 求视图中显示图片的原点
m_nClientOriginX = (m_nClientWidth-m_nPicZoomedWidth)/2;
m_nClientOriginY = (m_nClientHeight-m_nPicZoomedHeight)/2;
if (m_nClientOriginX<0)
m_nClientOriginX = 0;
if (m_nClientOriginY<0)
m_nClientOriginY = 0;
// 在内存画布上显示图形
// 将准备的位图画布选入内存DC中成为内存画布
m_pOldBitmap = m_memDC.SelectObject(&m_memBitmap);
m_memDC.FillSolidRect(0, 0, 5000, 4000, RGB(255,255,255)); // 用白色填充整个内存画布
Graphics graphics(m_memDC.GetSafeHdc()); // 内存DC和GDI+的Graphics对象关联
// 设置插值模式改善图像缩放质量
graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);
Rect destinationRc(0, 0, m_nPicZoomedWidth, m_nPicZoomedHeight);
// 在内存画布上显示图像
graphics.DrawImage(image, destinationRc, 0, 0, m_nPicWidth, m_nPicHeight, UnitPixel);
m_memDC.SelectObject(m_pOldBitmap);
Invalidate(); // 提示OnDraw重绘
}
最后在OnDraw中显示图像
void CMy2008BAC35B02View::OnDraw(CDC* pDC)
{
CMy2008BAC35B02Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
// 直接绘制内存画布上的位图(双缓冲)
pDC->BitBlt(m_nClientOriginX, m_nClientOriginY, m_nPicZoomedWidth, m_nPicZoomedHeight,
&m_memDC, 0, 0, SRCCOPY);
}
如果按照上面的代码,分配的内存应该都是及时清空了的,因为每一次显示JPG时,都会先清空上一次分配的内存。但实际情况是随着JPG的不断显示,程序耗费的内存越来越多。不过,如果程序最小化了,貌似内存又被瞬间清空了,不知是怎么回事?