博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java游戏开发中怎样才能获得更快的FPS?
阅读量:5896 次
发布时间:2019-06-19

本文共 21267 字,大约阅读时间需要 70 分钟。

众所周知, 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:
 */
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:
 */
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
、角色图
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
里这部分,因为没有混淆且功能较少,其网站地址
,纯
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方法会根据后缀判定加载的资源类型,其中jpggif或者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:
 */
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
、角色图
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

转载地址:http://oeasx.baihongyu.com/

你可能感兴趣的文章
OEL的下载
查看>>
U盘强制拔出数据丢失怎么办
查看>>
php 7.2 安装 mcrypt 扩展
查看>>
Linux上安装使用boost入门指导
查看>>
16种oracle查询日期语句
查看>>
myeclipse添加spket插件
查看>>
Silverlight WCF RIA服务(二十二)Silverlight 客户端 3
查看>>
Linux内核的文件预读详细详解
查看>>
网站优化
查看>>
【转载】面试_现在有4个石头,1000层的楼房,需要测定这个石头破碎的高度。求最少多少次一定可以测出来。...
查看>>
RAID0、RAID1、RAID0+1、RAID5原理介绍
查看>>
指针加减法运算的“定义域”
查看>>
sscanf与正则表达式
查看>>
XP和Scrum的比较
查看>>
Ethernet IP TCP UDP 协议头部格式
查看>>
请高手指点如何提高笔记本WIFI网络速度
查看>>
前端必读:浏览器内部工作原理
查看>>
现代软件工程讲义 8 软件的血型
查看>>
Android自定义GridView显示一行,并且可以左右滑动
查看>>
【web开发】spring+hibernate4支持中文排序
查看>>