CDC双缓冲防闪屏绘图总结
详解使用双缓存解决CanvasclearRect引起的闪屏问题

详解使⽤双缓存解决CanvasclearRect引起的闪屏问题前⾔今天⽤ canvas 做 H5 的时候遇到了闪屏问题。
闪烁效果如下图:问题简介功能简介H5 该部分的功能为:通过点击⼆级菜单,切换图⽚的遮罩或者更换背景。
因为功能简单,所以⽤了原⽣ canvas 实现这个功能。
但在使⽤clearRect清除画布的时候会出现闪烁的情况。
代码实现(问题代码)以下代码即为出现闪屏的关键代码,省略了图⽚的定义与 onload:// 点击⼆级菜单后,触发该函数更新画布updateCanvas(){const canvas = document.getElementById('canvas'); // 获取画布const ctx = canvas.getContext('2d');ctx.clearRect(0,0,1448,750); // 清空画布// 开始重绘ctx.drawImage(bg,0,0); // 背景... // 省略其他绘制过程}问题分析经过简单分析,得出闪屏的原因是clearRect清除画布后,绘制的时间较长导致出现闪屏的现象。
什么是双缓存来看⼀下⽹站中这篇⽂章对双缓存的解释:对图形进⾏编程时出现闪烁是⼀个常见问题。
需要多个复杂画图操作的图形操作可导致呈现的图像出现闪烁或具有不可接受的外观。
为解决这些问题,.NET Framework 提供了双缓冲功能。
双缓冲使⽤内容缓冲来解决与多个画图操作相关的闪烁问题。
启⽤双缓冲后,所有画图操作会⾸先呈现到内存缓冲⽽不是屏幕上的绘图图⾯。
所有画图操作完成后,内存缓冲会直接复制到与之关联的绘图图⾯。
由于屏幕上仅执⾏⼀个图形操作,因此与复杂画图操作相关的图像闪烁可得以消除。
使⽤双缓存解决问题以上引⽤,简单来说,主要问题就是绘制时间较长导致了闪屏,解决⽅法就是新建⼀个 canvas 作为缓存 canvas ,通过缓存canvas 完成绘制过程,绘制完成后,直接将缓存 canvas 复制到原来的 canvas,这样就可以解决绘制时间过长导致的闪屏问题。
提高二维矢量绘图效率之一般做法

提高二维矢量绘图效率之一般做法作者:朱金灿如何提高二维矢量绘图效率?这个问题很普遍。
最近在研究这个问题,在网上搜了一些资料,再结合自己的经验,谈谈自己的一些想法。
一.双缓存能提高绘图效率吗?网上有篇文章:绘图效率完整解决方案——三种手段提高GDI/GDI+绘图效率,其中提到一种方法是:1. 缓存——Bitmap或者DoubleBuffer。
缓存就是先把绘制的图形绘制到一张内存位图上,然后在一次性的贴位图,他可以提高绘图速度,也能避免闪烁。
DoubleBuffer=true是C#窗体的属性,设置了此属性估计系统本身会起用无效区的内存位图缓存,而不需要程序员Bitmap处理。
这里对双缓存的通常做法不作介绍,网上的相关资料很多。
说实话,我对使用双缓存能提升绘图效率表示怀疑,理由很简单,同是DC,同是绘制1000条线段,有什么理由内存DC就比窗口DC快(当然这个我没有作具体的测试,这个有空可以测试下)。
我还稍微怀疑使用双缓存绘图比直接使用窗口DC绘图还慢一些,理由有二:一是使用双缓存需要增加创建内存DC和内存位图的操作;二是使用双缓存还需要增加一个把内存DC拷贝到窗口DC的操作。
那么双缓存的主要作用是什么?其实就是解决绘图过程的闪烁问题,改善绘图效果。
二.Windows环境下二维绘图引擎的选取和绘图效率的一个重要相关因素是绘图引擎。
Windows环境下二维绘图引擎有多种选择:GDI、GDI+、DirectDraw、QT、Agg、Cairo、skia、Direct2D 、Direct3D、OpenGL等。
下面我逐一作一个简单的分析:GDI:微软原生的二维绘图引擎。
优点:微软的全力支持,作为操作系统核心层效率方面不用担心,支持多种开发框架(含语言):Win SDK、MFC、Delphi等。
缺点:基于过程,缺乏面向对象,使用起来不太方便,不支持反锯齿,不支持复杂的绘图效果(这个相对于GDI+而言)。
GDI+:微软后来推出的二维绘图引擎。
MFC双缓冲绘图实例

函数中CreateRectRgn函数设置裁剪区可以保证刷新时只刷新图片的部分,不刷新图片外的其他控件,这样其他控件就不会出现闪烁的情况,另外函数结束时要将裁剪区设置为空。
5.至于拖动关键点的操作需要调用以下三个函数,以及声明对应的三个消息即可,在OnLButtonUp,OnMouseMove中将变化后的点的坐标更新到face中,并调用Invalidate()即可。
dc->SelectClipRgn(&rgn);
dc_mem.CreateCompatibleDC(dc);
bitmap.CreateCompatibleBitmap(dc, edit_rect.Width(), edit_rect.Height());
void CEditLmDlg::DrawOnBuffer(){ CRgn rgn; rgn.CreateRectRgn(0, 0, image_width, image_height); dc->SelectClipRgn(&rgn); dc_mem.CreateCompatibleDC(dc); bitmap.CreateCompatibleBitmap(dc, edit_rect.Width(), edit_rect.Height()); CBitmap *pOldBit = dc_mem.SelectObject(&bitmap); dc_mem.FillSolidRect(edit_rect, dc->GetBkColor()); dc_mem.SetStretchBltMode(HALFTONE); CRect rect(0, 0, image_width, image_height); image.Draw(dc_mem.m_hDC, rect); dc->BitBlt(0, 0, edit_rect.Width(), edit_rect.Height(), &dc_mem, 0, 0,SRCCOPY); /**将所有的点绘制到dc_mem上*/代码略 dc->SelectClipRgn(NULL); dc_mem.DeleteDC(); bitmap.DeleteObject();}
CDC绘图

图形设备接口一、GDI、DC的概念1.GDI:(Graphics Device Interfase)图形设备接口,是一个应用程序与输出设备之间的中介。
一方面,GDI向应用程序提供一个与设备无关的编程环境,另一方面,它又以设备相关的格式和具体的设备打交道。
user32.dll2.DC:(Device Context)设备描述表,是一种Windows数据结构。
包括了与一个设备的绘制属性相关的信息。
所有的绘制操作通过一个设备描述表进行,绘制线条、形状和文本的Windows API 函数都与DC有关。
二、在Windows Application程序中画线1.定义两个全局变量用于记录鼠标按下的(x,y)坐标。
int nOrginX;int nOrginY;2.响应鼠标按下和鼠标抬起的消息:在Swich中加入case WM_LBUTTONDOWN:case WM_LBUTTONUP:3.在鼠标按下时记录鼠标按下的(x,y)坐标,查MSDN得知WM_LBUTTONDOWN lParam的低字存放x坐标,高字存放y坐标,将其取出存入nOrginX,nOrginY。
case WM_LBUTTONDOWN:nOrginX=lParam & 0x0000ffff;nOrginY=lParam >> 16 & 0x0000ffff;break;4.在鼠标抬起时画线:case WM_LBUTTONUP:HDC hdc;hdc=GetDC(hwnd);PAINTSTRUCT ps;::MoveToEx(hdc,nOrginX,nOrginY,NULL);::LineTo(hdc,LOWORD(lParam),HIWORD(lParam) );::ReleaseDC(hwnd,hdc);三、在MFC程序中画线:1.在CxxxView(其中xxx是你的工程名字)中响应鼠标按下和鼠标抬起的消息(因为只有CxxxView中才能接收到鼠标消息):使用ClassWizard加入WM_LBUTTONDOWN,WM_LBUTTONUP的消息响应函数OnLButtonDown, OnLButtonUp。
+MFC+GDI双缓冲避免图形闪烁

// TODO: Add your specialized creation code here SetTimer(1,10,NULL); return 0; } 利用定时器直接进行 10 毫秒的屏幕刷新,这样效果会出现不停的闪烁的情况. 解决方法利用双缓冲,首先触发 WM_ERASEBKGND,然后修改返回 TRUE; 定义变量: CBitmap *m_pBitmapOldBackground ; CBitmap m_bitmapBackground ; CDC m_dcBackground; //绘制背景 if(m_dcBackground.GetSafeHdc()== NULL|| (m_bitmapBackground.m_h Object == NULL)) {
MemDC.CreateCompatibleDC(NULL); //这时还不能绘图,因为没有地方画 ^_^ //下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的 大小 MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight); //将位图选入到内存显示设备中 //只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上 CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap); //先用背景色将位图清除干净,这里我用的是白色作为背景 //你也可以用自己应该用的颜色 MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255)); //绘图 MemDC.MoveTo(……); MemDC.LineTo(……);
} 说到这里可能又有人要说了 , 为什么一个简单图形看起来没有复杂图形那么闪呢? 这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要闪得厉 害一些,但是闪烁频率要低。 那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,闪烁 是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间的 差异很小所以看起来不闪 。 如果不信 , 可以在动画的每一帧中间加一张纯白的帧 , 不闪才怪呢。 2、如何避免闪烁 在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉 MFC 提 供的背景绘制过程了。实现的方法很多, * 可以在窗口形成时给窗口的注册类的背景刷付 NULL * 也可以在形成以后修改背景 static CBrush brush(RGB(255,0,0)); SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRU SH)brush); * 要简单也可以重载 OnEraseBkgnd(CDC* pDC)直接返回 TRUE. 这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,变 得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有图 形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存 中绘制好 , 然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去 (这个过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便 用什么反差大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为 内存中最终的图形与屏幕显示图形差别很小 (如果没有运动 , 当然就没有差别) , 这样看起来就不会闪。 3、如何实现双缓冲 首先给出实现的程序,然后再解释,同样是在 OnDraw(CDC *pDC)中: CDC MemDC; //首先定义一个显示设备对象 CBitmap MemBitmap;//定义一个位图对象 //随后建立与屏幕显示兼容的内存显示设备
于双缓冲技术的GDI无闪烁绘图

Abstract: Graphics flickering is an intractable common problem when programming Windows forms applications by using GDI + . On the basis of profound analysis on the model of form redrawing, the essentials of graphics flickering were explained in this paper. Two flickerfree GDI + drawing methods were proposed based on double buffering built in the . NET framework. To take analog clock as an example, it is shown that these two methods are very easytouse and effective. Key words: double buffering; flickerfree drawing; graphics flickering; GDI + ; . NET framework; analog clock
基于双缓冲技术的 GDI + 无闪烁绘图
江建国 ,温少营,张瑞楠
( 辽宁师范大学 数学学院, 辽宁 大连 116029 ) ( * 通信作者电子邮箱 jjgbox@ sina. com)
*
要: 图形闪烁是使用 GDI + 编写 Windows 窗体应用程序时经 常 遇 到的 一 个 问题。 在 深 入 分析 窗 体重 绘 模型 阐述了图形闪烁的本质原因。利用. NET框架 中 内 置 的 双 缓 冲 技术, 提出了 两 种 GDI + 无 闪烁 绘 图方 法。 以 基础上, 模拟时钟程序为例, 说明了这两种方法都非常简便且有效。 关键词: 双缓冲; 无闪烁绘图; 图形闪烁; GDபைடு நூலகம் + ; . NET框架; 模拟时钟 中图分类号: TP391. 41 文献标志码: A
基于双缓存技术解决某模拟系统实时显示屏幕闪烁的方法

实 时数 据 显 示 , 解 决 屏 幕 闪烁 的难 题 , 该 模 拟 系 统软 件 开 发 要 重 点 解 决 的 关键 问题 之 一 。 并 是 在 屏 幕 显 示 区域 , 实 时 显示 探 测 数 据 , 要 实 时绘 制 和显 示 图形 , 果 直 接 在 屏 幕 上 动态 绘 图 , 幕 就 会 明显 的闪 烁 。这 是 因 要 并 如 屏 为 图形 绘 制 过 程 , 分 为 绘 制 、 除 、 绘 三 个 阶段 f 其 中擦 除 窗 口显 示 区 的操 作 是 必须 的 , 在 新 的图 形 绘 制 出 之 前 , 极 短 时 间 可 擦 重 , 而 在
Vo ., 7 S p e e 0 8 P 1 7 —1 7 13No., e t mb r2 0 ,P . 5 4 5 6
基于双缓存技术解决某模拟系统实时显示屏幕 闪烁 的方法
董 周 明,卫 燕,燕 崔 兵
( 国 人 民 解放 军 炮 兵 学 院 科 研 部 , 徽 合 肥 2 0 3 ) 中 安 30 1
Ke y wor s ou ebuf rm e o ;r a—t e ds ly; c e n f c rn m eho d :d bl f m r e y e —i ip a s r e ikei g; l m l t d
1 引 言
某 模 拟 系 统 的模 拟 对 象 之 一 是 监 视 雷 达 天 线 , 扫 描 周 期 为 1 或 1 其 秒 . , 就 是 说 雷 达 天 线 每 转 一 圈 , 测 数 据 和 探 测 数 据 5秒 也 探 曲 线 至少 要 在 1秒 之 内 , 刷新 终 端 显 示 器 1次 , 就 对模 拟 系 统 的 数 据 处 理模 块程 序 提 出 了很 高 的要 求 。如 何 实 现 实 时 图 形绘 制 和 这
双缓冲绘图

1创建画笔,画刷void CGraphView::OnDraw(CDC* pDC){CGraphDoc* pDoc = GetDocument( );ASSERT_V ALID(pDoc);// TODO: add draw code for native data here// 使用画笔第一步:创建一支新画笔CPen pen(PS_SOLID, 1, RGB(255,0,0));CPen pen(PS_DASHDOT, 1, RGB(255,0,0));//第二步:选择新画笔到设备环境中,同时保存旧画笔CPen * pOldPen = pDC->SelectObject(&pen);// 使用画刷第一步:创建一支新画刷CBrush brush(RGB(255,0,0)); //创建实体画刷CBrush brush(HS_CROSS, RGB(255,0,0)); //创建阴影模式画刷//第二步:选择新画刷到设备环境中,同时保存旧画刷CBrush * pOldBrush = pDC->SelectObject(&brush);// 常用的绘图函数////绘制一个彩色点pDC->TextOut(20, 20, "点");pDC->SetPixel(100, 40, RGB(255,0,0));//绘制直线pDC->TextOut(320, 20,"线段");pDC->MoveTo(400, 40);pDC->LineTo(500, 40);//绘制折线pDC->TextOut(20, 170, "折线");POINT polyline[4]={{240,240},{80,120},{240,120},{80,240}}; pDC->Polyline(polyline,4);//绘制矩形pDC->TextOut(320, 170, "矩形");pDC->Rectangle(390, 110, 600, 230);//绘制椭圆pDC->TextOut(20, 320, "椭圆");pDC->Ellipse(80, 260, 280, 380);//绘制多边形pDC->TextOut(320, 320, "多边形");POINT polygon[3]={{380,330},{530,260},{500,360}};pDC->Polygon(polygon,3);//第三步:恢复设备环境的旧画笔,以便释放新画笔// pDC->SelectObject(pOldPen);//第三步:恢复设备环境的旧画刷,以便释放新画刷// pDC->SelectObject(pOldBrush);}2创建字体的两种方法第一种创建字体的方法:使用CreateFont函数,如下定义:CFont::CreateFontBOOL CreateFont( int nHeight, //字体的高度int nWidth, //字体的宽度int nEscapement, //字体显示的角度int nOrientation, //字体的角度int nWeight, //字体的磅数BYTE bItalic, //斜体字体BYTE bUnderline, //带下划线的字体BYTE cStrikeOut, //带删除线的字体BYTE nCharSet, //所需的字符集BYTE nOutPrecision, //输出的精度BYTE nClipPrecision, //裁减的精度BYTE nQuality, //逻辑字体与输出设备的实际//字体之间的精度BYTE nPitchAndFamily, //字体间距和字体集LPCTSTR lpszFacename //字体名称);例子:font.CreateFont(12, // nHeight0, // nWidth0, // nEscapement0, // nOrientationFW_NORMAL, // nWeightFALSE, // bItalicFALSE, // bUnderline0, // cStrikeOutANSI_CHARSET, // nCharSetOUT_DEFAULT_PRECIS, // nOutPrecisionCLIP_DEFAULT_PRECIS, // nClipPrecision DEFAULT_QUALITY, // nQualityDEFAULT_PITCH | FF_SWISS, // nPitchAndFamily "Arial"); // lpszFacename一般只修改几项。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
CDC双缓冲防闪屏绘图总结
CDC在屏幕绘图可以用以下方法:
CDC dc;
dc.CreateCompatibleDC(NULL);
CBitmap m_bitmap;
m_bitmap.LoadBitmap(IDB_BITMAP1); //载入资源文件
dc.SelectObject(&m_bitmap);
CDC *pDC = GetDC();
pDC->BitBlt(0,0,200,100,&dc,0,0,SRCCOPY);
m_bitmap.DeleteObject();
dc.DeleteDC();
上面虽然可以在屏幕绘图,但是如里需要绘多张图,并且有重叠的部份,当鼠标改变窗口大小时,重叠部份有严重的闪烁。
非常难看
CDC *pDC = GetDC();
pDC->BitBlt(0,0,200,100,&dc,0,0,SRCCOPY); 这个方式是直接在屏幕绘图,如果多张图,即多次调用此方法。
为了避免闪烁,可采用双缓冲的方法,不管你要绘多少张图,先把它们绘在一个内存DC,之后再在屏幕显示
DC小知识点:
CDC dc; 这个是内存dc, 使用BitBlt只会在内存中,不会在屏幕显示
一旦dc=GetDC(); 此时dc就与屏幕有关了,BitBlt方法就会输出的屏幕
CDC双缓冲防闪屏绘图实例
下面的nWidth,nHeight 是屏幕的宽高
CDC *pDC=this->GetDC(); //此DC负责在屏幕显示
CDC dc,sub_dc; //dc 负责组建对话框整张大图,sub_dc负责里面小块
dc.CreateCompatibleDC(NULL);
sub_dc.CreateCompatibleDC(&dc); //注意参数,&dc就指定了sub_dc是基于dc的。
CBitmap bg_bmp,load_bmp; // bg_bmp负责构造背景,load_bmp负责从资源文件载入位图
bg_bmp.CreateCompatibleBitmap(pDC,nWidth,nHeight); //先创建一张与对话框当前大小一致的位图
dc.SelectObject(&bg_bmp);
dc.FillSolidRect(0,0,nWidth,nHeight,RGB(236,236,234)); //构造背景
bg_bmp.DeleteObject();
//构造第一张图
load_bmp.LoadBitmap(IDB_TITLE_L); //从资源载入第一张图
sub_dc.SelectObject(&load_bmp); //放到sub_dc中,
dc.BitBlt(0,0,16,28,&sub_dc,0,0,SRCCOPY); //把sub_dc的内容写到dc中,此时dc的内容并不会在屏幕显示
load_bmp.DeleteObject();
//构造第二张图
load_bmp.LoadBitmap(IDB_TITLE_BG); //从资源载入第二张图
sub_dc.SelectObject(&load_bmp); //放到sub_dc中,
dc.StretchBlt(16,0,(nWidth-145),28,&sub_dc,0,0,1,28,SRCCOPY); //再把sub_dc的内容写到dc中,这样第二张图就与第一张图合在一起变成了一张图
load_bmp.DeleteObject();
//输出到屏幕
pDC->BitBlt(0,0,rc.Width(),rc.Height(),&dc,0,0,SRCCOPY);
this->ReleaseDC(pDC);
this->ReleaseDC(&dc);
this->ReleaseDC(&sub_dc);
OK,至此双缓冲绘图完全完成了。
单缓冲用两个dc, 比缓冲用了三个dc:dc,sub_dc , pDC
dc利用BitBlt方法合并成一张大图,pDC把dc的内存显示到屏幕、
这样,不管有多少张图,实际上只在屏幕输出一次,就避免了闪烁现象。