Java双缓冲技术

c#小王子 c#小王子 2022-01-24 602 Java

Java 的强大特性让其在游戏编程和多媒体动画处理方面也毫不逊色。在 Java 游戏编程和动画编程中最常见的就是对于屏幕闪烁的处理。本文从 J2SE 的一个再现了屏幕闪烁的 Java Appilication 简单动画实例展开,对屏幕闪烁的原因进行了分析,找出了闪烁成因的关键:update(Graphics g)函数对于前端屏幕的清屏。由此引出消除闪烁的方法——双缓冲。双缓冲是计算机动画处理中的传统技术,在用其他语言编程时也可以实现。本文从实例出发,着重介绍了用双缓冲消除闪烁的原理以及双缓冲在 Java 中的两种常用实现方法(即在update(Graphics g)中实现和在 paint(Graphics g)中实现),以期读者能对双缓冲在 Java 编程中的应用能有个较全面的认识。


关键词:Java 消除闪烁 双缓冲


一、问题的引入

在编写 Java 多媒体动画程序或用 Java 编写游戏程序的时候,我们得到的动画往往存在严重的闪烁(或图片断裂)。这种闪烁虽然不会给程序的效果造成太大的影响,但着实有违我们的设计初衷,也给程序的使用者造成了些许不便。闪烁到底是什么样的呢?下面的 JavaApplication 再现了这种屏幕闪烁的情况:


//代码段一[①],闪烁的再现


import java.awt.*;
import java.awt.event.*;
public class DoubleBuffer extends Frame//主类继承 Frame 类
{
public paintThread pT;//绘图线程
public int ypos=-80; //小圆左上角的纵坐标
public DoubleBuffer()//构造函数{
pT=new paintThread(this);
this.setResizable(false);
this.setSize(300,300); //设置窗口的首选大小
this.setVisible(true); //显示窗口
pT.start();//绘图线程启动
}
public void paint(Graphics scr) //重载绘图函数
{
scr.setColor(Color.RED);//设置小圆颜色
scr.fillOval(90,ypos,80,80); //绘制小圆
}
public static void main(String[] args)
{
DoubleBuffer DB=new DoubleBuffer();//创建主类的对象
DB.addWindowListener(new WindowAdapter()//添加窗口关闭处理函数
{
public void windowClosing(WindowEvent e)
{System.exit(0);
}});
} 
}
class paintThread extends Thread//绘图线程类
{
DoubleBuffer DB;
public paintThread(DoubleBuffer DB) //构造函数
{
this.DB=DB;
}
public void run()//重载 run()函数
{
while(true)//线程中的无限循环
{
try{
sleep(30); //线程休眠 30ms
}catch(InterruptedException e){}
DB.ypos+=5; //修改小圆左上角的纵坐标if(DB.ypos>300) //小圆离开窗口后重设左上角的纵坐标
DB.ypos=-80;
DB.repaint();//窗口重绘
} 
} 
}


编译、运行上述例子程序后,我们会看到窗体中有一个从上至下匀速运动的小圆,但仔细观察,你会发现小圆会不时地被白色的不规则横纹隔开,即所谓的屏幕闪烁,这不是我们预期的结果。这种闪烁是如何出现的呢?


首先我们分析一下这段代码。DoubleBuffer 的对象建立后,显示窗口,程序首先自动调用重载后的paint(Graphics g)函数,在窗口上绘制了一个小圆,绘图线程启动后,该线程每隔 30ms 修改一下小圆的位置,然后调用 repaint()函数。


注意,这个 repaint()函数并不是我们重载的,而是从 Frame 类继承而来的。它先调用 update(Graphics g)函数,update(Graphics g)再调用 paint(Graphics g)函数[②]。问题就出在 update(Graphics g)函数,我们来看看这个函数的源代码:


public void update(Graphics g)
{
if (isShowing())
{
if (! (peer instanceof LightweightPeer))
{g.clearRect(0, 0, width, height);
}
paint(g);
} 
}


以上代码的意思是:(如果该组件是轻量组件的话)先用背景色覆盖整个组件,然后再调用paint(Graphics g)函数,重新绘制小圆。这样,我们每次看到的都是一个在新的位置绘制的小圆,前面的小圆都被背景色覆盖掉了。这就像一帧一帧的画面匀速地切换,以此来实现动画的效果。

但是,正是这种先用背景色覆盖组件再重绘图像的方式导致了闪烁。在两次看到不同位置小圆的中间时刻,总是存在一个在短时间内被绘制出来的空白画面(颜色取背景色)。但即使时间很短,如果重绘的面积


较大的话花去的时间也是比较可观的,这个时间甚至可以大到足以让闪烁严重到让人无法忍受的地步。另外,用 paint(Graphics g)函数在屏幕上直接绘图的时候,由于执行的语句比较多,程序不断地改变窗体中正在被绘制的图象,会造成绘制的缓慢,这也从一定程度上加剧了闪烁。

就像以前课堂上老师用的旧式的幻灯机,放完一张胶片,老师会将它拿下去,这个时候屏幕上一片空白,直到放上第二张,中间时间间隔较长。当然,这不是在放动画,但上述闪烁的产生原因和这很类似。


二、问题的解决

知道了闪烁产生的原因,我们就有了更具针对性的解决闪烁的方案。已经知道 update(Graphics g)是造成

闪烁的主要原因,那么就从这里入手。


(1) 尝试这样重载 update(Graphics g)函数(基于代码段一修改):


public void update(Graphics scr)
{paint(scr);
}


以上代码在重绘小圆之前没有用背景色重绘整个画面,而是直接调用 paint(Graphics g)函数,这就从根本上避免了上述的那幅空白画面。

看看运行结果,闪烁果然消除了!但是更大的问题出现了,不同时刻绘制的小圆重叠在一起形成了一条线!这样的结果我们更不能接受了。为什么会这样呢?仔细分析一下,重载后的 update(Graphics g)函数中没有了任何清屏的操作,每次重绘都是在先前已经绘制好的图象的基础上,当然会出现重叠的现象了。


2)使用双缓冲:

这是本文讨论的重点。所谓双缓冲,就是在内存中开辟一片区域,作为后台图象,程序对它进行更新、修改,绘制完成后再显示到屏幕上。


1、重载 paint(Graphics g)实现双缓冲:


这种方法要求我们将双缓冲的处理放在 paint(Graphics g)函数中,那么具体该怎么实现呢?先看下面的代码(基于代码段一修改):

在 DoubleBuffer 类中添加如下两个私有成员:


private Image iBuffer;
private Graphics gBuffer;
重载 paint(Graphics scr)函数:
public void paint(Graphics scr)
{
if(iBuffer==null)
{iBuffer=createImage(this.getSize().width,this.getSize().height);
gBuffer=iBuffer.getGraphics();
}
gBuffer.setColor(getBackground());
gBuffer.fillRect(0,0,this.getSize().width,this.getSize().height);
gBuffer.setColor(Color.RED);
gBuffer.fillOval(90,ypos,80,80);
scr.drawImage(iBuffer,0,0,this);
}


分析上述代码:我们首先添加了两个成员变量 iBuffer 和 gBuffer 作为缓冲(这就是所谓的双缓冲名字的来历)。在 paint(Graphics scr)函数中,首先检测如果 iBuffer 为 null,则创建一个和屏幕上的绘图区域大小一样的缓冲图象,再取得 iBuffer 的 Graphics 类型的对象的引用,并将其赋值给 gBuffer,然后对 gBuffer 这个内存中的后台图象先用 fillRect(int,int,int,int)清屏,再进行绘制操作,完成后将 iBuffer 直接绘制到屏幕上。


这段代码看似可以完美地完成双缓冲,但是,运行之后我们看到的还是严重的闪烁!为什么呢?回想上文所讨论的,问题还是出现在 update(Graphics g)函数!这段修改后的程序中的 update(Graphics g)函数还是我们从父类继承的。在 update(Graphics g)中,clearRect(int,int,int,int)对前端屏幕进行了清屏操作,而在 paint(Graphics g)中,对后台图象又进行了清屏操作。那么如果保留后台清屏,去掉多余的前台清屏应该就会消除闪烁。所以,我们只要按照(1)中的方法重载 update(Graphics g)即可:


【下载地址】

百度网盘链接:https://pan.baidu.com/s/1ohusePeSfKhSlBiMeqnMlw

提取码:cbfu


相关文章


使用-JFreeChart来创建基于web的图表

使用-JFreeChart来创建基于web的图表

XStream使用文档

XStream使用文档

WebService发布过程及常见问题

WebService发布过程及常见问题

webpack实战入门进阶调优分享

webpack实战入门进阶调优分享

weblogic调优参数及监控指标

weblogic调优参数及监控指标

weblogic节点管理

weblogic节点管理

weblogic管理控制台概述

weblogic管理控制台概述

weblogic-部署和启动

weblogic-部署和启动

WebLogic-Server-性能及调优-调优-Java-虚拟机

Java 虚拟机(Java virtual machine,简称 JVM)是一种虚拟“执行引擎”实例,可在微处理器上执行 Java 类文件中的字节码。调整 JVM 的方式会影响 Weblogic Server 和应用程序的性能。

Velocity用户教程

Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。

Velocity用户手册

Velocity 用户手册是帮助页面设计者和内容提供者认识 Velocity 和其简单而功能强大的脚本语言――Velocity 模板语言(VTL)。在手册上的许多例子,都是用 Velocity 插入动态的内容到网页上,但是所有的 VLT 例子都能应用到其他的页面和模板中。


文章热度: 166291
文章数量: 333
推荐阅读

FlashFXP绿色版网盘下载,附激活教程 1781

FlashFxp百度网盘下载链接:https://pan.baidu.com/s/1MBQ5gkZY1TCFY8A7fnZCfQ。FlashFxp是功能强大的FTP工具

Adobe Fireworks CS6 Ansifa绿色精简版网盘下载 1564

firework可以制作精美或是可以闪瞎眼的gif,这在广告领域是需要常用的,还有firework制作下logo,一些原创的图片还是很便捷的,而且fireworks用法简单,配合dw在做网站这一块往往会发挥出很强大的效果。百度网盘下载链接:https://pan.baidu.com/s/1fzIZszfy8VX6VzQBM_bdZQ

navicat for mysql中文绿色版网盘下载 1623

Navicat for Mysql是用于Mysql数据库管理的一款图形化管理软件,非常的便捷和好用,可以方便的增删改查数据库、数据表、字段、支持mysql命令,视图等等。百度网盘下载链接:https://pan.baidu.com/s/1T_tlgxzdQLtDr9TzptoWQw 提取码:y2yq

火车头采集器(旗舰版)绿色版网盘下载 1707

火车头采集器是站长常用的工具,相比于八爪鱼,简洁好用,易于配置。火车头能够轻松的抓取网页内容,并通过自带的工具对内容进行处理。站长圈想要做网站,火车头采集器是必不可少的。百度网盘链接:https://pan.baidu.com/s/1u8wUqS901HgOmucMBBOvEA

Photoshop(CS-2015-2023)绿色中文版软件下载 1823

安装文件清单(共46G)包含Window和Mac OS各个版本的安装包,从cs到cc,从绿色版到破解版,从安装文件激活工具,应有尽有,一次性打包。 Photoshop CC绿色精简版 Photoshop CS6 Mac版 Photoshop CC 2015 32位 Photoshop CC 2015 64位 Photoshop CC 2015 MAC版 Photoshop CC 2017 64位 Adobe Photoshop CC 2018 Adobe_Photoshop_CC_2018 Photoshop CC 2018 Win32 Photoshop CC 2018 Win64

知之

知之平台是全球领先的知识付费平台。提供各个领域的项目实战经验分享,提供优质的行业解决方案信息,来帮助您的工作和学习

使用指南 建议意见 用户协议 友情链接 隐私政策 Powered by NOOU ©2020 知之