图片控件上实现双缓冲绘图防止闪烁
- 格式:doc
- 大小:34.00 KB
- 文档页数:2
详解使⽤双缓存解决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,这样就可以解决绘制时间过长导致的闪屏问题。
C#在PictureBox中绘图防止闪烁的办法展开全文很久没发技术文章了啊……被人说装文艺了啊……我在乱说些啥吗…………最近学校开了数据结构的课设设计,说是允许使用C++,Java和C#来进行开发。
Java上上个学期学的,说实话,感觉真的不是很爽……或许是我电脑的缘故,也或许是心理作用,我总觉的NetBeans 一开就卡得不行!无论怎样都得不到在VS中开发和调试的那种爽快感,于是一度打算投奔C++的阵营,还为此买了Qt的书来学习,不过由于一直以来被诸多事情所扰(懒?),Qt的学习就停留在了……编译完毕。
好吧,我输了……C#,就决定是你了!那么进入正题吧。
在课程设计的过程中,我需要在窗体上进行图片的绘制,但是在实际的测试中发现了问题,那就是重绘的时候会发生闪烁,这个问题其实在大一的C语言课设的时候就出现过了,在程序绘制动画的高频率刷新的时候,也会产生闪烁,而那时候的解决办法,是对动画进行双缓冲(Double Buffering)处理。
在被双缓冲这个名词吓到之前,我们先来探讨下为什么重绘的时候会发生闪烁:说道动画的原理大家都懂,就是利用了人眼的视觉残留(Visual staying)现象,当一副画面进入人眼成像后,并不会立刻消失,而是仍会保留一小段时间,于是当连续的图像以很高的速度切换的时候,人眼会看到动态的影响,而不是处于切换中的单个图像。
这个过程可以参考图1:当这三幅图片以一定频率直接切换的时候,人们就会看到A貌似是在像右方移动。
那么为什么我们依据这个原理来编程绘制动画的时候会出现闪烁呢?是因为计算机的速度太慢不够给力么?当然不是!我们如果不加任何处理,就在画布C上进行绘图,那么计算机的处理过程是这样的:Step 1: 将C以背景色填充(也就是清除C上现有的内容)Step 2: 在C上按照要求绘制新的画面那么这样的过程会对动画产生怎样的影响呢?请看图2:看出和图1的差别了吧?Step1相当于在原本连续的动画中嵌入了空白的画面,这个空白的画面由于和人眼中原本残留的图像反差非常大,所以便会破坏视觉残留产生的动画,给人的感觉就是,这个动画在不停的闪烁。
VC无闪烁刷屏技术的实现---经验总结之防止窗口闪烁的方法也许我们都碰到过这种情况,当你想重画某个窗口的时候,或你需要每隔一段时间要进行重画窗口,窗口会不停的闪烁。
那么如何消除闪烁呢?借鉴了别人的经验,自己也总结一下,现将总结的几种方法介绍一下,供大家参考。
1、将Invalidate()替换为InvalidateRect()。
因为Invalidate()会导致整个窗口的图象重画,需要的时间比较长,而InvalidateRect()仅仅重画Rect区域内的内容,所需时间会少一些。
所以替换之后在很大程度上会减少闪烁。
如果你确实需要改善闪烁的情况,计算一个Rect所用的时间比起重画那些不需要重画的内容所需要的时间要少得多。
2、不要让系统擦除你的窗口。
系统在需要重画窗口的时候会帮你用指定的背景色来擦除窗口。
可是,也许需要重画的区域也许非常小。
或者,在你重画这些东西之间还要经过大量的计算才能开始。
这个时候你可以禁止系统擦掉原来的图象。
直到你已经计算好了所有的数据,自己把那些需要擦掉的部分用背景色覆盖掉(如:dc.FillRect(rect,&brush);rect是需要擦除的区域,brush是带背景色的刷子),再画上新的图形。
要禁止系统擦除你的窗口,可以重载OnEraseBkgnd()函数,让其直接返回TRUE就可以了。
如BOOL CMyWin::OnEraseBkgnd(CDC* pDC){return TRUE;//return CWnd::OnEraseBkgnd(pDC);//把系统原来的这条语句注释掉。
}3、有效的进行擦除。
擦除背景的时候,不要该擦不该擦的地方都擦。
比如,你在一个窗口上放了一个很大的Edit框,几乎占了整个窗口,那么你频繁的擦除整个窗口背景将导致Edit不停重画形成剧烈的闪烁。
事实上你可以CRgn创建一个需要擦除的区域,只擦除这一部分。
如GetClientRect(rectClient);rgn1.CreateRectRgnIndirect(rectClient);rgn2.CreateRectRgnIndirect(m_rectEdit);if(bineRgn(&rgn1,&rgn2,RGN_XOR) == ERROR)//处理后的rgn1只包括了Edit框之外的客户区域,这样,Edit将不会被我的背景覆盖而导致重画。
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#程序设计有所帮助。
import java.awt.BasicStroke;import java.awt.BorderLayout;import java.awt.Color;import java.awt.Dimension;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Rectangle;import java.awt.event.MouseEvent;import java.awt.event.MouseListener;import java.awt.event.MouseMotionListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.awt.image.BufferedImage;import javax.swing.JFrame;import javax.swing.JPanel;public class BufferedDraw extends JPanel implements MouseListener,MouseMotionListener {Rectangle rect = new Rectangle(0, 0, 100, 50);BufferedImage bi = new BufferedImage(5, 5, BufferedImage.TYPE_INT_RGB); Graphics2D big;int last_x, last_y;boolean firstTime = true;Rectangle area;boolean pressOut = false;public BufferedDraw() {setBackground(Color.white);addMouseMotionListener(this);addMouseListener(this);}// Handles the event of the user pressing down the mouse button.public void mousePressed(MouseEvent e) {last_x = rect.x - e.getX();last_y = rect.y - e.getY();// Checks whether or not the cursor is inside of the rectangle while the // user is pressing themouse.if (rect.contains(e.getX(), e.getY())) {updateLocation(e);} else {pressOut = true;}}// Handles the event of a user dragging the mouse while holding down the // mouse button.public void mouseDragged(MouseEvent e) {if (!pressOut) {updateLocation(e);}}// Handles the event of a user releasing the mouse button.public void mouseReleased(MouseEvent e) {if (rect.contains(e.getX(), e.getY())) {updateLocation(e);}}public void mouseMoved(MouseEvent e) {}public void mouseClicked(MouseEvent e) {}public void mouseExited(MouseEvent e) {}public void mouseEntered(MouseEvent e) {}public void updateLocation(MouseEvent e) {rect.setLocation(last_x + e.getX(), last_y + e.getY());repaint();}public void paint(Graphics g) {update(g);}public void update(Graphics g) {Graphics2D g2 = (Graphics2D) g;if (firstTime) {Dimension dim = getSize();int w = dim.width;int h = dim.height;area = new Rectangle(dim);bi = (BufferedImage) createImage(w, h);big = bi.createGraphics();rect.setLocation(w / 2 - 50, h / 2 - 25); big.setStroke(new BasicStroke(8.0f));firstTime = false;}big.setColor(Color.white);big.clearRect(0, 0, area.width, area.height);big.setPaint(Color.red);big.draw(rect);big.setPaint(Color.blue);big.fill(rect);g2.drawImage(bi, 0, 0, this);}private boolean checkRect() {if (area == null) {return false;}if (area.contains(rect.x, rect.y, 100, 50)) { return true;}int new_x = rect.x;int new_y = rect.y;if ((rect.x + 100) > area.width) {new_x = area.width - 99;}if (rect.x < 0) {new_x = -1;}if ((rect.y + 50) > area.height) {new_y = area.height - 49;}if (rect.y < 0) {new_y = -1;}rect.setLocation(new_x, new_y);return false;}public static void main(String s[]) {JFrame f = new JFrame("BufferedShapeMover");f.addWindowListener(new WindowAdapter() {public void windowClosing(WindowEvent e) {System.exit(0);}});f.getContentPane().setLayout(new BorderLayout());f.getContentPane().add(new BufferedDraw(), "Center");f.pack();f.setSize(new Dimension(550, 250));f.show();}}。
绘图的双缓冲技术简介幸运的是当编写一个典型的Windows 窗体程序时,窗体和控件的绘制、效果等操作是不需要特别加以考虑的。
这是为什么呢?因为通过使用.Net 框架,开发人员可以拖动一系列的控件到窗体上,并书写一些简单的与事件相关联的代码然后在IDE中按F5,一个完完全全的窗体程序就诞生了!所有控件都将自己绘制自己,窗体或者控件的大小和缩放都调整自如。
在这里经常会用到的,且需要引起一点注意的就是控件效果。
游戏,自定义图表控件以及屏幕保护程序的编写会需要程序员额外撰写用于响应Paint 事件的代码。
本文针对那些Windows 窗体开发人员并有助于他们在应用程序编制过程中使用简单的绘图技术。
首先,我们会讨论一些基本的绘图概念。
到底谁在负责进行绘制操作?Windows 窗体程序是如何知道何时该进行绘制的?那些绘制代码究竟被放置在哪里?之后,还将介绍图像绘制的双重缓冲区技术,你将会看到它是怎样工作的,怎样通过一个方法来实现缓存和实际显示的图像间的交替。
最后,我们将会探讨”智能无效区域”,实际就是仅仅重绘或者清除应用程序窗体上的无效部分,加快程序的显示和响应速度。
希望这些概念和技术能够引导读者阅读完本文,并且有助于更快和更有效的开发Windows 窗体程序。
Windows 窗体使用GDI+图像引擎,在本文中的所有绘图代码都会涉及使用托管的.Net 框架来操纵和使用Windows GDI+图像引擎。
尽管本文用于基本的窗体绘图操作,但是它同样提供了快速的、有效的且有助于提高程序性能的技术和方法。
所以,在通读本文之前建议读者对.Net框架有个基本的了解,包括Windows 窗体事件处理、简单的GDI+对象譬如Line,Pen和Brush等。
熟悉Visual Basic .Net或者C#编程语言。
概念Windows 应用程序是自己负责绘制的,当一个窗体”不干净”了,也就是说窗体改变了大小,或者部分被其它程序窗体遮盖,或者从最小化状态恢复时,程序都会收到需要绘制的信息。
屏幕无闪烁绘图少将在使用VC++编写某些绘图程序时,你可能会发现,窗口在每次重绘时都会发生闪烁。
特别是在绘制较复杂的图像时,这种闪烁将会更加剧烈。
这里介绍一种防止窗口绘图时闪烁的双缓冲技术。
首先,先解释一下什么叫双缓冲。
双缓冲,即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。
通过这个简短的解释,你是否对双缓冲有了初步的理解?不理解没关系,继续往下看。
双缓冲实现过程如下:1、在内存中创建与画布一致的缓冲区2、在缓冲区画图3、将缓冲区位图拷贝到当前画布上4、释放内存缓冲区举例:对话框中应用双缓冲绘图编译环境:操作系统Windows XP编译工具Visual C++ v6.01、新建一个基于对话框的应用程序在对话框中添加一个picture控件更改ID为IDC_FIGURE,在需要绘图的类的头文件中定义几个双缓冲用到的变量CRect rect; // 存储绘图控件的绘图区域CDC *pDC; // 控件的屏幕绘图设备指针CDC memDC; // 内存绘图设备CBitmap memBitmap; // 用于内存绘图的位图CBitmap* pOldBmp; // 备份旧的位图指针CWnd* pWnd; // 绘图控件的指针//添加一个成员函数:void DrawFigure(CDC *pDC);2、在对话框的初始化函数OnInitDialog()中添加以下代码pWnd = GetDlgItem(IDC_FIGURE); // 获得对话框上的picture控件的窗口句柄pWnd->GetClientRect(&rect); // 获取绘制区域pDC = pWnd->GetDC(); // 获得对话框上的picture的设备指针pOldBmp = NULL; // 将旧的位图指针置空// 创建内存绘图设备,使内存位图的DC与控件的DC关联memDC.CreateCompatibleDC(pDC);memBitmap.CreateCompatibleBitmap(pDC,rect.right,rect.bottom);pOldBmp = memDC.SelectObject(&memBitmap);SetTimer(1,100,NULL); // 启动定时器3、DrawFigure函数的定义void CMyDialog::DrawFigure(CDC *pDC) //CMyDialog,是需要绘图的对话框类{//声明画笔对象,及画笔指针CPen pen,*oldpen;//画一个矩形区域pDC->Rectangle(&rect);pDC->MoveTo(0,0);int x,y;for(int i =0; i<1000; i++){pen.CreatePen(PS_SOLID,1, RGB(rand()%255,rand()%255,rand()%255)); oldpen = pDC->SelectObject(&pen);x = rand()%rect.Width();y = rand()%rect.Height();pDC->LineTo(x,y);pDC->SelectObject(oldpen);pen.DeleteObject();}}4、添加Paint事件,在Paint事件处理代码中添加代码pDC->BitBlt(rect.left,rect.top,rect.right,rect.bottom,&memDC,0,0,SRCCOPY);5、添加Timer事件,在Timer事件处理函数中添加代码//在位图中画图片DrawFigure(&memDC);//使屏幕刷新OnPaint();6、在Close事件中添加代码,用来删除创建的对象,以释放内存memDC.SelectObject(pOldBmp);memDC.DeleteDC();memBitmap.DeleteObject();至此,对话框中应用双缓冲的简单例子就介绍完毕若在View视图中应用双缓冲,需要将OnEraseBkgnd(CDC* pDC)函数中的返回语句改为return TRUE;从而避免背景不同而形成闪烁。
vc双缓冲:VC++双缓冲实现方法疯狂代码 / ĵ:http://VC/Article22436.html 在图形图象处理编程过程中,双缓冲是种基本技术我们知道,如果窗体在响应WM_PAINT消息时候要进行复杂图形处理那么窗体在重绘时由于过频刷新而引起闪烁现象解决这问题有效思路方法就是双缓冲技术 窗体在刷新时总要有个擦除原来图象过程OnEraseBkgnd它利用背景色填充窗体绘图区然后在新绘图代码进行重绘这样擦写造成了图象颜色反差当WM_PAINT响应很频繁时候这种反差也就越发明显于是我们就看到了闪烁现象我们会很自然想到避免背景色填充是最直接办法但是那样话窗体上会变团糟每次绘制图象时候都没有将原来图象清除造成了图象残留于是窗体重绘时画面往往会变乱 7 8糟所以单纯禁止背景重绘是不够我们还要进行重新绘图但要求速度很快于是我们想到了使用BitBlt它可以支持图形块复制速度很快我们可以先在内存中作图然后用此将做好图复制到前台同时禁止背景刷新这样就消除了闪烁以上也就是双缓冲绘图基本思路、普通思路方法: 先按普通做图思路方法进行编程即在视类OnDraw中添加绘图代码在此我们绘制若干同心圆代码如下: CBCDoc* pDoc = GetDocument;ASSERT_VALID(pDoc);CPo ptCenter;CRect rect,ellipseRect;GetClientRect(&rect);ptCenter = rect.CenterPo;for( i=20;i>0;i--){ellipseRect.SetRect(ptCenter,ptCenter);ellipseRect.InflateRect(i*10,i*10);pDC->Ellipse(ellipseRect);}编译运行尝试改变窗口大小可以发现闪烁现象2、双缓冲思路方法: 在双缓冲思路方法中首先要做是屏蔽背景刷新背景刷新其实是在响应WM_ERASEBKGND消息我们在视类中添加对这个消息响应可以看到缺省代码如下:BOOL CMYView::OnEraseBkgnd(CDC* pDC){CView::OnEraseBkgnd(pDC);}是父类OnEraseBkgnd我们屏蔽此只须直接 TRUE;即可 下面是内存缓冲作图步骤CPo ptCenter;CRect rect,ellipseRect;GetClientRect(&rect);ptCenter = rect.CenterPo;CDC dcMem; //用于缓冲作图内存DCCBitmap bmp; //内存中承载临时图象位图dcMem.CreateCompatibleDC(pDC); //依附窗口DC创建兼容内存DCbmp.CreateCompatibleBitmap(pDC,rect.Width,rect.Height);//创建兼容位图 dcMem.SelectObject(&bmp); //将位图选择进内存DC//按原来背景填充客户区不然会是黑色dcMem.FillSolidRect(rect,pDC->GetBkColor);for( i=20;i>0;i--) //在内存DC上做同样同心圆图象{ellipseRect.SetRect(ptCenter,ptCenter);ellipseRect.InflateRect(i*10,i*10);dcMem.Ellipse(ellipseRect);}pDC->BitBlt(0,0,rect.Width,rect.Height,&dcMem,0,0,SRCCOPY);//将内存DC上图象拷贝到前台dcMem.DeleteDC; //删除DCbm.DeleteObject; //删除位图由于复杂画图操作转入后台我们看到是速度很快复制操作自然也就消除了闪烁现象注意:bmp.CreateCompatibleBitmap(pDC,rect.Width,rect.Height);这里面CreateCompatibleBitmap第个参数不能用dcMem这样话创建是黑白位图如果你要创建彩色位图需要用pDC它用来创建了内存DC. 详细请见下面MSDN:When a memory device context is created, it initially has a 1-by-1 monochrome bitmap selected o it. If this memory device context is used in CreateCompatibleBitmap, the bitmap that is created is a monochrome bitmap. To create a color bitmap, use the hDC that was used to create the memory device context, as shown in the following code:HDC memDC = CreateCompatibleDC ( hDC );HBITMAP memBM = CreateCompatibleBitmap ( hDC, nWidth, nHeight );SelectObject ( memDC, memBM ); 2009-2-12 3:59:02疯狂代码 /。
利用双缓存技术提高GDI+的绘图效果摘要在进行图像处理的软件中,一般的图形处理程序需要大量的绘图操作,首要解决的就是绘图的效率和效果问题:前者表现在性能方面,是否有延迟,一个小小的交互操作是否要等上几秒才能看到结果;后者表现在重新绘制或刷新的过程中出现闪烁?本文主要介绍GDI+程序中引用双缓冲技术以解决重绘时出现的闪烁问题—提高绘图效果。
关键字:GDI+;双缓存;快速;缓存位图;效果引言在使用GDI+的实际的绘图中,绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面重新刷新一次以维持窗口正常显示。
这样的效率非常低,不仅达不到性能要求,而且还可能会出现闪烁的现象。
刷新过程中会导致所有图元重新绘制,而各个图元的重绘操作并不会导致Paint事件发生,因此窗口的每一次刷新只会调用Paint事件一次。
窗口刷新一次的过程中,每一个图元的重绘都会立即显示到窗口,因此整个窗口中,只要是图元所在的位置,都在刷新,而刷新的时间是有差别的,闪烁现象自然会出现。
所以说,此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少,而在于各个图元的重绘。
根据以上分析可知,当图元数目不多时,窗口刷新的位置也不多,窗口闪烁效果并不严重;当图元数目较多时,绘图窗口进行重绘的图元数量增加,绘图窗口每一次刷新都会导致较多的图元重新绘制,窗口的较多位置都在刷新,闪烁现象自然就会越来越严重。
特别是图元比较大绘制时间比较长时,闪烁问题会更加严重,因为时间延迟会更长。
1、双缓存技术为了解决画图的效率问题,我们引进了双缓存技术,双缓冲技术早在GDI的程序开发中,就已经开始使用。
在普通的绘图模式下,图像处理程序是按照设计的顺序,将被绘制的元素一个接一个地在目标设备上进行绘制。
何谓“双缓冲”?它的基本原理就是:先在内存中开辟一块虚拟画布,然后将所有需要画的图形先画在这块“虚拟画布”上,最后在一次性将整块画布画到真正的窗体上。
android surfaceview双缓冲机制原理
在Android中,SurfaceView是一个特殊的视图,它可以在后台线程中绘制,而且在主线程中也可以更新UI,这使得它成为绘制动画和游戏的理想选择。
SurfaceView的双缓冲机制是为了防止闪烁和卡顿现象。
在绘制过程中,通常会发生不可避免的计算和IO延迟,这可能会导致屏幕绘制停顿。
如果没有双缓冲机制,在主线程更新UI的同时,绘制的结果也将立即呈现在前台界面上,这可能导致视觉上的不连续性和闪烁。
双缓冲机制会维护两个Surface对象,一个在前台(呈现在屏幕上),另一个在后台(在后台绘制)。
在绘制之前,将要绘制的内容先绘制到后台Surface上,然后前后交换两个Surface,这样前台Surface就显示了最新的绘制结果。
整个双缓冲机制的过程大致如下:
1. 创建两个Surface对象,分别为前台和后台Surface。
2. 获取后台Surface上的Canvas对象。
3. 在Canvas对象上绘制要显示的内容。
4. 将后台Surface上的内容复制到前台Surface。
5. 显示前台Surface上的内容。
在Java编写具有连贯变化的窗口程序时,通常的办法是在子类中覆盖父类的paint(Graphics)方法,在方法中使用GUI函数实现窗口重绘的过程。
连贯变换的窗口会不断地调用update(Graphics)函数,该函数自动的调用paint(Graphics)在Java编写具有连贯变化的窗口程序时,通常的办法是在子类中覆盖父类的paint(Graphics)方法,在方法中使用GUI函数实现窗口重绘的过程。
连贯变换的窗口会不断地调用update(Graphics)函数,该函数自动的调用paint(Graphics)函数。
这样就会出现闪烁的情况。
为了解决这一问题,可以应用双缓冲技术。
可以通过截取上述过程,覆盖update(Graphics)函数,在内存中创建一个与窗口大小相同的图形,并获得该图形的图形上下文(Graphics),再将图片的图形上下文作为参数调用paint(Graphics)函数(paint(Graphics)中的GUI函数会在图片上画图),再在update(Graphics)函数调用drawImage函数将创建的图形直接画在窗口上。
01.Image ImageBuffer = null;02.Graphics GraImage = null;03.04.public void update(Graphics g){ //覆盖update方法,截取默认的调用过程05. ImageBuffer = createImage(this.getWidth(), this.getHeight());//创建图形缓冲区06. GraImage = ImageBuffer.getGraphics(); //获取图形缓冲区的图形上下文07. paint(GraImage); //用paint方法中编写的绘图过程对图形缓冲区绘图08. GraImage.dispose(); //释放图形上下文资源09. g.drawImage(ImageBuffer, 0, 0, this); //将图形缓冲区绘制到屏幕上10.}11.12.public void paint(Graphics g){ //在paint方法中实现绘图过程13. g.drawLine(0, 0, 100, 100);14.}Image ImageBuffer = null;Graphics GraImage = null;public void update(Graphics g){ //覆盖update方法,截取默认的调用过程ImageBuffer = createImage(this.getWidth(), this.getHeight()); //创建图形缓冲区GraImage = ImageBuffer.getGraphics(); //获取图形缓冲区的图形上下文paint(GraImage); //用paint方法中编写的绘图过程对图形缓冲区绘图GraImage.dispose(); //释放图形上下文资源g.drawImage(ImageBuffer, 0, 0, this); //将图形缓冲区绘制到屏幕上}public void paint(Graphics g){ //在paint方法中实现绘图过程g.drawLine(0, 0, 100, 100);}因为大部分绘图过程是在内存中进行,所以有效地消除了闪烁。
一、闪烁问题。
闪烁问题在MFC窗体中经常见到。
在网上碰到这些问题的层次不穷,解决方法也是多样的!但是最经典也最耐用的还是靠用双缓存解决!首先让我们来了解一下,为什么会产生屏幕闪烁问题:闪烁可以这样定义:当后面一幅图像以很快的速度画在前面一幅图像上时,在后面图像显示前,你可以很快看到前面那一个图像,这样的现象就是闪烁。
我认为,闪烁会让使用者对程序很不满,原因是:如果用户接口编码如此糟糕,那么程序的其他部分呢,如何能相信数据的正确性呢?一个具有平滑,快速相应的程序会给用户带来信心,这个道理很简单。
程序出现闪烁可以由多种形式造成,最常见的原因是窗口大小发生改变时,其内容重画造成闪烁。
仅仅画一次这是一个黄金法则,在任何计算机(Windows或者你使用的任何操作系统)上处理画法逻辑都需要遵循,即永远不要将同一像素画两次。
一个懒惰的程序员常常不愿意在画法逻辑上投入过多精力,而是采用简单的处理逻辑。
要避免闪烁,就需要确保不会出现重复绘制的情况发生。
现在,WIndows和计算机还是很笨的,除非你给他们指令,否则他们不会做任何事情。
如果闪烁的现象发生,那是因为你的程序刻意地多绘制了屏幕的某些区域造成的. 这个现象可能是因为一些明确的命令,或者一些被你忽视了的地方。
如果程序有闪烁的现象出现,你需要你知道如何找到好的方案去解决这个问题。
WM_ERASEBKGND通常,首先需要怀疑的是WM_ERASEBKGND消息。
当一个窗口的背景需要被擦除时,这个消息会被发送。
这是因为窗口的绘画通常经历了两个过程WM_ERASEBKGND: 清除背景WM_PAINT: 在上面绘制内容这两个过程让窗体在绘制内容时变得很简单,即:每次当收到WM_PAINT消息时,你知道已经有了一个新画布等待去绘制。
然而,画窗口两次(一次是通过WM_ERASEBKGND画背景,另外一次是WM_PAINT)将会导致窗口出现比较糟糕的闪烁现象。
只要看看标准的编辑框-打开Windows的写字板并改变窗口大小,就可以看到那种闪烁的效果。
doublebuffered 原理
DoubleBuffered是一种减少控件刷新导致闪烁的技术。
它的原理是使用辅助缓冲区来重绘控件的图面,从而减少或避免闪烁。
具体来说,显示缓冲区是和显示器一起的,显示器只负责从显示缓冲区取数据显示。
当需要更新控件文本显示时,DoubleBuffered 会将更新的图形数据写入缓冲区,然后快速写入显示的表面内存。
这种快速切换显示图形内存的方式,通常可以减少可能发生的闪烁。
DoubleBuffered在图形图像显示和Winform应用中都有广泛的应用。
在不同的应用场景中,DoubleBuffered的实现方式和效果可能会有所不同,但基本原理都是通过使用辅助缓冲区来提高图形更新的速度和质量。
1.添加图片控件
2.对话框初始化函数(或其他函数中获得控件的绘图指针)
/*******获得图片区绘图指针*******/
pWnd = GetDlgItem(IDC_STA TIC_SHOWPICTURE);//获得控件窗口
//获得控件大小,PictureRect是全局变量,用于存放控件大小
pWnd->GetClientRect(&PictureRect);
pDC = pWnd->GetDC();//定义pDC为控件绘图设备指针
3.内存中绘图并将图形复制到当前区域中
void CShockSensorConfiguratorDlg::OnPaint()
{
…;
else
{
if(m_iWitchPicture==3)//如果要显示的是第三张也就是自己绘制的图形
{
CBitmap memBitmap;
CBitmap* pOldBmp = NULL;
memDC.CreateCompatibleDC(pDC);
memBitmap.CreateCompatibleBitmap(pDC,PictureRect.right,PictureRect.bottom);
pOldBmp = memDC.SelectObject(&memBitmap);
memDC.BitBlt(PictureRect.left,PictureRect.top,PictureRect.right,PictureRect.bottom,pDC, 0,0,SRCCOPY);
memDC.FillSolidRect(1,1,PictureRect.right-2,PictureRect.bottom-2,RGB(255,255,255));
DrawPicture(&memDC);//自绘函数,传入的是内存指针,也就是在内存中绘图pDC->BitBlt(PictureRect.left,PictureRect.top,PictureRect.right,PictureRect.bottom,&m emDC,0,0,SRCCOPY);
memDC.SelectObject(pOldBmp);
memDC.DeleteDC();
memBitmap.DeleteObject();
}
else if(m_iWitchPicture==2)
{
CDC *pDC1;
pDC1=GetDC();
CBitmap m_bmpBK;
m_bmpBK.LoadBitmap(IDB_BITMAP_OPA);
BITMAP bitMap;//位图结构体
m_bmpBK.GetBitmap(&bitMap);//获得原图片尺寸
CDC dcMem; //目标DC
dcMem.CreateCompatibleDC(pDC1); //创建与dc兼容的内存DC
dcMem.SelectObject(&m_bmpBK);//将位图对象m_bmpBK选入内存DC
pDC1->StretchBlt(PictureRectPt.left,PictureRectPt.top,PictureRect.Width(),PictureRect. Height(),&dcMem,0,0,bitMap.bmWidth,bitMap.bmHeight,SRCCOPY);
dcMem.DeleteDC();
m_bmpBK.DeleteObject();
}
CPaintDC dc(this); //这个语句如果被删除会一直重绘
/*****************改变Group Box的背景颜色*****************/
CRect rect,rect1;
GetDlgItem(IDC_CONFIG1)->GetWindowRect(&rect); //获得控件的大小
ScreenToClient(&rect);
rect1.left=rect.left;
rect1.top=rect.top+7;
rect1.right=rect.right;
rect1.bottom=rect.bottom;
dc.FillSolidRect(rect1,CONFIGRECT);
}
}
Invalidate(FALSE);
UpdateWindow();
UpdateData(FALSE);//实现重绘,如果在重绘很频繁的时候双缓冲技术可以解决闪烁问题。