双缓冲绘图
来源:互联网 发布:327国债事件 知乎 编辑:程序博客网 时间:2024/06/05 18:47
双缓冲绘图
更新日期:2011-8-28
测试环境:VS2008+WinXP
━━━━━━━━━━━━━━━━━━━━━━━━
一:新建一个对话框工程。在OnButton1()里添加以下代码。(WIN32 API版本)
━━━━━━━━━━━━━━━━━━━━━━━━
/***************************************************
更新于:2011-8-28
双缓冲原理简介:
虽然代码有点“繁琐”,但记得我们关心的主要是下面
代码中hMemDC。所有绘图函数使用这个内存dc句柄即可。
我们经常不是这样调用的吗:
//屏幕上打印字体,画矩形
HDC hdc=::GetDC(m_hWnd);
::TextOut(hdc.......);
::RectangleRect(hdc......);
::ReleaseDC(m_hWnd,hdc) ;
而在双缓冲里,只需把所有需要用到屏幕hdc的函数中的hdc替换成hMemDC,如:
//内存dc上打印字体,画矩形
::TextOut(hMemDC.......);
::RectangleRect(hMemDC......);
画好后再调用BitBlt把内存hMemDC所有画好的东西拷贝到屏幕hScrDC
BitBlt(hScrDC,0,60,1000,1000,hMemDC,0,0,SRCCOPY);
**************************************************/
/***************************************************
步骤一:创建。
1.主要关心的是CreateCompatibleBitmap的后两个参数,
你往内存hMemDC里画的东东就在这个这个范围内
2.如果你已经有一个屏幕dc了,就去掉
HDC hScrDC=::GetDC(m_hWnd)和::ReleaseDC(m_hWnd,hScrDC) 这两行代码,
直接使用已有的屏幕dc代替下面代码中的所有hScrDC
**************************************************/
RECT ClientRect;
::GetClientRect(m_hWnd,&ClientRect);
UINT ClientWidth=ClientRect.right-ClientRect.left;
UINT ClientHeight=ClientRect.bottom-ClientRect.top;
HDC hScrDC=::GetDC(m_hWnd); //创建屏幕DC
HDC hMemDC=CreateCompatibleDC(hScrDC); //创建内存DC
HBITMAP bitmap=::CreateCompatibleBitmap(hScrDC,ClientWidth,ClientHeight);//创建兼容位图内存块,存放图片数据
HBITMAP OldBitmap=(HBITMAP)::SelectObject(hMemDC,bitmap); //内存hMemDC与位图内存块相关联
/***************************************************
步骤二:绘图。
使用hMemDC绘图,就像你平时绘图一样操作
绘图代码主要在这里完成
**************************************************/
//先画整个客户区的背景
::FillRect(hMemDC,&ClientRect,(HBRUSH)GetStockObject(WHITE_BRUSH));
//填充颜色矩形
HBRUSH brush=::CreateSolidBrush(RGB(255,216,156));
RECT rect={0,0,100,100};
::FillRect(hMemDC,&rect,brush);
::DeleteObject(brush);
//画空矩形2
::SelectObject(hMemDC,(HBRUSH)GetStockObject(NULL_BRUSH));
::Rectangle(hMemDC,10,10,60,60);
/***************************************************
步骤三:将内存DC图拷贝到屏幕DC上进行显示。
1.BitBlt前4个参数nXDest,nYDest,nWidth,nHeight.确定屏幕DC上的一个矩形区域。
将来要显示在屏幕上的图形范围就在这4个参数所决定的矩形区域内。
2.BitBlt后面2个坐标参数x,y指定从内存hMemDC的哪个位置开始拷贝到屏幕DC上
**************************************************/
BitBlt(hScrDC,0,0,ClientWidth,ClientHeight,hMemDC,0,0,SRCCOPY);
/***************************************************
步骤四:最后的收尾工作(删除或释放资源)
1.如果你还想继续使用bitmap所指向的图片数据,请不要调用::DeleteObject(bitmap);
**************************************************/
::SelectObject(hMemDC,OldBitmap); //bitmap指向的内存块再也没有和hMemDC挂钩,之后你可以使用bitmap所指向的图片数据了
::DeleteObject(bitmap); //释放位图所占Memory
::DeleteDC(hMemDC) ; //删除内存DC
::ReleaseDC(m_hWnd,hScrDC) ; //释放屏幕DC
二:新建一个对话框工程。在OnButton1()里添加以下代码。(MFC版本)
━━━━━━━━━━━━━━━━━━━━━━━━
/***************************************************
步骤一:创建。
1.主要关心的是CreateCompatibleBitmap的后两个参数,
你往内存hMemDC里画的东东就在这个这个范围内
2.如果你已经有一个屏幕dc了,就不要“CClientDC hScrDC(this);”
并且使用已有的屏幕dc代替下面代码中的所有hScrDC
**************************************************/
CClientDC hScrDC(this); //创建屏幕DC
CDC hMemDC;
hMemDC.CreateCompatibleDC(&hScrDC); //创建内存DC
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&hScrDC,1000,1000); //创建兼容位图
hMemDC.SelectObject(&bitmap); //创建兼容位图
/***************************************************
步骤二:绘图。
使用hMemDC绘图,就像你平时绘图一样操作
**************************************************/
//填充颜色矩形
CBrush brush(RGB(200,23,23));
RECT rect={0,0,200,200};
hMemDC.FillRect(&rect,&brush);
//画空矩形1
hMemDC.SelectObject((HBRUSH)GetStockObject(NULL_BRUSH));
hMemDC.Rectangle(10,10,20,20);
/***************************************************
步骤三:将内存DC图拷贝到屏幕DC上进行显示
1.BitBlt前4个参数nXDest,nYDest,nWidth,nHeight.确定一个矩形区域。
将来要显示在屏幕上的图形范围就在这4个参数所决定的矩形区域内
2.BitBlt后面2个x,y坐标参数指定从内存hMemDC哪个位置开始拷贝到屏幕DC上
**************************************************/
hScrDC.BitBlt(20,20,100,100,&hMemDC,0,0,SRCCOPY);
/***************************************************
补充说明:
当进行大量绘图时,判断所绘图形是否在CLipRect后在绘制,可以提高效率
RECT CLipRect;
::GetClipBox(hScrDC.m_hDC,&CLipRect);
**************************************************/
三、为了方便使用双缓冲,我们可以把这些看起来很繁琐的代码封装成一个类,以方便我们操作。
━━━━━━━━━━━━━━━━━━━━━━━━
/****************************************************************************
双缓冲封装类。
一、使用方法:
(1) 创建一个内存dc,并指定宽和高
CMenDC dc(m_hWnd,100,100);
::DrawText (dc.hMemDC...........)
(2) 只需传递窗口句柄,创建的内存dc的宽和高会设置为窗口的客户端宽和高
CMenDC dc(m_hWnd);
::DrawText (dc.hMemDC...........)
(3) 传递一个已经创建的屏幕dc(如WM_PAINT里)
CMenDC dc(hdc,100,100);
::DrawText (dc.hMemDC...........)
二、请不要用CMenDC创建的对象作为类的成员变量或静态变量。CMenDC创建的对象只能是临时的。
因为需要对象析构(临时对象被摧毁)时调用析构函数来把内存图绘制到屏幕上
****************************************************************************/
class CMenDC
{
private:
int x;
int y;
UINT ClientWidth ;
UINT ClientHeight ;
HWND m_hWnd;
HDC hScrDC;
HBITMAP bitmap;
/*HBITMAP OldBitmap;*/
public:
HDC hMemDC;
//设置整幅内存图绘制在窗口上的位置(相对于窗口的左上角)
void SetBitBltPos(int x,int y) { this->x=x ; this->y=y; }
CMenDC(HWND wnd) : m_hWnd(wnd),x(0),y(0)
{
ASSERT(m_hWnd);
RECT ClientRect;
::GetClientRect(m_hWnd,&ClientRect);
ClientWidth=ClientRect.right-ClientRect.left;
ClientHeight=ClientRect.bottom-ClientRect.top;
hScrDC=::GetDC(m_hWnd); //创建屏幕DC
hMemDC=CreateCompatibleDC(hScrDC); //创建内存DC
bitmap=::CreateCompatibleBitmap(hScrDC,ClientWidth,ClientHeight);//创建兼容位图内存块,存放图片数据
/*OldBitmap=(HBITMAP)*/::SelectObject(hMemDC,bitmap); //内存hMemDC与位图内存块相关联
}
CMenDC(HWND wnd,UINT width,UINT height) : m_hWnd(wnd),ClientWidth(width),ClientHeight(height),x(0),y(0)
{
ASSERT(m_hWnd);
hScrDC=::GetDC(m_hWnd); //创建屏幕DC
hMemDC=CreateCompatibleDC(hScrDC); //创建内存DC
bitmap=::CreateCompatibleBitmap(hScrDC,ClientWidth,ClientHeight);//创建兼容位图内存块,存放图片数据
/*OldBitmap=(HBITMAP)*/::SelectObject(hMemDC,bitmap); //内存hMemDC与位图内存块相关联
}
CMenDC(HDC hdc,UINT width,UINT height) : m_hWnd(NULL),hScrDC(hdc),ClientWidth(width),ClientHeight(height),x(0),y(0)
{
ASSERT(hdc);
hMemDC=CreateCompatibleDC(hScrDC); //创建内存DC
bitmap=::CreateCompatibleBitmap(hScrDC,ClientWidth,ClientHeight);//创建兼容位图内存块,存放图片数据
/*OldBitmap=(HBITMAP)*/::SelectObject(hMemDC,bitmap); //内存hMemDC与位图内存块相关联
}
//在类析构时把内存图bitblt到窗口上
~CMenDC()
{
/***************************************************
步骤三:将内存DC图拷贝到屏幕DC上进行显示。
1.BitBlt前4个参数nXDest,nYDest,nWidth,nHeight.确定屏幕DC上的一个矩形区域。
将来要显示在屏幕上的图形范围就在这4个参数所决定的矩形区域内。
2.BitBlt后面2个坐标参数x,y指定从内存hMemDC的哪个位置开始拷贝到屏幕DC上
**************************************************/
BitBlt(hScrDC,x,y,ClientWidth,ClientHeight,hMemDC,0,0,SRCCOPY);
/***************************************************
步骤四:最后的收尾工作(删除或释放资源)
1.如果你还想继续使用bitmap所指向的图片数据,请不要调用::DeleteObject(bitmap);
**************************************************/
/*::SelectObject(hMemDC,OldBitmap);*/ //bitmap指向的内存块再也没有和hMemDC挂钩,之后你可以使用bitmap所指向的图片数据了
::DeleteObject(bitmap); //释放位图所占Memory
::DeleteDC(hMemDC) ; //删除内存DC
if (m_hWnd)
::ReleaseDC(m_hWnd,hScrDC) ; //释放屏幕DC
}
};
双缓冲类测试代码:
━━━━━━━━━━━━━━━━━━━━━━━━
void CDemoDlg::OnButton1()
{
RECT rect;
::GetClientRect(m_hWnd,&rect);
CMenDC dc(m_hWnd,rect.right-rect.left,rect.bottom-rect.top);
// CMenDC dc(m_hWnd);
//先画整个客户区的背景
::FillRect(dc.hMemDC,&rect,(HBRUSH)GetStockObject(WHITE_BRUSH));
//填充颜色矩形
HBRUSH brush=::CreateSolidBrush(RGB(255,216,156));
RECT rect2={0,0,100,100};
::FillRect(dc.hMemDC,&rect2,brush);
::DeleteObject(brush);
//绘制文字
::DrawText (dc.hMemDC,_T("双缓冲绘图"),-1,&rect2,DT_SINGLELINE | DT_LEFT | DT_VCENTER|DT_END_ELLIPSIS ) ;
//画空矩形2
::SelectObject(dc.hMemDC,(HBRUSH)GetStockObject(NULL_BRUSH));
::Rectangle(dc.hMemDC,10,10,60,60);
//设置整幅内存图绘制在窗口上的位置(相对于窗口的左上角)
dc.SetBitBltPos(20,20);
}
三、实例解说双缓冲
转自http://www.vckbase.com/document/viewdoc/?id=1612
实例解说双缓冲
作者:HateMath 的网上田园
源代码下载
本文适合初学者
(转自 VC知识库 Blog HateMath 的网上田园)
昨天在论坛上,有人问起双缓冲的实现问题,想起网上这方面资料比较凌乱,而且多是 DirectX 相关的,今天特地在这里给大家简要的介绍一下双缓冲技术及其在 VC++ 的 GDI 绘图环境下的实现。
1、Windows 绘图原理
我们在 Windows 环境下看到各种元素,如菜单、按钮、窗口、图像,从根本上说,都是“画”出来的。这时的屏幕,就相当于一块黑板,而 Windows 下的各种 GDI 要素,如画笔、画刷等,就相当于彩色粉笔了。我们在黑板上手工画图时,是一笔一划的,电脑亦然。只不过电脑的速度比手工快的太多,所以在我们看起来好像所有的图形文字都是同时出现的。
2、普通绘图方式的局限
上述绘图方式我们暂且称之为普通绘图方式吧。虽然这种方式能满足相当一部分的绘图需要,但是当要绘制的对象太复杂,尤其是含有位图时,电脑便力不从心了。这时的画面会显示的很慢,对于运动的画面,会给人“卡”住了的感觉,总之一个字:不爽。
3、解决之道:双缓冲
双缓冲的原理可以这样形象的理解:把电脑屏幕看作一块黑板。首先我们在内存环境中建立一个“虚拟“的黑板,然后在这块黑板上绘制复杂的图形,等图形全部绘制完毕的时候,再一次性的把内存中绘制好的图形“拷贝”到另一块黑板(屏幕)上。采取这种方法可以提高绘图速度,极大的改善绘图效果。下面是原理图:
图一 双缓冲原理示意图
4、相关的函数介绍
1)、为屏幕 DC 创建兼容的内存 DC:CreateCompatibleDC()
{
::PostQuitMessage(0);
}
2)、创建位图:CreateCompatibleBitmap()
m_Bmp.CreateCompatibleBitmap(&m_dcMemory, rt.Width(), rt.Height()); // CBitmap m_Bmp;3)、把位图选入设备环境:SelectObject(),可以理解为选择画布
::SelectObject(m_dcMemory.GetSafeHdc(), m_Bmp);4)、把绘制好的图形“拷贝“到屏幕上:BitBlt()
pdcView->BitBlt(0, 0, rt.Width(), rt.Height(), &m_dcMemory, 0, 0, SRCCOPY);函数的具体用法详见 MSDN。有一句话我重复了多遍,再说一遍也无妨:MSDN是最好的老师。
5、本文给出了一个例子,用效果对比的方法说明普通绘图方式的局限和双缓冲技术的好处。
这个例子在一个 View 上画出很多半径渐变的圆,大家可以注意两种不同的绘图方式下动画的效果:
- 内存绘图、双缓冲绘图
- 内存绘图、双缓冲绘图
- 双缓冲绘图
- GDI双缓冲绘图
- vc双缓冲绘图?
- 双缓冲绘图
- GDI双缓冲绘图
- vc++ 双缓冲绘图
- MFC绘图,双缓冲
- VC双缓冲绘图
- VC6双缓冲绘图
- Windows 双缓冲绘图
- 双缓冲绘图
- 绘图双缓冲
- 双缓冲绘图
- VC 双缓冲绘图
- VC双缓冲绘图
- VC 双缓冲绘图
- 学会使用DNSPod,搞定二级域名的配置难题
- malloc的两个兄弟calloc,realloc你认识吗?
- 执行impdp命令出错的解决办法
- ListView item中的 RadioButton(自定义RadioButton图标,默认无图,选择有图)点击时不显示图片问题
- 转炉流程轴承钢技术的开发是轴承钢的冶炼时间缩短
- 双缓冲绘图
- How tomcat works 读书笔记十五 Digester库 下
- ubuntu下无法编译ruby-2.1.5提示something wrong with CFLAGS -arch x86_64
- 程序中的三国
- leetCode Two Sum
- 下载Ext JS 5.1 gpl版本的方法
- 简单的用户登录验证
- sql 字段左边去掉0
- iOS小问题---Your build settings specify a provisioning profile with the UUID的问题