原文地址: https://blog.csdn.net/smallerxuan/article/details/79395993
在窗口DC上进行绘图,简单直接的做法就是直接调用GDI绘图函数进行绘图操作。当需要绘制新的图形时,就利用画刷刷去旧有的图形,然后再次调用GDI绘图。这种简单、朴素的绘图方法在简单场景下能够胜任简单的绘图任务,但当需要绘制复杂图形例如位图或者绘图量操作很大的时候就不能够胜任了,会产生闪屏现象。所以就有了双缓冲绘图,双缓冲绘图指的是:不简单的使用画刷去刷新DC窗口,而是预先创建一个起缓冲作用的内存DC,现将需要绘制的内容绘制到这个缓冲DC中去,然后使用较画刷绘制更快的图形块绘制函数BitBlt快速的将缓冲DC中的数据拷贝到窗口DC中,这样原有的内容将被直接覆盖。因为在绘制缓冲DC的期间窗口DC内容并没有发生改变,而从缓冲DC到窗口DC的复制拷贝十分迅速,所以就不在容易发生闪屏现象,这就实现了绘图的双缓冲处理。
以下是一个简单的双缓冲绘图示例的实现过程,给出简单步骤及主要代码:
1、 创建一个Win32工程BufferDraw
2、 在资源视图添加菜单项:普通绘图(ID_NORMAL_DRAW)、双缓冲绘图(ID_BUFFER_DRAW)
3、 在BufferDraw.cpp中添加需要的全局变量:
// 添加所需全局变量
int radius = 0; // 圆的半径
HDC hdcMem = NULL; // 创建内存DC句柄
HBITMAP hbmp = NULL; // 位图句柄
BOOL gbflag = 0; // 绘图模式,0为普通画刷绘图,1为双缓冲绘图
- 4、在BufferDraw.cpp中添加自定义绘图函数
// 添加自定义绘图函数:双缓冲绘图 BufferDraw()和普通绘图 NormalDraw()
void BufferDraw(HWNDhWnd) // 双缓冲绘图
{
HDC hdc;
staticHBRUSH hbr; // 用于存放当前窗口的画刷
int i, j;
RECT rt;
GetClientRect(hWnd, &rt); // 获取当前窗口大小
hdc = GetDC(hWnd); // 获取当前窗口句柄
if (!hdcMem) // 如果用于缓冲的内存DC还没有创建
{
hdcMem = CreateCompatibleDC(hdc);// 创建用于缓冲的内存DC
// 创建兼容位图
hbmp = CreateCompatibleBitmap(hdcMem,rt.right, rt.bottom); SelectObject(hdcMem, hbmp); // 把兼容位图选入用于缓冲的内存DC,作为画布
hbr = (HBRUSH)GetClassLong(hWnd, GCL_HBRBACKGROUND); // 获得默认背景画刷
}
FillRect(hdcMem, &rt, hbr); // 刷掉原来绘制的DC内容
// 绘制很多圆形
for (i = 0; i < rt.right - 1; i += 30)
{
for (j = 0; j < rt.bottom - 1; j += 30)
{
Ellipse(hdcMem, i, j, i + radius, j+ radius);
}
}
//使用BitBlt()函数快速的将缓冲DC中的数据拷贝到窗口DC中
BitBlt(hdc, 0, 0, rt.right, rt.bottom,hdcMem, 0, 0, SRCCOPY);
//释放窗口DC
ReleaseDC(hWnd, hdc);
}
void NormalDraw(HWNDhWnd) // 普通绘图
{
HDC hdc;
HBRUSH hbr; // 用于存放当前窗口的画刷
int i, j;
RECT rt;
GetClientRect(hWnd, &rt); // 获取当前窗口大小
hdc = GetDC(hWnd); // 获取当前窗口句柄
hbr = (HBRUSH)GetClassLong(hWnd, GCL_HBRBACKGROUND); // 获得默认背景画刷
FillRect(hdc, &rt, hbr); // 刷掉原来绘制的DC内容
// 绘制很多圆形
for (i = 0; i < rt.right - 1; i += 30)
{
for (j = 0; j < rt.bottom - 1; j += 30)
{
Ellipse(hdc, i, j, i + radius, j +radius);
}
}
ReleaseDC(hWnd, hdc); //释放窗口DC
}
- 5、在BufferDraw.cpp中添加自定义函数的前向声明
// 自定义函数的前向声明
void BufferDraw(HWND hWnd); // 双缓冲绘图
void NormalDraw(HWND hWnd); // 普通绘图
- 6、在BufferDraw.cpp的WndProc函数中添加WM_CREATE消息处理
// 添加WM_CREATE消息处理
case WM_CREATE:
ShowWindow(hWnd,SW_MAXIMIZE); // 设置当前窗口为当前DC下最大
SetTimer(hWnd,1, 50, NULL); // 开启定时器,设置每50ms发送一次 WM_TIMER消息
break;
- 7、在BufferDraw.cpp的WndProc函数中添加WM_TIMER消息处理
// 添加WM_TIMER消息处理
caseWM_TIMER:
if (radius < 30)
radius++; // 圆形半径增加
else
radius = 0; // 圆形半径归0
if (gbflag)
BufferDraw(hWnd); // 双缓冲绘图
else
NormalDraw(hWnd); // 普通绘图
break;
- 8、在WM_COMMAND消息处理中添加自定义菜单命令处理
// 添加自定义菜单命令处理
case ID_NORMAL_DRAW:
gbflag = 0; // 设置模式标志,以便下次定时器操作时调用普通绘图函数
break;
case ID_BUFFER_DRAW:
gbflag = 1; // 设置模式标志,以便下次定时器操作时调用双缓冲绘图函数
break;
- 9、在窗口销毁消息WM_DESTROY处理中添加资源释放相关代码
//在窗口销毁消息WM_DESTROY处理中添加资源释放相关代码
case WM_DESTROY:
KillTimer(hWnd, 1); // 关闭计时器
DeleteObject((HGDIOBJ)hbmp); // 删除兼容位图
DeleteDC(hdcMem); // 删除用于缓存的内存DC
PostQuitMessage(0);
break;