最近在写一个屏幕下雪的VC程序, 取了一个VC#的类似程序做示范.
VC#的程序中原作者使用了窗体自带的DoubleBuffer属性,但是C++中只有自己实现...
原理大概就是做一个全屏的窗口,设置背景色为透明色,之后调用GDI+绘图,这样就可以在不妨碍用户操作电脑的同时实现下雪效果.
但是现在问题是我在OnPaint()中绘制雪花后窗口闪烁严重,但如果重载OnEraseBkgnd()不调用CDialog::OnEraseBkgnd()就会出现屏幕不刷新的现象,而我又不能用画刷直接刷窗口(窗口是透明的),导致无法使用双缓冲绘图.
所以问题就是要使系统的OnEraseBkgnd在我的MemDC上作图,而我创建的MemDC甚至使我使用它初始化的Graphics类的作图也失效了...
我调试了一下,发现MFC会把我调用CDialog::OnEraseBkgnd()的参数直接替换成消息队列的信息,于是我直接调用了CWnd::DefWndProc,但还是不起作用,雪花也上不去...
求各位帮忙...
VC#的程序中原作者使用了窗体自带的DoubleBuffer属性,但是C++中只有自己实现...
原理大概就是做一个全屏的窗口,设置背景色为透明色,之后调用GDI+绘图,这样就可以在不妨碍用户操作电脑的同时实现下雪效果.
但是现在问题是我在OnPaint()中绘制雪花后窗口闪烁严重,但如果重载OnEraseBkgnd()不调用CDialog::OnEraseBkgnd()就会出现屏幕不刷新的现象,而我又不能用画刷直接刷窗口(窗口是透明的),导致无法使用双缓冲绘图.
所以问题就是要使系统的OnEraseBkgnd在我的MemDC上作图,而我创建的MemDC甚至使我使用它初始化的Graphics类的作图也失效了...
我调试了一下,发现MFC会把我调用CDialog::OnEraseBkgnd()的参数直接替换成消息队列的信息,于是我直接调用了CWnd::DefWndProc,但还是不起作用,雪花也上不去...
求各位帮忙...
void CSnowDlg::OnPaint()
{
CRect rcWindow;
GetWindowRect(rcWindow);
CDC dcMemory;
dcMemory.CreateCompatibleDC(GetDC());
CBitmap bmpMemory;
bmpMemory.CreateCompatibleBitmap(GetDC(), rcWindow.Width(), rcWindow.Height());
dcMemory.SelectObject(&bmpMemory);
CWnd::DefWindowProc(WM_ERASEBKGND, (WPARAM) dcMemory.m_hDC, 0);
using namespace Gdiplus;
Graphics gMemory(dcMemory.m_hDC);
gMemory.SetSmoothingMode(SmoothingModeAntiAlias);
EnterCriticalSection(&g_cs);
for (std::list<SnowFlake>::iterator iter = g_lSnowFlakes.begin(); iter != g_lSnowFlakes.end(); ++iter) {
gMemory.ResetTransform();
gMemory.TranslateTransform(-16, -16, MatrixOrderAppend); //平移
gMemory.ScaleTransform(iter->Scale, iter->Scale, MatrixOrderAppend); //缩放
gMemory.RotateTransform(iter->Rotation, MatrixOrderAppend); //旋转
gMemory.TranslateTransform(iter->X, iter->Y, MatrixOrderAppend); //平移
gMemory.DrawImage(m_bmpSnowFlake, 0, 0); //绘制
}
LeaveCriticalSection(&g_cs);
GetDC()->BitBlt(rcWindow.left, rcWindow.top, rcWindow.Width(), rcWindow.Height(), &dcMemory, rcWindow.left, rcWindow.top,SRCCOPY);
bmpMemory.DeleteObject();
dcMemory.DeleteDC();
}
UINT SnowFlakesWorker(LPVOID pParam)
{
unsigned long Tick = 0;
SnowFlake s; while(true) {
if (g_bExitWorker) {
return 0;
}
Sleep(20);
++Tick;
//Create new snowflakes
//if (Tick % 3 == 0 && Random() < 0.7)
if (Tick % 10 == 0 && Random() < 0.7) {
s.X = Random(-50, GetSystemMetrics(SM_CXSCREEN) + 50);
s.Y = Random(-20, -7);
s.XVelocity = (Random() - 0.5) * 2;
s.YVelocity = (Random() * 3) + 1;
s.Rotation = Random(0, 359);
s.RotationVelocity = Random(-3, 3) * 2;
if (s.RotationVelocity == 0) {
s.RotationVelocity = 3;
}
s.Scale = Random() / 2 + 0.75;
EnterCriticalSection(&g_cs);
g_lSnowFlakes.push_back(s);
LeaveCriticalSection(&g_cs);
}
//Delete snowflakes
EnterCriticalSection(&g_cs);
for (std::list<SnowFlake>::iterator iter = g_lSnowFlakes.begin(); iter != g_lSnowFlakes.end(); ++iter) {
iter->X += iter->XVelocity;
iter->Y += iter->YVelocity;
iter->Rotation += iter->RotationVelocity;
iter->XVelocity += (Random() - 0.5) * 0.7;
iter->XVelocity = MAX(iter->XVelocity, -2);
iter->XVelocity = MIN(iter->XVelocity, +2);
if (iter->YVelocity > GetSystemMetrics(SM_CYSCREEN) + 10) {
iter = g_lSnowFlakes.erase(iter);
--iter;
}
}
LeaveCriticalSection(&g_cs);
//Refresh
((CSnowDlg *) pParam)->Invalidate();
}
}
CBitmap* pOldBmp = dcMemory.SelectObject(&bmpMemory);
....
dcMemory.SelectObject(pOldBmp);
另外在OnPaint中最好用CPaintDC
比如
void CSnowDlg::OnPaint()
{
CPaintDC dc; CRect rcWindow;
GetWindowRect(rcWindow);
CDC dcMemory;
dcMemory.CreateCompatibleDC(&dc);
请问最后为何要重新调用SelectObject?是被替换的CBitmap导致的内存泄露吗?另外代码已修正为如下,但内存占用还是稳定且缓慢增长...void CSnowDlg::OnPaint()
{
CRect rcWindow;
CDC *pdcWindow, dcMemory;
CBitmap bmpMemory;
GetWindowRect(rcWindow);
pdcWindow = GetDC();
dcMemory.CreateCompatibleDC(pdcWindow);
bmpMemory.CreateCompatibleBitmap(pdcWindow, rcWindow.Width(), rcWindow.Height());
dcMemory.SelectObject(&bmpMemory);
CWnd::DefWindowProc(WM_ERASEBKGND, (WPARAM) dcMemory.m_hDC, 0);
using namespace Gdiplus;
Graphics gMemory(dcMemory.m_hDC);
gMemory.SetSmoothingMode(SmoothingModeAntiAlias);
EnterCriticalSection(&g_cs);
for (std::list<SnowFlake>::iterator iter = g_lSnowFlakes.begin(); iter != g_lSnowFlakes.end(); ++iter) {
gMemory.ResetTransform();
gMemory.TranslateTransform(-16, -16, MatrixOrderAppend); //平移
gMemory.ScaleTransform(iter->Scale, iter->Scale, MatrixOrderAppend); //缩放
gMemory.RotateTransform(iter->Rotation, MatrixOrderAppend); //旋转
gMemory.TranslateTransform(iter->X, iter->Y, MatrixOrderAppend); //平移
gMemory.DrawImage(m_bmpSnowFlake, 0, 0); //绘制
}
LeaveCriticalSection(&g_cs);
pdcWindow->BitBlt(rcWindow.left, rcWindow.top, rcWindow.Width(), rcWindow.Height(), &dcMemory, rcWindow.left, rcWindow.top,SRCCOPY);
ReleaseDC(pdcWindow);
bmpMemory.DeleteObject();
dcMemory.DeleteDC();
}