众所周知, 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: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
场景图 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: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
场景图 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 里这部分,因为没有混淆且功能较少,其网站地址 http://www.handseeing.com ,纯 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: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
场景图 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.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 );
}
}
效果图如下:
免责声明:
1. 本站所有资源来自网络搜集或用户上传,仅作为参考不担保其准确性!
2. 本站内容仅供学习和交流使用,版权归原作者所有!© 查看更多
3. 如有内容侵害到您,请联系我们尽快删除,邮箱:kf@codeae.com