Java游戏开发中怎样才能获得更快的FPS?
众所周知, Java 应用的运行速度虽然不慢,却也谈不上快,以最新的 JRE1.6 表现来说,至多也就是优胜于一些纯粹的解释型语言,距离 C/C++ 等编译型的执行效率还有一定差距。 平心而论,如果我们使用 Java 去制作一些简单的桌面应用,那么目前 Java 组件的绘图速度勉强还能接受;但如果用 Java 来进行游戏开发,尤其是制作一些需要高 FPS 才能满足的动态效果,就必须利用技术手段对其加以优化,解决其绘图效率问题,其它才有的谈。 什么是 FPS ? 这里所说的 FPS ,并非指射击游戏,而是指俗称的“帧率”( Frames per Second ,缩写: FPS )或“赫兹”( Hz )。笔者在以前的博文中曾给出过专门的解释及 Java 实现示例,此处不再赘述。 受到人类眼球的生理结构制约,通常情况下只要我们所见画面高于每秒 16 帧,就会认为画面是连贯的,生物学上称此现象为视觉暂留,这也就是为什么电影胶片是一格一格拍摄出来,然而我们却认为它是连续的一段。当然,所谓的 16 帧也只是一个中值,并非放之四海而皆准,根据具体视觉对象不同,帧率的具体标准会有所变化。在最坏情况下,动画不低于每秒 12 帧,现代电影不低于 24 帧,动作游戏不低于 30 帧,在人眼中的画面才是连续不间断的。 总之, FPS 数值越高则画面流畅度便越高,越低则越显停滞,要提高 Java 游戏运行效率,那么焦点就必然要集中在如何提高程序的 FPS 之上。 我们该从什么地方入手,才能提高 FPS ? Java 绘图最终要通过 native ,这是地球人都知道的事情。这意味着除非我们学习 SWT 重写本地图形接口,否则显示部分我们无法干涉;这样便决定了我们对 FPS 的优化必然要集中在本地图形系统调用之前,而非之后。也就是说,提高 Java 应用的 FPS ,还要老生常谈的从 Image 与 Graphics 及其相关子类入手。 那么,对这些尽人皆知的 Java 绘图组件,我们究竟能改进那些部分呢? 我在不久前发布的博文《 Java 游戏开发中应始终坚持的 10 项基本原则》中,曾经就如何构建一个高效的 Java 游戏发表过一些见解,其中第 7 点“始终双缓冲游戏图像”,它不但是解决 Java 图像闪烁问题的关键所在,而且同样是提高 Java 游戏帧率的制胜法宝之一。是的, Java 绘图机能的优化与改进,关键就在于如何对其缓冲区域进行优化处理。 下面,我将展示三种不同的Java 缓冲绘图方式。 1 、采用 BufferedImage 对象进行缓冲 这种方法是最简单,同时也是最常用的双缓冲构建方式,也就是构建一个 BufferedImage 缓冲当前绘图,所有 Graphics 操作在其上进行,仅在需要时才将贴图 paint 于窗体之上,使用上再简单不过,但效率如何呢?文章进行到此处时尚不得而知。 2 、采用 BufferStrategy 构建缓冲区 使用 BufferStrategy 构建缓冲能够获得系统所提供的硬件加速, Java 系统会根据本地环境选择最适合的 BufferStrategy 。要创建 BufferStrategy ,需要使用 createBufferStrategy() 方法告诉系统你所期望的缓冲区数目(通常使用双缓冲,也就是填入“ 2 ”),并使用 getDrawGraphics() 方法在缓冲区之间进行交换,该方法返回下一个要使用的缓冲区。 BufferStrategy 最大的缺点与优点都在于其受本地图形环境影响,既不会出现很快的图形环境跑出很慢的 FPS ,也别指望很慢的图形环境跑出很快的 FPS 。 3 、完全在 BufferedImage 的 DataBuffer 中进行图像处理 每个 BufferedImage 都有一个与之对应得 WritableRaster 对象( getRaster 方法获得),通过它我们获得指定 BufferedImage 的 DataBuffer ( getDataBuffer 方法获得),与方法 1 类似,我们同样构建一个 BufferedImage 缓冲当前所有绘图,所有操作都在其上进行,仅在需要时才将贴图 paint 于窗体之上。但区别在于,由于 DataBuffer 可以转化为描述 BufferedImage 象素点的 int[] , byte[] 或 short[] 等数组集合,因此我们不再使用 Java 提供的 Graphics 对象,而是直接操作像素点进行所有绘图的实现。 但是,这样进行数组操作会快吗? 现在我们为其各自构建三个示例,尽量以比较趋同的处理流程构建,分别测算三种方法的具体效率。 ( PS :写完才突然想起,这台 2002 年买的电脑实在不适合测试 FPS ( -_-||| ),较新机型的 FPS 至少能达到此测试结果的 2 倍以上,特此声明……) ( PS :以下图像素材源自成都汉森信息技术有限公司首页,特此声明) 以下开始将分别对三种方法分别进行绘制 1 名角色、绘制 100 名角色、绘制 1000 名角色的简单 FPS 绘图效率测算。 一、采用 BufferedImage 对象进行缓冲 代码如下: 1、DoubleBufferGameFrame.Java
package test1; import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; /**
* @author chenpeng
* @email:[email]ceponline@yahoo.com.cn[/email]
*/
public class DoubleBufferGameFrame extends Frame implements Runnable { /**
*
*/
private static final long serialVersionUID = 1L; private int width = 480;
private int height = 360;
private DoubleBufferCanvas canvas = null; public DoubleBufferGameFrame() {
super("AWT标准Graphics2D使用测试");
this.setPreferredSize(new Dimension(width + 5, height + 25));
this.requestFocus();
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
canvas = new DoubleBufferCanvas();
this.add(canvas);
this.pack();
this.setResizable(false);
this.setLocationRelativeTo(null);
this.setIgnoreRepaint(true);
Thread thread = new Thread(this);
thread.start();
} public void run() {
for (;;) {
canvas.drawScreen();
}
} public static void main(String[] args) {
DoubleBufferGameFrame frm = new DoubleBufferGameFrame();
frm.setVisible(true);
} } 2、DoubleBufferCanvas.Java package test1; import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.util.Random;
public class DoubleBufferCanvas extends Canvas { /**
*
*/
private static final long serialVersionUID = 1L; private boolean isFPS = true; private boolean initFlag; private int frames = 0; private long totalTime = 0; private long curTime = System.currentTimeMillis(); private long lastTime = curTime; private Random rand = new Random(); private int fps; private Image backImage = ImageUtils.loadImage("back.gif"); private Image hero_idle_0 = ImageUtils.loadImage("idle0_0.png",true); private BufferedImage bufferImage; private Graphics2D graphics2D; public DoubleBufferCanvas() { } public void update(Graphics g) {
paint(g);
} public void paint(Graphics g) {
if (initFlag) {
graphics2D.drawImage(backImage, 0, 0, null);
for (int i = 0; i < 1000; ++i) {
int x = rand.nextInt(480);
int y = rand.nextInt(360);
graphics2D.drawImage(hero_idle_0, x-100, y-100, null);
}
graphics2D.drawString(("当前FPS:" + fps).intern(), 15, 15);
g.drawImage(bufferImage, 0, 0, null);
g.dispose();
}
} private synchronized void initializtion() {
bufferImage = ImageUtils.createImage(getWidth(), getHeight(), true);
graphics2D = bufferImage.createGraphics();
initFlag = true;
} public synchronized void drawScreen() {
getGraphics2D();
repaint();
if (isFPS) {
lastTime = curTime;
curTime = System.currentTimeMillis();
totalTime += curTime - lastTime;
if (totalTime > 1000) {
totalTime -= 1000;
fps = frames;
frames = 0;
}
++frames;
}
Thread.yield();
} public synchronized Graphics2D getGraphics2D() {
if (!initFlag) {
initializtion();
}
return graphics2D;
} }
场景图1 、角色图1 、 FPS 60 - 70
场景图1 、角色图100 、FPS 20 - 25 
场景图1 、角色图1000 、 FPS 13 - 17
二、采用 BufferStrategy 构建缓冲区(双缓冲)
1、 BufferStrategyGameFrame.java package test1;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; /**
* @author chenpeng
* @email: [email]ceponline@yahoo.com.cn[/email]
*/
public class BufferStrategyGameFrame extends Frame implements Runnable { /**
*
*/
private static final long serialVersionUID = 1L; private BufferStrategyCanvas canvas = null; private int width = 480;
private int height = 360;
public BufferStrategyGameFrame() {
super("AWT标准Graphics2D(BufferStrategy)使用测试");
this.setPreferredSize(new Dimension(width + 5, height + 25));
this.requestFocus();
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
canvas = new BufferStrategyCanvas();
this.add(canvas);
this.pack();
this.setResizable(false);
this.setLocationRelativeTo(null);
this.setIgnoreRepaint(true);
Thread thread = new Thread(this);
thread.start();
} public void run() {
for (;;) {
canvas.drawScreen();
}
} public static void main(String[] args) {
BufferStrategyGameFrame frm = new BufferStrategyGameFrame();
frm.setVisible(true);
} } 2、 BufferStrategyCanvas.java package test1; import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.util.Random; public class BufferStrategyCanvas extends Canvas { /**
*
*/
private static final long serialVersionUID = 1L; private boolean isFPS = true; private boolean initFlag; private int frames = 0; private long totalTime = 0; private long curTime = System.currentTimeMillis(); private long lastTime = curTime; private Random rand = new Random(); private int fps; private Image backImage = ImageUtils.loadImage("back.gif"); private Image hero_idle_0 = ImageUtils.loadImage("idle0_0.png", true); private BufferedImage screenImage; final static GraphicsEnvironment environment = GraphicsEnvironment
.getLocalGraphicsEnvironment(); final static GraphicsDevice graphicsDevice = environment
.getDefaultScreenDevice(); final static GraphicsConfiguration graphicsConfiguration = graphicsDevice
.getDefaultConfiguration(); private Graphics canvasGraphics = null; private BufferStrategy bufferImage; private Graphics2D graphics2d; public BufferStrategyCanvas() { } public void createBufferGraphics() {
createBufferStrategy(2);
bufferImage = getBufferStrategy();
screenImage = graphicsConfiguration.createCompatibleImage(getWidth(),
getHeight());
graphics2d = screenImage.createGraphics();
} public synchronized void paintScreen() {
if (initFlag) {
graphics2d.drawImage(backImage, 0, 0, null);
for (int i = 0; i < 1000; ++i) {
int x = rand.nextInt(480);
int y = rand.nextInt(360);
graphics2d.drawImage(hero_idle_0, x - 100, y - 100, null);
}
graphics2d.drawString(("当前FPS:" + fps).intern(), 15, 15);
canvasGraphics = bufferImage.getDrawGraphics();
canvasGraphics.drawImage(screenImage, 0, 0, null);
bufferImage.show();
canvasGraphics.dispose();
}
} private synchronized void initializtion() {
createBufferGraphics();
initFlag = true;
} public synchronized void drawScreen() {
loadGraphics();
paintScreen();
if (isFPS) {
lastTime = curTime;
curTime = System.currentTimeMillis();
totalTime += curTime - lastTime;
if (totalTime > 1000) {
totalTime -= 1000;
fps = frames;
frames = 0;
}
++frames;
}
Thread.yield(); } public synchronized Graphics2D loadGraphics() {
if (!initFlag) {
initializtion();
}
return graphics2d;
} }
场景图1 、角色图1 、FPS 80 - 90 
场景图1、角色图100、 FPS 25 - 35
场景图1 、角色图1000 、 FPS 3 - 6
三、完全在 BufferedImage 的 DataBuffer 中进行图像处理 实际上在 Java 网游海盗时代、 Java 网游暗影世界等网络游戏、还有 netbaens 以及其它种种不算慢的 Java 应用中,都存在大量使用 DataBuffer 进行的图像渲染;但在使用方式上,却只有暗影使用的 JGnet 引擎与常用的 Image 与 Graphics 搭配模式较为接近,所以此示例使用了 JGnet 的部分代码构建。 ( PS :实际上汉森的 JGnet 客户端引擎目前并未开源,此部分代码得自其主页 homepage.jar 的反编译,是前天我看 csdn 的 CTO 栏目专访汉森老总后去其首页发现的;视频中说 JGnet 有部分代码准备开源,我估计应该是 homepage.jar 里这部分,因为没有混淆且功能较少,其网站地址 [url]http://www.handseeing.com[/url] ,纯 Applet 站点 )。 为给不愿反编译的看客提供方便,这里我稍微对 JGnet 进行一点分析( homepage.jar 中的那部分)。 就代码来看, JGnet 中所有 UI 继承自 GUIWidget ( GUIWidget 继承自 Container ),所有 JGnet 产生的 GUI 组件也都应当继承它。 JGnet 提供了一个 GUIRoot 为底层面板,所有 GUIWidget 衍生组件都应附着于 GUIRoot 之上,另外该引擎提供有 AbstractGameClient 、 ClientContext 接口及相关实现用以调度相关组件, JGnet 的 GUIRoot 载体可以使用 Applet 或者 Frame 。 JGnet 中大部分资源采用 zip 打包,当然也可以读取单图, JGnet 资源加载依赖于其提供的 ImageFactory 类, ImageFactory 的操作可分为如下两步: 第一步是addArchive,进行此步骤时指定的zip资源会被加载,其后将read图像资源为ShortBufferImage,再根据zip中该资源对应路径缓存到daff.utill包自备的HashMap中。 第二步是getImage,即获得指定路径的ShortBufferImage对象,此步骤以缓存优先,如果获得失败会尝试调用loadImage方法再次加载,并缓存到daff包自备的HashMap对象中。其中loadImage方法会根据后缀判定加载的资源类型,其中jpg、gif或者png后缀文件会调用daff包中提供的AwtImageLoader类转化为ShortBufferImage,而非此后缀文件会直接按照ShortBufferImage格式进行加载。 JGnet 的图像绘制主要使用其内部提供的 ShortBufferImage 及 ShortBufferGraphics 以及相关衍生类。 ShortBufferImage 是一个抽象类,没有父类,所有 JGnet 中图形资源都将表现为 ShortBufferImage ,绘制 ShortBufferImage 需要使用 daff.gui 包提供的专用画布 ShortBufferGraphics ,实际上 ShortBufferGraphics 是一个针对于 ShortBufferImage 的象素渲染器,内部以 short 数组形式保存有一个空白 BufferedImage 的 DataBuffer ,所有绘制基于其上。 同大多数网游一样, JGnet 中大量使用自定义图形格式 ( 后缀 .img) ,其解释与转化通过 CompressedImage 类得以完成,由于自定义格式通常能更简洁的声明图像中各元素关系,所以 JGnet 处理其自定义格式资源会比加载普通图像资源更快。 JGnet 会根据环境的不同会采用 MsJvmGraphics 或 SunJvmGraphics 两套绘图组件(皆继承自 ShortBufferGraphics ),据此我们可以初步断定其运行环境上兼容微软 jvm ,也就是最低可运行于 JRE1.1 之上。 讲解结束,下面我以 JGnet 进行针对 DataBuffer 进行渲染的 FPS 测试(对其某些细节进行了使用习惯上的扩展)。 1、DataBufferGameFrame.java
package test1;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; /**
* @author chenpeng
* @email: [email]ceponline@yahoo.com.cn[/email]
*/
public class DataBufferGameFrame extends Frame implements Runnable { /**
*
*/
private static final long serialVersionUID = 1L; private DataBufferCanvas canvas = null; private int width = 480;
private int height = 360;
public DataBufferGameFrame() {
super("JGnet中ShortBufferGraphics使用测试");
this.setPreferredSize(new Dimension(width + 5, height + 25));
this.requestFocus();
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
canvas = new DataBufferCanvas();
this.add(canvas);
this.pack();
this.setResizable(false);
this.setLocationRelativeTo(null);
this.setIgnoreRepaint(true);
Thread thread = new Thread(this);
thread.start();
} public void run() {
for (;;) {
canvas.drawScreen();
}
} public static void main(String[] args) {
DataBufferGameFrame frm = new DataBufferGameFrame();
frm.setVisible(true);
} }
2、DataBufferCanvas.java
package test1; import impl.gui.SunJvmGraphics; import java.awt.*;
import java.util.Random; import daff.gui.ImageFactory;
import daff.gui.ShortBufferFont;
import daff.gui.ShortBufferGraphics;
import daff.gui.ShortBufferImage;
public class DataBufferCanvas extends Canvas { /**
*
*/
private static final long serialVersionUID = 1L; private boolean isFPS = true; private ShortBufferGraphics shortBufferGraphics; private MyColor color = new MyColor(255, 255, 255); private ShortBufferFont font; private boolean initFlag; private static ImageFactory factory = new ImageFactory(20); private int frames = 0; private long totalTime = 0; private long curTime = System.currentTimeMillis(); private long lastTime = curTime; private Random rand = new Random(); private int fps; private ShortBufferImage hero_idle_0 = factory
.getImage("image/hero/idle/idle0_0.img"); private ShortBufferImage backImage = factory.getImage("back.gif"); static {
factory.addArchive("pack/hero.zip");
} public DataBufferCanvas() { } public void update(Graphics g) {
paint(g);
} public void paint(Graphics g) {
if (initFlag) {
shortBufferGraphics.drawImage(backImage, 0, 0);
for (int i = 0; i < 1000; ++i) {
int x = rand.nextInt(480);
int y = rand.nextInt(360);
shortBufferGraphics.drawImage(hero_idle_0, x - 100, y - 100);
}
shortBufferGraphics.drawString(font, color, ("当前FPS:" + fps)
.intern(), 15, 15);
shortBufferGraphics.drawTo(g, 0, 0);
g.dispose();
}
} private synchronized void initializtion() {
shortBufferGraphics = (ShortBufferGraphics) new SunJvmGraphics();
shortBufferGraphics.createGraphics(getWidth(), getHeight(), this);
font = new ShortBufferFont("Dialog", 0, 12, this);
initFlag = true;
} public synchronized void drawScreen() {
loadGraphics().reset();
repaint();
if (isFPS) {
lastTime = curTime;
curTime = System.currentTimeMillis();
totalTime += curTime - lastTime;
if (totalTime > 1000) {
totalTime -= 1000;
fps = frames;
frames = 0;
}
++frames;
}
Thread.yield();
} public synchronized ShortBufferGraphics loadGraphics() {
if (!initFlag) {
initializtion();
}
return shortBufferGraphics;
} }
场景图1 、角色图1 、FPS 140 - 160
场景图1 、角色图100 、FPS 115 - 125
场景图1 、角色图1000 、FPS 55 - 70
通过简单比较后我们可以发现, Graphics 绘图无论在直接 BufferedImage 双缓冲或者利用 BufferStrategy 以换取硬件加速的前提下,都无法与使用 DataBuffer 直接进行像素绘图的 ShortBufferGraphics 相比, BufferStrategy 在绘制千人时更出现了令笔者整个人都斯巴达了的 4 帧 ||| ,即使在测试中笔者改 BufferedImage 为 VolatileImage 也收效甚微,在配置较高的微机上应当会有些许改善,但用户使用机型却并非我们所能控制的。 由于 JGnet 中直接获得图像 DataBuffer 的 short[] 形式,并以此进行图像绘制,所以 ShortBufferImage 及 ShortBufferGraphics 称得上是一组先天的 Java 图形缓冲对象,就效率上讲,使用 ShortBufferGraphics 绘图获得的 FPS 明显高于使用 Graphics2D 绘图,这是数组操作先天性的效率优势所决定的。但缺点在于, ShortBufferImage 及 ShortBufferGraphics 其并非直接继承自 Image 与 Graphics ,两者间函数不能完全对应,有些常用方法尚待实现,很多常用方法尚需 ShortBufferGraphics 使用者自行解决。 单就效率而言,应该说采取 DataBuffer 进行缓冲绘图明显优于单纯利用 BufferStrategy 或者 BufferedImage 获得 Graphics 后进行绘图。 总体上看,如果我们追求最高 FPS ,则使用 BufferedImage 产生 DataBuffer 进行象素处理最为高效的,与方法二混用后效率还将更高,但遗憾的是目前缺少相关开源组件,可用资源上略显不足,有待相关公司或者个人提供,另外我们也可以考虑功能混用,即主体部分使用 DataBuffer 渲染,未实现部分以 Graphics 补足,但这肯定会对运行效率产生影响。 由于 JGnet 目前属于闭源项目,所以我无法公开其任何代码实现。为此我自制了一个非常简单的 DataBuffer 中图像处理类 SimpleIntBufferImage ,以供各位看客参考。 SimpleIntBufferImage.java 源码如下: package test1;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
* Copyright 2008 - 2009
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* [url]http://www.apache.org/licenses/LICENSE-2.0[/url]
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loonframework
* @author chenpeng
* @email:[email]ceponline@yahoo.com.cn[/email]
* @version 0.1
*/
public class SimpleIntBufferImage {
private BufferedImage bufferImage;
private int width;
private int height;
final private int[] pixels;
final private int[] clear;
public SimpleIntBufferImage( final String fileName) {
try {
BufferedImage tmpImage = ImageIO.read( new File(fileName));
width = tmpImage.getWidth( null);
height = tmpImage.getHeight( null);
clear = new int[width * height];
bufferImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
pixels = ((DataBufferInt) bufferImage.getRaster().getDataBuffer())
.getData();
PixelGrabber pgr = new PixelGrabber(tmpImage, 0, 0, width, height,
pixels, 0, width);
try {
pgr.grabPixels();
} catch (InterruptedException ex) {
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
/**
* 清空pixels数据
*
*/
public void clearImage() {
System.arraycopy(clear, 0, pixels, 0, clear.length);
}
/**
* 删除指定颜色
*
* @param rgbs
*/
public void clearRGBs( final int[] rgbs) {
for ( int i = 0; i < pixels.length; i++) {
int npixel = (255 << 24) + (rgbs[0] << 16) + (rgbs[1] << 8)
+ rgbs[2];
if (pixels[i] == npixel) {
pixels[i] = 0;
}
}
}
/**
* 转换当前贴图为灰白位图
*
*/
public void convertToGray() {
for ( int i = 0; i < pixels.length; i++) {
pixels[i] = colorToGray(pixels[i]);
}
}
/**
* 转换当前贴图为异色(反色)位图
*
*/
public void convertToXor() {
for ( int i = 0; i < pixels.length; i++) {
pixels[i] = colorToXor(pixels[i]);
}
}
/**
* 获得r,g,b
*
* @param pixel
* @return
*/
private int[] getRGBs( final int pixel) {
int[] rgbs = new int[3];
rgbs[0] = (pixel >> 16) & 0xff;
rgbs[1] = (pixel >> 8) & 0xff;
rgbs[2] = (pixel) & 0xff;
return rgbs;
}
/**
* 转换指定像素为灰白
*
* @param pixel
* @return
*/
private int colorToGray( final int pixel) {
int[] rgbs = getRGBs(pixel);
int value = ( int) (0.299 * rgbs[0] + 0.587 * rgbs[1] + 0.114 * rgbs[2]);
int npixel = (255 << 24) + (value << 16) + (value << 8) + value;
return npixel;
}
/**
* 异或指定像素
*
* @param pixel
* @return
*/
private int colorToXor( final int pixel) {
int[] rgbs = getRGBs(pixel);
int r = rgbs[0] ^ 0xff;
int g = rgbs[1] ^ 0xff;
int b = rgbs[2] ^ 0xff;
int npixel = (255 << 24) + (r << 16) + (g << 8) + b;
return npixel;
}
/**
* copy指定贴图于本身位图之上
*
* @param simpleImage
* @param left
* @param top
*/
public void copyImage( final SimpleIntBufferImage simpleImage,
final int left, final int top) {
int[] src = simpleImage.getPixels();
int srcWidth = simpleImage.getWidth();
int srcHeight = simpleImage.getHeight();
for ( int x = 0, x1 = left; x < srcWidth && x < width && x1 < width; x++, x1++) {
for ( int y = 0, y1 = top; y < srcHeight && x < height
&& y1 < height; y++, y1++) {
int npixels = src[x + y * srcWidth];
if (npixels != -16777216) {
pixels[x1 + y1 * width] = npixels;
}
}
}
}
/**
* 截取指定范围内像素点数组
*
* @param x
* @param y
* @param w
* @param h
* @return
*/
public int[] getSubPixels( final int x, final int y, final int w, final int h) {
int[] pixels = new int[2 + w * h];
pixels[0] = w;
pixels[1] = h;
if (pixels == null) {
return null;
}
for ( int i = 0; i < h; i++) {
for ( int j = 0; j < w; j++) {
pixels[2 + x + j + (y + i) * w] = getPixel(x + j, y + i);
}
}
return pixels;
}
/**
* 截取指定范围内像素点
*
* @param x
* @param y
* @return
*/
public int getPixel( final int x, final int y) {
return pixels[x + y * width];
}
public int[] getPixels() {
return pixels;
}
public BufferedImage getBufferedImage() {
return bufferImage;
}
public int getHeight() {
return height;
}
public void setHeight( int height) {
this.height = height;
}
public int getWidth() {
return width;
}
public void setWidth( int width) {
this.width = width;
}
}
简单的应用示例: package test1;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/**
*
* Copyright 2008 - 2009
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* [url]http://www.apache.org/licenses/LICENSE-2.0[/url]
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loonframework
* @author chenpeng
* @email:[email]ceponline@yahoo.com.cn[/email]
* @version 0.1
*/
public class Sample extends Frame implements Runnable{
/**
*
*/
private static final long serialVersionUID = 1L;
private SimpleIntBufferImage simple1 = new SimpleIntBufferImage( "back.gif");
private SimpleIntBufferImage simple2 = new SimpleIntBufferImage(
"idle0_0.png");
private int width = 480;
private int height = 360;
public Sample() {
super( "自制SimpleIntBufferImage示例");
this.setBackground(Color.black);
simple1.convertToXor();
simple1.convertToXor();
this.setPreferredSize( new Dimension(width, height));
this.requestFocus();
this.addWindowListener( new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
this.pack();
this.setResizable( false);
this.setLocationRelativeTo( null);
this.setIgnoreRepaint( true);
Thread thread = new Thread( this);
thread.start();
}
public void run() {
for(;;){
repaint();
try {
Thread.sleep(20L);
} catch (InterruptedException e) {
}
}
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
simple1.copyImage(simple2,15,50);
g.drawImage(simple1.getBufferedImage(), 0, 0, null);
}
public static void main(String[] args) {
Sample frm = new Sample();
frm.setVisible( true);
}
}
效果图如下:
本文转自 cping 51CTO博客,原文链接:http://blog.51cto.com/cping1982/145872
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
