CDC双缓冲防闪屏绘图总结
- 格式:doc
- 大小:29.00 KB
- 文档页数:4
详解使⽤双缓存解决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+:微软后来推出的二维绘图引擎。
图形设备接口一、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。
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一般只修改几项。
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,至此双缓冲绘图完全完成了。
C#双缓冲实现⽅法(可防⽌闪屏)本⽂实例讲述了C#双缓冲实现⽅法。
分享给⼤家供⼤家参考,具体如下:// 该调⽤是 Windows.Forms 窗体设计器所必需的。
InitializeComponent();// TODO: 在 InitComponent 调⽤后添加任何初始化this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);//开启双缓冲this.SetStyle(ControlStyles.DoubleBuffer,true);this.SetStyle(erPaint,true);this.SetStyle(ControlStyles.ResizeRedraw,true);1、在内存中建⽴⼀块“虚拟画布”:Bitmap bmp = new Bitmap(600, 600);2、获取这块内存画布的Graphics引⽤:Graphics g = Graphics.FromImage(bmp);3、在这块内存画布上绘图:g.FillEllipse(brush, i * 10, j * 10, 10, 10);4、将内存画布画到窗⼝中this.CreateGraphics().DrawImage(bmp, 0, 0);还有的⽅式在构造函数中加如下代码代码⼀:SetStyle(erPaint, true);SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁⽌擦除背景.SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲代码⼆:this.SetStyle(ControlStyles.DoubleBuffer | erPaint | ControlStyles.AllPaintingInWmPaint, true);this.UpdateStyles();更多关于C#相关内容感兴趣的读者可查看本站专题:《》、《》及《》希望本⽂所述对⼤家C#程序设计有所帮助。
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的内存显示到屏幕、
这样,不管有多少张图,实际上只在屏幕输出一次,就避免了闪烁现象。