Win32下双缓冲绘图技术
一:双缓冲原理
为了解决窗口刷新频率过快所带来的闪烁问题,利用双缓冲技术进行绘图。所谓双缓冲技术,就是将资源加载到内存,然后复制内存数据到设备DC(这个比较快),避免了直接在设备DC上绘图(这个比较慢)。打个简单的比方:有个画家在街边办了一个即时画展,在同一块画布上根据观众的要求画不同的图像,每当有一位观众制定要看什么画时,画家先把之前画布上的东西全部擦干净,再重新绘画。显然有一些经典的画像是大家都想看的,按照以前的老办法,画家每次都要重新画这幅图像,但这种擦了画,画了擦的方式很费时。所以画家想了一个办法,把这些经典画像预先用一块或几块画布画下来,等有人需要看时,把这些预备好的画布贴在现有画布的前面,这样就能满足观众的实时性要求。那么这些事先预备好的画布就相当于内存DC,把资源放在内存DC里,等到要刷新显示时,将内存DC上的东西“贴”到当前窗口DC上,就可以减少延时带来的闪烁问题,这就是双缓冲的原理。
详细介绍见后面的几片博文。下面举两个例子:
二: 例子
例子一:加载位图
代码:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc; switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
myDraw(hdc); EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage();
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return ;
}
myDraw函数的实现:
const int g_picHeight = ;
const int g_picWidth = ;
void myDraw(HDC &dc)
{
RECT rect;
HBITMAP hOrgBitmap;
HBITMAP hOldBitmap;
int disHeight, disWidth; GetClientRect(g_hWnd, &rect);//获取客户区大小
disHeight = rect.bottom-rect.top;
disWidth = rect.right-rect.left; //加载图片
hOrgBitmap = (HBITMAP)::LoadImage(hInst, _T("test2.bmp"), IMAGE_BITMAP, g_picWidth, g_picHeight, LR_LOADFROMFILE); HDC mDc = ::CreateCompatibleDC(dc);//创建当前上下文的兼容dc(内存DC)
hOldBitmap = (HBITMAP)::SelectObject(mDc, hOrgBitmap);//将位图加载到内存DC //拷贝内存DC数据块到当前DC,自动拉伸
::StretchBlt(dc, , , disWidth, disHeight, mDc, , , g_picWidth, g_picHeight, SRCCOPY); //恢复内存原始数据
::SelectObject(mDc, hOldBitmap); //删除资源,防止泄漏
::DeleteObject(hOrgBitmap);
::DeleteDC(mDc);
}
结果:
调整窗口大小,发现无闪烁现象。
例子二:画各种直线和曲线。这是一个稍微复杂点的例子,是我平时做的一个demo。可以顺便熟悉一下Windows绘图的一些操作。
代码:
void CRTVIEW_win32DlgDlg::OnPaint()
{
if (IsIconic())
{
/********此段代码忽略*********/
}
else
{
CDialog::OnPaint(); //调用基类的默认控件绘制
CRect ctrlRect;
CStatic *pDegreePicCtrl = (CStatic *)GetDlgItem(IDC_STC_DEGREEPIC);
pDegreePicCtrl->GetClientRect(&ctrlRect);//获取静态控件尺寸 CDC *pdc = pDegreePicCtrl->GetWindowDC();//获取控件DC
CDC dcMemory;
dcMemory.CreateCompatibleDC(pdc);//创建内存DC CBitmap *pOldMapMemory;
CBitmap mapMemory;
mapMemory.CreateCompatibleBitmap(pdc, ctrlRect.Width(), ctrlRect.Height());//创建控件DC的兼容位图。其实就是与控件DC大小相同的一块区域
pOldMapMemory = dcMemory.SelectObject(&mapMemory);//加载兼容位图,只有制定了“桌布”尺寸之后,你才能在内存DC上面绘图 DrawDegreePicBkGrd(&dcMemory);//在内存DC上绘图
pdc->BitBlt(, , ctrlRect.Width(), ctrlRect.Height(), &dcMemory, , , SRCCOPY);//将内存DC上的内容复制到控件DC上
dcMemory.SelectObject(pOldMapMemory);//还原原来的内存DC
::DeleteObject(mapMemory);//删除兼容位图资源
::DeleteDC(dcMemory);//删除内存DC
ReleaseDC(pdc);//释放控件DC
}
}
void CRTVIEW_win32DlgDlg::DrawDegreePicBkGrd(CDC *pdc)
{
CRect stcRect, picRect;
CStatic *pDegreePicCtrl = (CStatic *)GetDlgItem(IDC_STC_DEGREEPIC);
pDegreePicCtrl->GetClientRect(&stcRect);
if(stcRect.Width() > stcRect.Height()) {
int diff = (stcRect.Width() - stcRect.Height()) / ;
picRect.left = stcRect.left + diff;
picRect.right = stcRect.right - diff;
picRect.top = stcRect.top;
picRect.bottom = stcRect.bottom;
} else {
int diff = (stcRect.Height() - stcRect.Width()) / ;
picRect.left = stcRect.left;
picRect.right = stcRect.right;
picRect.top = stcRect.top + diff;
picRect.bottom = stcRect.bottom - diff;
} CBrush *pOldBrush;
/**************画圆形***************/
CBrush newBrush1;
newBrush1.CreateSolidBrush(RGB(, , ));
pOldBrush = pdc->SelectObject(&newBrush1);
pdc->Ellipse(&picRect); /**************画原点***************/
CRect orgRect(stcRect.Width()/-, stcRect.Height()/-, stcRect.Width()/+, stcRect.Height()/+);
CBrush newBrush2;
newBrush2.CreateSolidBrush(RGB(,,));
pOldBrush = pdc->SelectObject(&newBrush2);
pdc->Ellipse(&orgRect); pdc->SelectObject(pOldBrush); /*************画刻度***************/
CPoint center(stcRect.Width()/, stcRect.Height()/);
double radias = (double)picRect.Width()/; CPen newPen(PS_SOLID, , RGB(,,));
CPen *poldPen = pdc->SelectObject(&newPen);
CPoint startPoint, endPoint; for(int i=; i<; i=i+) {
double cosval = cos(DEGREETORADIAN(i));
double sinval = sin(DEGREETORADIAN(i));
startPoint.x = center.x + int(radias * cosval); //当前角度对应的圆上的点的x坐标
startPoint.y = center.y - int(radias * sinval); //当前角度对应的圆上的点的y坐标 if(i% == ) {
endPoint.x = startPoint.x - int( * cosval);
endPoint.y = startPoint.y + int( * sinval);
} else {
endPoint.x = startPoint.x - int( * cosval);
endPoint.y = startPoint.y + int( * sinval);
}
pdc->MoveTo(startPoint);
pdc->LineTo(endPoint);
}
pdc->SelectObject(poldPen);
}
效果:
三:小结
这两个例子里面,其实每次重绘都是重新申请内存DC,然后复制到窗口DC。虽然这样子比较繁琐,但是也不影响效果,如果在响应onpaint消息时,不擦除背景(如调用Invalidate(FALSE)),也不会产生闪烁。不过最好的办法,就是文章开头说的,只画一次,把那个内存DC的句柄保存下来,每次在onpaint里面重绘时,直接调用BitBlt复制即可。不过要注意这些句柄对象的销毁,以免内存泄漏。
下面这些文章也可以看看:
1 http://baike.baidu.com/view/1149326.htm
2 http://blog.csdn.net/xsc2001/article/details/5378601
3 http://www.cppblog.com/wrhwww/archive/2011/03/01/140913.html
4 http://www.cnblogs.com/afarmer/archive/2012/03/31/2427315.html
5 http://www.programlife.net/mfc-draw-pictures-with-memory-dc-buffer.html
6 http://blog.csdn.net/zxzerster/article/details/5659775
7 http://blog.csdn.net/yiruirui0507/article/details/6153607
8 http://blog.csdn.net/zicheng_lin/article/details/7179278
Win32下双缓冲绘图技术的更多相关文章
- 【MFC】MFC绘制动态曲线,用双缓冲绘图技术防闪烁
摘自:http://zhy1987819.blog.163.com/blog/static/841427882011614103454335/ MFC绘制动态曲线,用双缓冲绘图技术防闪烁 2011 ...
- win32下的双缓冲绘图技术
一:双缓冲原理 为了解决窗口刷新频率过快所带来的闪烁问题,利用双缓冲技术进行绘图.所谓双缓冲技术,就是将资源加载到内存,然后复制内存数据到设备DC(这个比较快),避免了直接在设备DC上绘图(这个比较慢 ...
- [Android学习笔记]双缓冲绘图技术
双缓冲技术绘图: 什么情况下产生的双缓冲技术?当数据量很大时,绘图可能需要花费很长的时间,这样屏幕就会出现卡顿,闪烁等现象. 什么是双缓冲技术?双缓冲是在内存中创建一个与屏幕绘制区域一致的对象,先将图 ...
- [转载] MFC绘制动态曲线,用双缓冲绘图技术防闪烁
转载的原文地址 先上效果图 随着时间的推移,曲线向右平移,同时X轴的时间坐标跟着更新. 一.如何绘制动态曲线 所谓动画,都是一帧一帧的图像连续呈现在用户面前形成的.所以如果你掌握了如何绘制静态曲线,那 ...
- C#-gdi绘图,双缓冲绘图,Paint事件的触发
一. 画面闪烁问题与双缓冲技术 1.1 导致画面闪烁的关键原因分析: 1 绘制窗口由于大小位置状态改变进行重绘操作时 绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面 ...
- 陈灯WGF双缓冲绘图框架
“木丸子童屋”,专售各类儿童玩具,价格优惠,请大家多多支持:http://shop65552598.taobao.com/ WGF(windows graphic foundation)为window ...
- MFC--自己优化滚动条的双缓冲绘图方法
2010-01-09 18:45 MFC--自己优化的双缓冲绘图方法 自己通过尝试,用修改视图坐标的方法, 优化了双缓冲绘图,实现起来并不复杂. 在介绍这个方法前,重新介绍一下窗口和视口的概念 ...
- [Qt2D绘图]-06QPainter的复合模式&&双缓冲绘图&&绘图中的其他问题
本篇读书笔记主要记录QPainter的复合模式&&双缓冲绘图&&绘图中的其他问题 大纲: 复合模式 双缓冲绘图 绘图中的其他问题 ...
- MFC双缓冲绘图(2015.09.24)
问题引入: 最近在尝试编写贪吃蛇游戏时遇到这么一个问题:当系统以较快频率向窗口发送WM_PAINT消息时,调用OnPaint()函数在窗口中绘制图形就会发生闪烁现象. 问题分析: 当我们把绘图过程放在 ...
随机推荐
- 时间复杂度T(n)
1:概念 T(n)被称为时间复杂度,一般为在某个算法中操作步骤的重复次数与问题规模n的关系,下面一一举例说明 2:具体说明 2.1:常数阶o(1) 无论代码有多少行,只要没有循环等复杂的结构,其算法时 ...
- 离群点检测(Novelty Detection, Outlier Detenction)
适合问题: 对于无标签的数据, 又想找出坏用户,完成业务目标. 参考: https://scikit-learn.org/stable/modules/outlier_detection.html 算 ...
- HNOI2018/AHOI2018 游戏
这题放过了暴力其实就没啥意思了 虽然暴力复杂度很玄学,但是思维水平确实没啥 Description link 题意概述:现在有一条长度为 \(n\) 的链,有些边是有限制的 限制为能到某个点,才能经过 ...
- java使用io流实现图片复制
import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException; public cl ...
- java图片上传,通过MultipartFile方式,如果后台获取null检查是否缺少步骤
本方法基于springMvc 1.首先需要在webap下创建images 2.在springmvc.xml上引入 <bean id="multipartResolver" c ...
- Linux读取目录文件
1.opendir与readdir函数 (1).opendir打开一个目录后得到一个DIR类型的的指针给readdir使用. (2).readdir函数调用一次后就会返回一个struct dirent ...
- Python笔记_第三篇_面向对象_7.多态
1. 多态的概念 多态:一种事物的多种形态.其表现形式就是连续的继承关系. 还以人喂食动物的例子.最终目标是人可以喂食任何一种动物.如果人要喂食100多种动物,难道要写100中方法吗?多态就是把属性和 ...
- Centos7.6环境中安装zabbix3.4
官网链接:https://www.zabbix.com/documentation/3.4/zh/manual/installation/install_from_packages 部署环境 虚拟机服 ...
- 14 微服务电商【黑马乐优商城】:day02-springcloud(理论篇二:知道什么是SpringCloud)
本项目的笔记和资料的Download,请点击这一句话自行获取. day01-springboot(理论篇) :day01-springboot(实践篇) day02-springcloud(理论篇一: ...
- 利用face_recognition,dlib与OpenCV调用摄像头进行人脸识别
用已经搭建好 face_recognition,dlib 环境来进行人脸识别 未搭建好环境请参考:https://www.cnblogs.com/guihua-pingting/p/12201077. ...