Java2D: 硬件加速 - 第一部分 - 非恒定图像类:Volatile Image

原文地址:[b][url=http://www.javalobby.org/java/forums/m91823967.html#91823967]Java2D: Hareware Accelerating - Part1 - Volatile Images[/url][/b]

Java 1.4在Java 2D的功能方面引入了对硬件加速的支持。毫无疑问,硬件加速非常有用——不过有效的使用[url=http://java.sun.com/j2se/1.5.0/docs/api/java/awt/image/VolatileImage.html]java.awt.image.VolatileImage[/url]至少要比使用传统的“图像缓冲”机制要复杂一些。仅当你在自行实现复杂的Java 2D渲染的时候,使用低级的“硬件加速”功能才是的确很重要的。如果你只是在使用比方说Swing里预编译的控件的话,那么这个技巧的大部分都不太合适。但是对那些Java的2D游戏编程的人,或者那些操作大量图形,如图表、图解的人来说,就非常有用了。

我假设这个技巧的读者至少熟悉双缓冲的概念——如果你不熟悉,请读[url=http://java.sun.com/docs/books/tutorial/2d/images/doublebuffering.html]这里[/url]。简短地说,双缓冲就是把渲染的过程推迟在“画面外”的缓冲里,然后快速地把缓冲复制到画面设备上,从而提高了画质(画面渲染得更柔和)。标准双缓冲的简单实现方式(没有硬件加速),代码基本如下:

// 也可以扩展其它类 - 不过通常会选择Canvas。
public class CustomGUI extends Canvas {

private Image offscreenImage;
private Graphics offscreenGraphics;
private Dimension offscreenDimension;

// ...
public void paint(Graphics g) {
Dimension currentSize = getSize();
if(offscreenImage == null || !currentSize.equals(offscreenDimension)) {
// 调用java.awt.Component.createImage(...)方法以获得Image对象
offscreenImage = createImage(currentSize.width, currentSize.height);
offscreenGraphics = offscreenImage.getGraphics();
offscreenDimension = currentSize;
}

// 渲染代码放在这里(使用offscreenGraphics的Graphics对象)
// 算法假设背景会被重新填充因为这里重用了Image对象
// (否则的话要从之前的渲染中取回背景)
offscreenGraphics.setColor(Color.WHITE);
offscreenGraphics.fillRect(0, 0, offscreenDimension.width, offscreenDimension.height);
offscreenGraphics.setColor(Color.BLACK);
offscreenGraphics.drawLine(0, 0, 10, 10); // 任意的渲染逻辑

// 把缓冲画回主Graphics对象
g.drawImage(offscreenImage, 0, 0, this);
}

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


这类“双缓冲”的情况是使用硬件加速最好的例子。然而现在问题变成了,如何使用[url=http://java.sun.com/j2se/1.5.0/docs/api/java/awt/image/VolatileImage.html]java.awt.image.VolatileImage[/url]类来加速这里的代码?哦,以下的是改编自[b]VolatileImage[/b]的Javadoc和[url=http://java.sun.com/j2se/1.4/pdf/VolatileImage.pdf]Java关于volatile image的白皮书[/url](深入理解阅读的强烈推荐)——上面那个类的新版本使用了VolatileImage做渲染——不用着急,我稍后会解释这段“离奇”的代码:

// 也可以扩展其它类 - 不过通常会选择Canvas。
public class CustomGUI extends Canvas {
private VolatileImage volatileImg;

// ...

public void paint(Graphics g) {
// 创建硬件加速图像
createBackBuffer();

// 主渲染循环。VolatileImage对象可能失去内容。
// 这个循环会不断渲染(如果需要的话并制造)VolatileImage对象
// 直到渲染过程圆满完成。
do {

// 为该控件的Graphics配置验证VolatileImage的有效性。
// 如果VolatileImage对象不能匹配GraphicsConfiguration
// (换句话说,硬件加速不能应用在新设备上)
// 那我们就重建它。
GraphicsConfiguration gc = this.getGraphicsConfiguration();
int valCode = volatileImg.validate(gc);

// 以下说明设备不匹配这个硬件加速Image对象。
if(valCode==VolatileImage.IMAGE_INCOMPATIBLE){
createBackBuffer(); // 重建硬件加速Image对象。
}

Graphics offscreenGraphics = volatileImg.getGraphics();
offscreenGraphics.setColor(Color.WHITE);
offscreenGraphics.fillRect(0, 0, getWidth(), getHeight());
offscreenGraphics.setColor(Color.BLACK);
offscreenGraphics.drawLine(0, 0, 10, 10); // 任意的渲染逻辑

// 把缓冲画回主Graphics对象
g.drawImage(volatileImg, 0, 0, this);
// 检查内容是否丢失
} while(volatileImg.contentsLost());
}

// 以下创建新的VolatileImage对象
private void createBackBuffer() {
GraphicsConfiguration gc = getGraphicsConfiguration();
volatileImg = gc.createCompatibleVolatileImage(getWidth(), getHeight());
}

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

呼——越来越复杂了。长话短说,VolatileImage只不过就是像名字所暗示的那样——易变化。VolatileImage可以被认为是在非标准内存(就是说显卡内存)中表达图像。这使得它们依赖于下层的硬件(显卡)特征,不是所有的时候都被激活,所以图像也可能或可能不被应用(表现在显卡没有使用的内存上)。这正是执行VolatileImage.IMAGE_INCOMPATIBLE检查的原因。我们要求VolatileImage对象验证自身在当前控件的GraphicsConfiguration中的有效性(请记住GraphicsConfiguration其核心代表了我们控件的目标“设备”),如果不匹配,我们就为当前GraphicsConfiguration(也就是当前显卡实例)重建VolatileImage。

除了外部的“有效性”检测之外,我们还恶心地(并罕见地)使用了do-while循环。有两个理由可以说明为何所有的逻辑被包裹在do-while循环中。第一,在我们已经开始渲染VolatileImage对象之后,图像可能再次变得不匹配。这种情况下,contentsLost()方法将返回true以让我们知道出问题了。第二,某些平台(我不指名)可能会出现显示内存“丢失”(BIU!哪去了?),这种情况需要你重写这块内存的所有数据。这种情况下VolatileImage.validate(...)返回的valCode实际上是VolatileImage.IMAGE_RESTORED(一共有三种结果——IMAGE_OK,IMAGE_RESTORED,IMAGE_INCOMPATIBLE)。而上面的代码的方案中,这个返回类型不需要显式地处理。通常,如果图像内容[b]在上一次调用VolatileImage.validate(...)之后[/b]因为上述原因丢失,那么VolatileImage.contentsLost()返回true。也由于我强调的这种区别,我们的代码仅仅在每次循环中执行两次检验:在渲染逻辑之前调用VolatileImage.validate()(确保我们的图像对象可用),以及渲染逻辑之后的VolatileImage.contentsLost()调用,以确定我们上一次验证之后的图像都还在(表明我们调用validate之后所有的渲染逻辑都如预期所致)。

不要转台,我明天将秀给你看如何使用java.awt.image.BufferStrategy类隐藏这些疯狂的代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值