评论

收藏

Java游戏开发中怎样才能获得更快的FPS?

游戏开发 游戏开发 发布于:2021-06-27 15:56 | 阅读数:599 | 评论:0

众所周知,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,还要老生常谈的从ImageGraphics及其相关子类入手。 那么,对这些尽人皆知的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、完全在BufferedImageDataBuffer中进行图像处理 每个BufferedImage都有一个与之对应得WritableRaster对象(getRaster方法获得),通过它我们获得指定BufferedImageDataBuffergetDataBuffer方法获得),与方法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:ceponline@yahoo.com.cn
*/
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.p_w_picpath.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 DSC0000.png
场景图1、角×××100 FPS 20 - 25 DSC0001.png
场景图1、角×××1000 FPS 13 - 17 DSC0002.png 二、采用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:ceponline@yahoo.com.cn
*/
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.p_w_picpath.BufferStrategy;
import java.awt.p_w_picpath.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 DSC0003.png
场景图1、角×××100 FPS 25 - 35 DSC0004.png 场景图1、角×××1000 FPS 3 - 6 DSC0005.png 三、完全在BufferedImageDataBuffer中进行图像处理 实际上在Java网游海盗时代、Java网游暗影世界等网络游戏、还有netbaens以及其它种种不算慢的Java应用中,都存在大量使用DataBuffer进行的图像渲染;但在使用方式上,却只有暗影使用的JGnet引擎与常用的ImageGraphics搭配模式较为接近,所以此示例使用了JGnet的部分代码构建。 PS:实际上汉森的JGnet客户端引擎目前并未开源,此部分代码得自其主页homepage.jar的反编译,是前天我看csdnCTO栏目专访汉森老总后去其首页发现的;视频中说JGnet有部分代码准备开源,我估计应该是homepage.jar里这部分,因为没有混淆且功能较少,其网站地址http://www.handseeing.com,纯Applet站点)。 为给不愿反编译的看客提供方便,这里我稍微对JGnet进行一点分析(homepage.jar中的那部分)。 就代码来看,JGnet中所有UI继承自GUIWidgetGUIWidget继承自Container),所有JGnet产生的GUI组件也都应当继承它。JGnet提供了一个GUIRoot为底层面板,所有GUIWidget衍生组件都应附着于GUIRoot之上,另外该引擎提供有AbstractGameClientClientContext接口及相关实现用以调度相关组件,JGnetGUIRoot载体可以使用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的图像绘制主要使用其内部提供的ShortBufferImageShortBufferGraphics以及相关衍生类。ShortBufferImage是一个抽象类,没有父类,所有JGnet中图形资源都将表现为ShortBufferImage,绘制ShortBufferImage需要使用daff.gui包提供的专用画布ShortBufferGraphics,实际上ShortBufferGraphics是一个针对于ShortBufferImage的象素渲染器,内部以short数组形式保存有一个空白BufferedImageDataBuffer,所有绘制基于其上。 同大多数网游一样,JGnet中大量使用自定义图形格式(后缀.img),其解释与转化通过CompressedImage类得以完成,由于自定义格式通常能更简洁的声明图像中各元素关系,所以JGnet处理其自定义格式资源会比加载普通图像资源更快。 JGnet会根据环境的不同会采用MsJvmGraphicsSunJvmGraphics两套绘图组件(皆继承自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:ceponline@yahoo.com.cn
*/
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("p_w_picpath/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 DSC0006.png 场景图1、角×××100 FPS 115 - 125
DSC0007.png 场景图1、角×××1000 FPS 55 - 70 DSC0008.png   
通过简单比较后我们可以发现,Graphics绘图无论在直接BufferedImage双缓冲或者利用BufferStrategy以换取硬件加速的前提下,都无法与使用DataBuffer直接进行像素绘图的ShortBufferGraphics相比,BufferStrategy在绘制千人时更出现了令笔者整个人都斯巴达了的4|||,即使在测试中笔者改BufferedImageVolatileImage也收效甚微,在配置较高的微机上应当会有些许改善,但用户使用机型却并非我们所能控制的。 由于JGnet中直接获得图像DataBuffershort[]形式,并以此进行图像绘制,所以 ShortBufferImageShortBufferGraphics称得上是一组先天的Java图形缓冲对象,就效率上讲,使用ShortBufferGraphics绘图获得的FPS明显高于使用Graphics2D绘图,这是数组操作先天性的效率优势所决定的。但缺点在于,ShortBufferImageShortBufferGraphics其并非直接继承自ImageGraphics,两者间函数不能完全对应,有些常用方法尚待实现,很多常用方法尚需ShortBufferGraphics使用者自行解决。 单就效率而言,应该说采取DataBuffer进行缓冲绘图明显优于单纯利用BufferStrategy或者BufferedImage获得Graphics后进行绘图。 总体上看,如果我们追求最高FPS,则使用BufferedImage产生DataBuffer进行象素处理最为高效的,与方法二混用后效率还将更高,但遗憾的是目前缺少相关开源组件,可用资源上略显不足,有待相关公司或者个人提供,另外我们也可以考虑功能混用,即主体部分使用DataBuffer渲染,未实现部分以Graphics补足,但这肯定会对运行效率产生影响。 由于JGnet目前属于闭源项目,所以我无法公开其任何代码实现。为此我自制了一个非常简单的DataBuffer中图像处理类SimpleIntBufferImage,以供各位看客参考。 SimpleIntBufferImage.java 源码如下: package test1;

import java.awt.p_w_picpath.BufferedImage;
import java.awt.p_w_picpath.DataBufferInt;
import java.awt.p_w_picpath.PixelGrabber;
import java.io.File;
import java.io.IOException;

import javax.p_w_picpathio.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
*   
http://www.apache.org/licenses/LICENSE-2.0
   
* 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:ceponline@yahoo.com.cn
* @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 == npixel) {
        pixels = 0;
      }
    }
  }

  /**
    转换当前贴图为灰白位图
   
   
    */

  public void convertToGray() {
    for (int i = 0; i < pixels.length; i++) {
      pixels = colorToGray(pixels);
    }
  }

  /**
    转换当前贴图为异色(反色)位图
   
   
    */

  public void convertToXor() {
    for (int i = 0; i < pixels.length; i++) {
      pixels = colorToXor(pixels);
    }
  }

  /**
    获得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
http://www.apache.org/licenses/LICENSE-2.0
*   
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:ceponline@yahoo.com.cn
@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);
  }

}

  效果图如下:

DSC0009.png

关注下面的标签,发现更多相似文章