两种修改png图片颜色方法的对比
在手机游戏开发中,为了节省资源,我们常常修改png图片以实现一张图片多种显示效果。有两种办法可以实现这个功能:
第一种是装载png图片,使用getRGB()取得取得图片的RGB颜色数据,然后修改RGB颜色数据,再用Image的静态方法createRGBImage()将修改后的RGB颜色数据生成新的png图片。示例代码:testImage为测试的Image对象,imgW,imgH为其宽和高
// 取得图片的RGB数据--这个数组是比较大的,因此修改RGB数据生成新的图片效率很低
rgbData = new int[imgW * imgH];
testImage.getRGB(rgbData,0,imgW,0,0,imgW,imgH);
// 修改RGB数据并生成新的图片
rgbImage = Image.createRGBImage(changeRGBData(rgbData),imgW,imgH,true);
第二中方法是取得png图片的二进制数据,修改其中的调色板域(PLTE chunk)数据,再使用
Image的静态对象createImage(byte[] imageData,int imageOffset,int imageLength)将修改后的二进制数据生成新的png对象。示例代码:查看代码中的getPLTEModifidImage()函数。
如果对png的数据格式组成不熟悉,可以查阅:http://www.w3.org/TR/PNG/
或者(不错的中文介绍): http://www.ismyway.com/png/png-struct1.htm
通常,图片的RGB颜色数据是比较大的,而调色板数据远比RGB颜色数据要少的多,修改RGB颜色数据效率要高的多。下面我使用一张大小为52*28的png图片作为测试图片,从以下测试中打印的数据就可看出(1456对比144):
Load file Gold total data is 1084 ――――――――图片的二进制字节数据个数
--------RGB data length is 1456―――――――――RGB颜色字节数据个数
--------The number of PLTE chunks is 48―――调色板数据块个数(实际字节数据为48*3)
效果图:
附测试的源代码:
//-----------------------------------------------------Code--START------------------------------------------------
/**
* Dicription : Create png image by modifying PLTE chunk.
* Author : 飘飘白云
* Created date : 2006-07-03
* Modified history :
*/
import java.io.DataInputStream;
import java.io.IOException;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
public class CreatePNG extends MIDlet
{
GameCanvas canvas;
Display display;
public CreatePNG()
{
super();
// TODO Auto-generated constructor stub
try {
display = Display.getDisplay(this);
canvas = new GameCanvas(this);
} catch (Exception e) {}
}
protected void startApp() throws MIDletStateChangeException
{
// TODO Auto-generated method stub
if( canvas != null ){
display.setCurrent(canvas);
canvas.start();
}
}
protected void pauseApp()
{
// TODO Auto-generated method stub
canvas.beExit = true;
}
protected void destroyApp( boolean arg0) throws MIDletStateChangeException
{
// TODO Auto-generated method stub
}
}
class GameCanvas extends Canvas implements Runnable
{
CreatePNG app;
Thread mainThread;
Image offImage = null;
public Graphics g = null;
public boolean beExit = false;
long sleepTime = 30;
int scrW;
int scrH;
Font font;
int fontH;
int fontW;
//-------------------------------------
int gameMode;
int subMode;
static final int smInit = 0;
static final int smProc = 1;
static final int smEnd = 2;
static final int gmStart = 0;
static final int gmProcPNGData = 1;
//--------------------------------------
Image testImage = null;
Image rgbImage = null;
Image plteImage = null;
int imgW;
int imgH;
int[] rgbData = null;
byte[] imgData = null;
String testFile = "Gold";
public GameCanvas(MIDlet midlet)
{
app = (CreatePNG)midlet;
}
public void start()
{
changeGameMode(gmStart);
mainThread = new Thread(this);
mainThread.start();
}
public void changeGameMode(int gm)
{
gameMode = gm;
subMode = smInit;
}
protected void paint( Graphics _g)
{
// TODO Auto-generated method stub
if( offImage != null)
_g.drawImage(offImage, 0, 0, 0);
}
public void run()
{
// TODO Auto-generated method stub
gameInit();
while( !beExit )
{
gameMain();
gameDraw();
repaint();
gcWait(sleepTime);
}
if( beExit ) {
try {
app.destroyApp(true);
}
catch ( MIDletStateChangeException e ) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
void gcWait(long tm)
{
System.gc();
try {
Thread.sleep(tm);
}
catch ( InterruptedException e ) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
void println(String str)
{
System.out.println( str );
}
void print(String str)
{
System.out.print( str );
}
void gameInit()
{
scrW = getWidth();
scrH = getHeight();
if(g == null || offImage == null) {
offImage = Image.createImage(scrW, scrH);
g = offImage.getGraphics();
g.translate(0,0);
}
repaint();
font = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN,Font.SIZE_MEDIUM);
g.setFont(font);
changeGameMode( gmStart );
}
/**
* 装载图片
* @param fileName 图片名称
* @return image 对象
*/
Image loadImage( String fileName)
{
Image tmp = null;
try {
tmp = Image.createImage("/" + fileName + ".png");
}
catch ( IOException e ) {
println("[Error] >> loadImage(): " + fileName + " Failed!");
e.printStackTrace();
}
if( tmp == null ) {
println("[Error] >> loadImage(): " + fileName + " Failed!");
}
return tmp;
}
/**
* 读取文件的二进制数据
* @param fileName 文件名
* @return 文件二进制数据的字节数组.
*/
byte[] loadFile(String fileName)
{
DataInputStream isr = null;
byte[] bData;
try {
try {
isr = new DataInputStream(getClass().getResourceAsStream("/" + fileName + ".png"));
}
catch (Exception e) {
}
int res_size = 0;
while (isr.read() != -1)
res_size++;
if (isr.markSupported())
isr.reset();
else {
isr.close();
isr = new DataInputStream(getClass().getResourceAsStream("/" + fileName + ".png"));
}
bData = new byte[res_size];
int read_bytes = isr.read(bData, 0, res_size);
isr.close();
System.gc();
println("Load file " + fileName + " total data is " + read_bytes);
return bData;
}
catch (Exception e) {
println("Error>>loadFile " + fileName);
return null;
}
}
void gameMain()
{
if( gameMode == gmStart )
{
if( subMode == smInit )
{
// 装载图片
testImage = loadImage(testFile);
imgW = testImage.getWidth();
imgH = testImage.getHeight();
subMode = smProc ;
}
else if( subMode == smProc )
{
// 取得图片的RGB数据--这个数组是比较大的,因此修改RGB数据生成新的图片效率很低
rgbData = new int[imgW * imgH];
testImage.getRGB(rgbData,0,imgW,0,0,imgW,imgH);
println("--------RGB data length is - " + rgbData.length);
changeGameMode(gmProcPNGData);
}
}
else if( gameMode == gmProcPNGData )
{
if( subMode == smInit )
{
// 取得图片的二进制数据
imgData = loadFile(testFile);
subMode = smProc ;
println("----Start---All Image Data--length-" + imgData.length);
for( int i = 0; i< imgData.length; i++ ){
if( i% 10 == 0 )
println("");
System.out.print(" " + imgData[i]);
}
println("\n----End---All Image Data--length-" + imgData.length);
// 修改调色板数据,改变图片颜色,这种方法修改的数据远比修改RGB数据要少,效率高很多
plteImage = getPLTEModifidImage(imgData,3);
}
else if( subMode == smProc )
{
// 修改RGB数据生成新的图片,非常低效
rgbImage = Image.createRGBImage(changeRGBData(rgbData),imgW,imgH,true);
subMode = smEnd;
}
else if( subMode == smEnd )
{
}
}
}
void gameDraw()
{
if( gameMode == gmStart )
{
if( subMode == smProc ){
// 描绘原始图片
g.drawImage(testImage,20,20,0);
}
}
else if( gameMode == gmProcPNGData )
{
if( subMode == smProc ){
// 描绘修改调试板数据生成的图片
g.drawImage(plteImage,20,80,0);
}
else if( subMode == smEnd ) {
// 描绘修改RGB数据生成的图片,也可以使用drawRGB()直接描绘RGB数据,不过要注意透明问题.
g.drawImage(rgbImage,20,140,0);
}
}
}
/**
* 修改png图片的调色板数据生成新的png图片
* @param imageSrc png图片的二进制数据字节数组
* @param type 修改策略
* @return 新的png图片
*/
public Image getPLTEModifidImage(byte[] imageSrc , int type)
{
if (imageSrc == null || imageSrc.length <= 1)
return null;
if (crcTable == null)
makeCrcTable();
// PLTE chunk数据域的类型标识
// see http://www.w3.org/TR/PNG/#11PLTE
String[] sPLTE = {"50", "4c", "54", "45"};
int i,j;
int pos = 0,startPos = 0;
byte[] data = imageSrc;
for (i = 0; i < data.length; i++)
{
if (Integer.toHexString(data[i]).equals(sPLTE[0])
&& Integer.toHexString(data[i + 1]).equals(sPLTE[1])
&& Integer.toHexString(data[i + 2]).equals(sPLTE[2])
&& Integer.toHexString(data[i + 3]).equals(sPLTE[3]))
{
pos = i;
break;
}
}
pos -= 4;
startPos = pos;
println("\n======================start Palette data process========================");
println("-------------Palette chunk start pos = " + pos);
// 取得PLTE chunk数据域的数据长度().
int imageNbColors = (
((data[pos] << 24) & 0xff000000)
| ((data[pos + 1] << 16) & 0x00ff0000)
| ((data[pos + 2] << 8 ) & 0x0000ff00)
| ((data[pos + 3] ) & 0x000000ff));
// 计算的PLTE chunk数据个数(每个PLTE chunk数据由R,G,B三个字节数据组成)
imageNbColors = imageNbColors/3;
// 为整形的PLTE chunk data分配空间
int imageRGBColors[] = new int[ imageNbColors ];
//12 = 数据长度(4个字节) + 类型标识(4个字节) + 校验码(4个字节)
for( i = pos,j = 0; i < pos + 12 + imageNbColors * 3 ; i++,j++ ){
if( j >= 8 && (j - %3 == 0 ) {
println("");
}
System.out.print(" " + data[i]);
}
pos += 8;
println("\n--------The number of PLTE chunks is " + imageNbColors + "------------");
if (imageRGBColors == null)
return null;
// 生成整形的PLTE chunk data
for( i = 0; i < imageNbColors; i++ )
{
imageRGBColors[i] = (
(data[pos + 0] & 0x000000ff) << 16)
| ((data[pos + 1] & 0x000000ff) <<
| ((data[pos + 2] & 0x000000ff));
if( i % 10 == 0 && i != 0) {
println("");
}
print(" " + imageRGBColors[i]);
pos += 3;
}
// 修改 PLTE chunk data
switch (type)
{
case 0:
// don't modify
break;
case 1: {
int l,r,g,b;
// gray
for (j = 1; j < imageNbColors; j++) {
r = imageRGBColors[j];
g = (r & 0x00FF00) >> 8;
b = r & 0x0000FF;
r = (r & 0xFF0000) >> 16;
l = (b + g * 6 + r * 3) / 16;
imageRGBColors[j] = l << 16 | l << 8 | l;
}
break;
}
case 2: {
// white
for (j = 1; j < imageNbColors; j++)
imageRGBColors[j] = 0xFFFFFF;
break;
}
case 3: {
int l,r,g,b;
// red
for (j = 1; j < imageNbColors; j++) {
r = imageRGBColors[j];
g = (r & 0x00FF00) >> 8;
b = r & 0x0000FF;
r = (r & 0xFF0000) >> 16;
l = (b + g * 6 + r * 3) / 10;
imageRGBColors[j] = l << 16;
}
break;
}
}
// 生成新的 PLTE chunk data
pos = startPos + 8;
for( i = 0; i < imageNbColors ;i++)
{
data[pos ] = (byte)((imageRGBColors[i] >> 16) ) ;
data[pos + 1 ] = (byte)((imageRGBColors[i] >> );
data[pos + 2] = (byte)(imageRGBColors[i] );
pos += 3;
}
// 更新 CRC 校验码
int crc = updateCrcChunk( data, startPos + 4, startPos + 4 + 4 + ( imageNbColors * 3 ) );
data[pos + 0] = (byte)(crc >> 24 & 0x000000FF);
data[pos + 1] = (byte)(crc >> 16 & 0x000000FF);
data[pos + 2] = (byte)(crc >> 8 & 0x000000FF);
data[pos + 3] = (byte)(crc & 0x000000FF);
pos = startPos;
println("\n---------------------New plte data crc " + crc +"--------------------------------");
for( i = 0; i < 4 + 4 + ( imageNbColors * 3 ) + 4; i++ )
{
if( i >= 8 && (i - %3 == 0 ) {
println("");
}
print(" " + data[pos + i]);
}
println("\n=========================End plte data-=================================");
return Image.createImage(data,0,data.length);
}
/**
* 修改png图片的RGB数据
* @param rgbSrc png图片的RGB数据的字节数组
* @return 新的RGB数据的字节数组
*/
public int[] changeRGBData(int[] rgbSrc)
{
int len = rgbSrc.length;
int[] ret = new int[len];
int a,r,g,b;
int tmp;
for( int i = 0; i < len; i++ )
{
b = rgbSrc[i];
a = ((b & 0xff000000) >> 24);
r = ((b & 0x00ff0000) >> 16);
g = ((b & 0x0000ff00) >> ;
b = ((b & 0x000000ff) >> 0);
// exchange red and blue
tmp = r;
r = b;
b = tmp;
ret[i] = (a << 24) | (r << 16) | (g << | b;
}
return ret;
}
// CRC校验表,加速CRC计算
static long crcTable[];
static public void makeCrcTable()
{
long c;
int n, k;
crcTable = new long[256];
for (n = 0; n < 256; n++) {
c = (long) n;
for (k = 0; k < 8; k++) {
if ((c & 1) == 1) {
c = 0xEDB88320L ^ (c >> 1);
}
else {
c = c >> 1;
}
}
crcTable[n] = c;
}
}
/**
* 计算CRC校验码
*/
static public int updateCrcChunk(byte[] buf, int dataOffsetStart,int dataOffsetEnd)
{
long c = 0xFFFFFFFFL;
for (int i = dataOffsetStart; i < dataOffsetEnd; i++)
c = (crcTable[(int) ((c ^ buf[i]) & 0xFF)] ^ (c >> );
return (int) (c ^ 0xFFFFFFFFL);
}
}
//-----------------------------------------------------Code------END----------------------------------------------
//2
游戏帧速度处理
保持游戏在不同性能手机平台的流畅性很重要,这就需要针对不通硬件设置相应合适的帧速度。假设一般
手机用户能接受的游戏帧速为40/s 则相关参数和代码如下:
//====================================================
// 游戏帧速度调整
//====================================================
private long framerate= 1000/40; // 设置帧速为每秒40帧,(25 ms / frame)
private long frameStart; // 帧开始的时间time the frame begin
private long frameCount = 0; // 这一秒的帧数
private long elapsedTime; // 一帧消耗的时间
private long totalElapsedTime = 0; // 多帧累计消耗的时间
private long reportedFramerate; // 实际计算出来的帧速
public void updateFrame()
{
frameStart = System.currentTimeMillis();
repaint();
elapsedTime = System.currentTimeMillis() - frameStart;
//帧速同步
try {
if( elapsedTime < framerate ) {
Thread.sleep(framerate - elapsedTime);
}else {
Thread.sleep(5);
}
}catch( Exception e ) {
e.printStackTrace();
}
//更新实际的帧速
++ frameCount;
totalElapsedTime += (System.currentTimeMillis() - frameStart);
if( totalElapsedTime > 1000 ) {
reportedFramerate = (long)((double)frameCount/(double)
totalElapsedTime*1000.0);
//在调试时可屏幕中显示帧速
//g.drawString("fps: " + reportedFramerate,x,y,0);
frameCount = 0;
totalElapsedTime = 0;
}
}
////3
J2me中实现淡入淡出效果
飘飘白云(l_zhaohui@163.com)
在J2me中实现淡入淡出效果,据我所知至少有三种方法。
第一种是取得需要变换图片的像素,依次设置每个象素的alpha通道值,让它在0~100之间变化。
第二种是修改图片的调色板数据,让其在调色板原始数据到255之间变化。
第三种,其实也是利用上面的办法,先描画图片,然后在图片上覆盖一个黑色矩形,改变这个黑色矩形透明度就可以实现淡入淡出的效果。
前两种方法相比较的话,第一种方法运算量是比较大的,而且第一种方法由于midp1.0不支持alpha通道,在一些手机上无法实现。
下面给出第二种方法的示例,在我们开始之前,应该熟悉png文件格式,如果还不是很明白的话,可以google一下,或者查看前面的帖子中的相关连接。
代码很清楚,下面是源代码:
------------CODE__START-----------------------
import java.io.DataInputStream;
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
/**
* Discription : 修改调色板数据实现淡入淡出效果
* Author : 飘飘白云(l_zhaohui@163.com)
* Created date : 2006/07/13 18:06:39
* Modified history :
*/
public class PngFadeInOut extends MIDlet
{
GameCanvas canvas;
Display display;
public PngFadeInOut()
{
super();
try {
display = Display.getDisplay(this);
canvas = new GameCanvas(this);
} catch (Exception e) {}
}
protected void startApp() throws MIDletStateChangeException
{
if( canvas != null ){
display.setCurrent(canvas);
canvas.start();
}
}
protected void pauseApp()
{
canvas.isExit = true;
}
protected void destroyApp( boolean arg0) throws MIDletStateChangeException
{
}
}
class GameCanvas extends Canvas
{
static String imageName = "/fadeInOut.png";
public static final int MAX_TARDINESS = 30;
//---------------------------------------------------
PngFadeInOut app;
boolean isInited = false;
public boolean isExit = false;
Image offImage = null;
public Graphics g = null;
int scrW;
int scrH;
//---------------------------------------------------
int gameMode;
int subMode;
static final int smInit = 0;
static final int smProc = 1;
static final int smEnd = 2;
static final int gmStart = 0;
//---------------------------------------------------
Image testImage = null;
byte[] oldRGBData = null;
byte[] imgData = null;
static final int FADE_IN = 0;
static final int FADE_OUT = 1;
static final int FADE_STEP = 1;
static int fadeType = FADE_IN;
//---------------------------------------------------
//---Timer process
//---------------------------------------------------
TimerTask task;
Timer timer;
int timerDelayTime;
public boolean isTimerRunning;
void startTimer(int delayTime){
timerDelayTime = delayTime;
isTimerRunning = true;
timer = new Timer();
task = new ProcessingRunner();
timer.scheduleAtFixedRate(task, 0, delayTime);
}
public void stopTimer(){
if( task != null )
task.cancel();
isTimerRunning = false;
timer = null;
task = null;
}
//-----------------------------------------------------
class ProcessingRunner extends TimerTask
{
public void run()
{
if( isExit ) {
stopTimer();
app.notifyDestroyed();
}
if (!isTimerRunning || System.currentTimeMillis() -
scheduledExecutionTime() >= timerDelayTime)
{
return;
}
gameMain();
gameDraw();
repaint();
serviceRepaints();
System.gc();
}
}
//-------------------------------------------------
public GameCanvas(MIDlet midlet)
{
app = (PngFadeInOut)midlet;
}
public void start()
{
setFullScreenMode(true);
while (!isShown())
;
if( !isInited ) {
gameInit();
isInited = true;
}
startTimer(MAX_TARDINESS);
}
void gameInit()
{
scrW = getWidth();
scrH = getHeight();
if(g == null || offImage == null) {
offImage = Image.createImage(scrW, scrH);
g = offImage.getGraphics();
}
changeGameMode(gmStart);
}
public void changeGameMode(int gm){
gameMode = gm;
subMode = smInit;
}
//---------------------------------------------------
// 游戏主逻辑处理
//---------------------------------------------------
void gameMain()
{
if( gameMode == gmStart )
{
if( subMode == smInit )
{
// 取得图片的二进制数据
imgData = loadFile(imageName);
//保存原始图片的调色板rgb数据
oldRGBData = getPlteRGBData(imgData);
if( fadeType == FADE_IN) {
// 设置调色板颜色为白色,淡入效果
for(int i = rgbDataStartPos; i < rgbDataStartPos + rgbDataLength;i++ ) {
imgData[i] = (byte)255;
}
}
// 生成新的图片
testImage = createFadeImage(imgData,rgbDataStartPos,rgbDataLength);
subMode = smProc ;
}
else if( subMode == smProc )
{
try {
// 淡入淡出效果处理
fade(fadeType);
testImage = createFadeImage(imgData,rgbDataStartPos,rgbDataLength);
}
catch ( Exception e ) {
println("Create fade image error. \n" + e);
}
}
}
}
//---------------------------------------------------
// 游戏描画处理
//---------------------------------------------------
protected void paint( Graphics _g){
if( offImage != null)
_g.drawImage(offImage, 0, 0, 0);
}
void gameDraw()
{
if( gameMode == gmStart )
{
if( subMode == smProc ){
g.setColor(0xffffff);
g.fillRect(0,0,scrW,scrH);
g.drawImage(testImage,20,140,0);
}
}
}
/**
* 淡入淡出效果处理
* @param type
*/
public void fade( int type)
{
int m = 0;
int n = 0;
// 淡入
if ( type == FADE_IN ) {
for( int i = 0; i < rgbDataLength; i++ )
{
m = imgData[rgbDataStartPos + i] & 0x000000ff;
n = oldRGBData[i] & 0x000000ff;
m = m < 0? m + 256 : m;
n = n < 0? n + 256 : n;
m = m > n ? m - FADE_STEP : n;
imgData[rgbDataStartPos + i] = (byte)(m & 0xff);
}
}
// 淡出
else if ( type == FADE_OUT ) {
for( int i = 0; i < rgbDataLength; i++ )
{
m = imgData[rgbDataStartPos + i] & 0x000000ff;
m = m < 0? m + 256 : m;
m = m < 255 ? m + FADE_STEP : 255;
imgData[rgbDataStartPos + i] = (byte)(m & 0xff);
}
}
}
/**
* 这里不是单纯创建一幅图片,crc32校验码也在这个函数里更新
* @param data
* @param startPos 需要校验的数据的起始位置
* @return
*/
public Image createFadeImage(byte[] data,int startPos,int length)
{
if (crcTable == null)
makeCrcTable();
int endPos = startPos + length ;
int crc = updateCrcChunk( data, startPos -8,endPos );
data[endPos + 0] = (byte)(crc >> 24 & 0x000000FF);
data[endPos + 1] = (byte)(crc >> 16 & 0x000000FF);
data[endPos + 2] = (byte)(crc >> 8 & 0x000000FF);
data[endPos + 3] = (byte)(crc & 0x000000FF);
return Image.createImage(data,0,data.length);
}
static int rgbDataStartPos = 0;
static int rgbDataLength = 0;
/**
* @param imgData png图片的二进制数据
* @return 调色板颜色数据的字节数组
*/
public byte[] getPlteRGBData(byte[] imgData)
{
if (imgData == null || imgData.length <= 1)
return null;
// PLTE chunk数据域的类型标识
// see http://www.w3.org/TR/PNG/#11PLTE
String[] sPLTE = {"50", "4c", "54", "45"};
int i;
int pos = 0;
//定位调色板数据的位置
for (i = 0; i < imgData.length; i++)
{
if (Integer.toHexString(imgData[i]).equals(sPLTE[0])
&& Integer.toHexString(imgData[i + 1]).equals(sPLTE[1])
&& Integer.toHexString(imgData[i + 2]).equals(sPLTE[2])
&& Integer.toHexString(imgData[i + 3]).equals(sPLTE[3]))
{
pos = i;
break;
}
}
pos -= 4;
// 取得PLTE chunk的字节数据长度.
int imageNbColors = (
((imgData[pos] << 24) & 0xff000000)
| ((imgData[pos + 1] << 16) & 0x00ff0000)
| ((imgData[pos + 2] << 8 ) & 0x0000ff00)
| ((imgData[pos + 3] ) & 0x000000ff));
//8 = 数据长度(4个字节) + 类型标识(4个字节)
pos += 8;
// 设置全局变量
rgbDataStartPos = pos;
rgbDataLength = imageNbColors;
//复制数据
byte[] data = new byte[rgbDataLength ];
for( i = 0 ; i < rgbDataLength ; i++){
data[i] = imgData[i + rgbDataStartPos ];
}
return data;
}
// CRC校验查找表,加速CRC32运算
static long crcTable[];
static public void makeCrcTable()
{
long c;
int n, k;
crcTable = new long[256];
for (n = 0; n < 256; n++) {
c = (long) n;
for (k = 0; k < 8; k++) {
if ((c & 1) == 1) {
c = 0xEDB88320L ^ (c >> 1);
}
else {
c = c >> 1;
}
}
crcTable[n] = c;
}
}
/**
* 计算crc32校验码
*/
static public int updateCrcChunk(byte[] buf, int dataOffsetStart,int dataOffsetEnd)
{
long c = 0xFFFFFFFFL;
for (int i = dataOffsetStart; i < dataOffsetEnd; i++)
c = (crcTable[(int) ((c ^ buf[i]) & 0xFF)] ^ (c >> );
return (int) (c ^ 0xFFFFFFFFL);
}
/**
* 读取文件的二进制数据
* @param fileName 文件名
* @return 文件二进制数据
*/
byte[] loadFile(String fileName)
{
DataInputStream isr = null;
byte[] bData;
try {
try {
isr = new DataInputStream(getClass().getResourceAsStream(fileName));
}
catch (Exception e) {
}
int res_size = 0;
while (isr.read() != -1)
res_size++;
if (isr.markSupported())
isr.reset();
else {
isr.close();
isr = new DataInputStream(getClass().getResourceAsStream(fileName));
}
bData = new byte[res_size];
int read_bytes = isr.read(bData, 0, res_size);
isr.close();
System.gc();
return bData;
}
catch (Exception e) {
println("Error>>loadFile " + fileName);
return null;
}
}
static void println(String str){
System.out.println( str );
}
}
----------------------CODE__END-------------------------------
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/liu01983zm/archive/2006/08/30/1143132.aspx
在手机游戏开发中,为了节省资源,我们常常修改png图片以实现一张图片多种显示效果。有两种办法可以实现这个功能:
第一种是装载png图片,使用getRGB()取得取得图片的RGB颜色数据,然后修改RGB颜色数据,再用Image的静态方法createRGBImage()将修改后的RGB颜色数据生成新的png图片。示例代码:testImage为测试的Image对象,imgW,imgH为其宽和高
// 取得图片的RGB数据--这个数组是比较大的,因此修改RGB数据生成新的图片效率很低
rgbData = new int[imgW * imgH];
testImage.getRGB(rgbData,0,imgW,0,0,imgW,imgH);
// 修改RGB数据并生成新的图片
rgbImage = Image.createRGBImage(changeRGBData(rgbData),imgW,imgH,true);
第二中方法是取得png图片的二进制数据,修改其中的调色板域(PLTE chunk)数据,再使用
Image的静态对象createImage(byte[] imageData,int imageOffset,int imageLength)将修改后的二进制数据生成新的png对象。示例代码:查看代码中的getPLTEModifidImage()函数。
如果对png的数据格式组成不熟悉,可以查阅:http://www.w3.org/TR/PNG/
或者(不错的中文介绍): http://www.ismyway.com/png/png-struct1.htm
通常,图片的RGB颜色数据是比较大的,而调色板数据远比RGB颜色数据要少的多,修改RGB颜色数据效率要高的多。下面我使用一张大小为52*28的png图片作为测试图片,从以下测试中打印的数据就可看出(1456对比144):
Load file Gold total data is 1084 ――――――――图片的二进制字节数据个数
--------RGB data length is 1456―――――――――RGB颜色字节数据个数
--------The number of PLTE chunks is 48―――调色板数据块个数(实际字节数据为48*3)
效果图:
附测试的源代码:
//-----------------------------------------------------Code--START------------------------------------------------
/**
* Dicription : Create png image by modifying PLTE chunk.
* Author : 飘飘白云
* Created date : 2006-07-03
* Modified history :
*/
import java.io.DataInputStream;
import java.io.IOException;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
public class CreatePNG extends MIDlet
{
GameCanvas canvas;
Display display;
public CreatePNG()
{
super();
// TODO Auto-generated constructor stub
try {
display = Display.getDisplay(this);
canvas = new GameCanvas(this);
} catch (Exception e) {}
}
protected void startApp() throws MIDletStateChangeException
{
// TODO Auto-generated method stub
if( canvas != null ){
display.setCurrent(canvas);
canvas.start();
}
}
protected void pauseApp()
{
// TODO Auto-generated method stub
canvas.beExit = true;
}
protected void destroyApp( boolean arg0) throws MIDletStateChangeException
{
// TODO Auto-generated method stub
}
}
class GameCanvas extends Canvas implements Runnable
{
CreatePNG app;
Thread mainThread;
Image offImage = null;
public Graphics g = null;
public boolean beExit = false;
long sleepTime = 30;
int scrW;
int scrH;
Font font;
int fontH;
int fontW;
//-------------------------------------
int gameMode;
int subMode;
static final int smInit = 0;
static final int smProc = 1;
static final int smEnd = 2;
static final int gmStart = 0;
static final int gmProcPNGData = 1;
//--------------------------------------
Image testImage = null;
Image rgbImage = null;
Image plteImage = null;
int imgW;
int imgH;
int[] rgbData = null;
byte[] imgData = null;
String testFile = "Gold";
public GameCanvas(MIDlet midlet)
{
app = (CreatePNG)midlet;
}
public void start()
{
changeGameMode(gmStart);
mainThread = new Thread(this);
mainThread.start();
}
public void changeGameMode(int gm)
{
gameMode = gm;
subMode = smInit;
}
protected void paint( Graphics _g)
{
// TODO Auto-generated method stub
if( offImage != null)
_g.drawImage(offImage, 0, 0, 0);
}
public void run()
{
// TODO Auto-generated method stub
gameInit();
while( !beExit )
{
gameMain();
gameDraw();
repaint();
gcWait(sleepTime);
}
if( beExit ) {
try {
app.destroyApp(true);
}
catch ( MIDletStateChangeException e ) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
void gcWait(long tm)
{
System.gc();
try {
Thread.sleep(tm);
}
catch ( InterruptedException e ) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
void println(String str)
{
System.out.println( str );
}
void print(String str)
{
System.out.print( str );
}
void gameInit()
{
scrW = getWidth();
scrH = getHeight();
if(g == null || offImage == null) {
offImage = Image.createImage(scrW, scrH);
g = offImage.getGraphics();
g.translate(0,0);
}
repaint();
font = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN,Font.SIZE_MEDIUM);
g.setFont(font);
changeGameMode( gmStart );
}
/**
* 装载图片
* @param fileName 图片名称
* @return image 对象
*/
Image loadImage( String fileName)
{
Image tmp = null;
try {
tmp = Image.createImage("/" + fileName + ".png");
}
catch ( IOException e ) {
println("[Error] >> loadImage(): " + fileName + " Failed!");
e.printStackTrace();
}
if( tmp == null ) {
println("[Error] >> loadImage(): " + fileName + " Failed!");
}
return tmp;
}
/**
* 读取文件的二进制数据
* @param fileName 文件名
* @return 文件二进制数据的字节数组.
*/
byte[] loadFile(String fileName)
{
DataInputStream isr = null;
byte[] bData;
try {
try {
isr = new DataInputStream(getClass().getResourceAsStream("/" + fileName + ".png"));
}
catch (Exception e) {
}
int res_size = 0;
while (isr.read() != -1)
res_size++;
if (isr.markSupported())
isr.reset();
else {
isr.close();
isr = new DataInputStream(getClass().getResourceAsStream("/" + fileName + ".png"));
}
bData = new byte[res_size];
int read_bytes = isr.read(bData, 0, res_size);
isr.close();
System.gc();
println("Load file " + fileName + " total data is " + read_bytes);
return bData;
}
catch (Exception e) {
println("Error>>loadFile " + fileName);
return null;
}
}
void gameMain()
{
if( gameMode == gmStart )
{
if( subMode == smInit )
{
// 装载图片
testImage = loadImage(testFile);
imgW = testImage.getWidth();
imgH = testImage.getHeight();
subMode = smProc ;
}
else if( subMode == smProc )
{
// 取得图片的RGB数据--这个数组是比较大的,因此修改RGB数据生成新的图片效率很低
rgbData = new int[imgW * imgH];
testImage.getRGB(rgbData,0,imgW,0,0,imgW,imgH);
println("--------RGB data length is - " + rgbData.length);
changeGameMode(gmProcPNGData);
}
}
else if( gameMode == gmProcPNGData )
{
if( subMode == smInit )
{
// 取得图片的二进制数据
imgData = loadFile(testFile);
subMode = smProc ;
println("----Start---All Image Data--length-" + imgData.length);
for( int i = 0; i< imgData.length; i++ ){
if( i% 10 == 0 )
println("");
System.out.print(" " + imgData[i]);
}
println("\n----End---All Image Data--length-" + imgData.length);
// 修改调色板数据,改变图片颜色,这种方法修改的数据远比修改RGB数据要少,效率高很多
plteImage = getPLTEModifidImage(imgData,3);
}
else if( subMode == smProc )
{
// 修改RGB数据生成新的图片,非常低效
rgbImage = Image.createRGBImage(changeRGBData(rgbData),imgW,imgH,true);
subMode = smEnd;
}
else if( subMode == smEnd )
{
}
}
}
void gameDraw()
{
if( gameMode == gmStart )
{
if( subMode == smProc ){
// 描绘原始图片
g.drawImage(testImage,20,20,0);
}
}
else if( gameMode == gmProcPNGData )
{
if( subMode == smProc ){
// 描绘修改调试板数据生成的图片
g.drawImage(plteImage,20,80,0);
}
else if( subMode == smEnd ) {
// 描绘修改RGB数据生成的图片,也可以使用drawRGB()直接描绘RGB数据,不过要注意透明问题.
g.drawImage(rgbImage,20,140,0);
}
}
}
/**
* 修改png图片的调色板数据生成新的png图片
* @param imageSrc png图片的二进制数据字节数组
* @param type 修改策略
* @return 新的png图片
*/
public Image getPLTEModifidImage(byte[] imageSrc , int type)
{
if (imageSrc == null || imageSrc.length <= 1)
return null;
if (crcTable == null)
makeCrcTable();
// PLTE chunk数据域的类型标识
// see http://www.w3.org/TR/PNG/#11PLTE
String[] sPLTE = {"50", "4c", "54", "45"};
int i,j;
int pos = 0,startPos = 0;
byte[] data = imageSrc;
for (i = 0; i < data.length; i++)
{
if (Integer.toHexString(data[i]).equals(sPLTE[0])
&& Integer.toHexString(data[i + 1]).equals(sPLTE[1])
&& Integer.toHexString(data[i + 2]).equals(sPLTE[2])
&& Integer.toHexString(data[i + 3]).equals(sPLTE[3]))
{
pos = i;
break;
}
}
pos -= 4;
startPos = pos;
println("\n======================start Palette data process========================");
println("-------------Palette chunk start pos = " + pos);
// 取得PLTE chunk数据域的数据长度().
int imageNbColors = (
((data[pos] << 24) & 0xff000000)
| ((data[pos + 1] << 16) & 0x00ff0000)
| ((data[pos + 2] << 8 ) & 0x0000ff00)
| ((data[pos + 3] ) & 0x000000ff));
// 计算的PLTE chunk数据个数(每个PLTE chunk数据由R,G,B三个字节数据组成)
imageNbColors = imageNbColors/3;
// 为整形的PLTE chunk data分配空间
int imageRGBColors[] = new int[ imageNbColors ];
//12 = 数据长度(4个字节) + 类型标识(4个字节) + 校验码(4个字节)
for( i = pos,j = 0; i < pos + 12 + imageNbColors * 3 ; i++,j++ ){
if( j >= 8 && (j - %3 == 0 ) {
println("");
}
System.out.print(" " + data[i]);
}
pos += 8;
println("\n--------The number of PLTE chunks is " + imageNbColors + "------------");
if (imageRGBColors == null)
return null;
// 生成整形的PLTE chunk data
for( i = 0; i < imageNbColors; i++ )
{
imageRGBColors[i] = (
(data[pos + 0] & 0x000000ff) << 16)
| ((data[pos + 1] & 0x000000ff) <<
| ((data[pos + 2] & 0x000000ff));
if( i % 10 == 0 && i != 0) {
println("");
}
print(" " + imageRGBColors[i]);
pos += 3;
}
// 修改 PLTE chunk data
switch (type)
{
case 0:
// don't modify
break;
case 1: {
int l,r,g,b;
// gray
for (j = 1; j < imageNbColors; j++) {
r = imageRGBColors[j];
g = (r & 0x00FF00) >> 8;
b = r & 0x0000FF;
r = (r & 0xFF0000) >> 16;
l = (b + g * 6 + r * 3) / 16;
imageRGBColors[j] = l << 16 | l << 8 | l;
}
break;
}
case 2: {
// white
for (j = 1; j < imageNbColors; j++)
imageRGBColors[j] = 0xFFFFFF;
break;
}
case 3: {
int l,r,g,b;
// red
for (j = 1; j < imageNbColors; j++) {
r = imageRGBColors[j];
g = (r & 0x00FF00) >> 8;
b = r & 0x0000FF;
r = (r & 0xFF0000) >> 16;
l = (b + g * 6 + r * 3) / 10;
imageRGBColors[j] = l << 16;
}
break;
}
}
// 生成新的 PLTE chunk data
pos = startPos + 8;
for( i = 0; i < imageNbColors ;i++)
{
data[pos ] = (byte)((imageRGBColors[i] >> 16) ) ;
data[pos + 1 ] = (byte)((imageRGBColors[i] >> );
data[pos + 2] = (byte)(imageRGBColors[i] );
pos += 3;
}
// 更新 CRC 校验码
int crc = updateCrcChunk( data, startPos + 4, startPos + 4 + 4 + ( imageNbColors * 3 ) );
data[pos + 0] = (byte)(crc >> 24 & 0x000000FF);
data[pos + 1] = (byte)(crc >> 16 & 0x000000FF);
data[pos + 2] = (byte)(crc >> 8 & 0x000000FF);
data[pos + 3] = (byte)(crc & 0x000000FF);
pos = startPos;
println("\n---------------------New plte data crc " + crc +"--------------------------------");
for( i = 0; i < 4 + 4 + ( imageNbColors * 3 ) + 4; i++ )
{
if( i >= 8 && (i - %3 == 0 ) {
println("");
}
print(" " + data[pos + i]);
}
println("\n=========================End plte data-=================================");
return Image.createImage(data,0,data.length);
}
/**
* 修改png图片的RGB数据
* @param rgbSrc png图片的RGB数据的字节数组
* @return 新的RGB数据的字节数组
*/
public int[] changeRGBData(int[] rgbSrc)
{
int len = rgbSrc.length;
int[] ret = new int[len];
int a,r,g,b;
int tmp;
for( int i = 0; i < len; i++ )
{
b = rgbSrc[i];
a = ((b & 0xff000000) >> 24);
r = ((b & 0x00ff0000) >> 16);
g = ((b & 0x0000ff00) >> ;
b = ((b & 0x000000ff) >> 0);
// exchange red and blue
tmp = r;
r = b;
b = tmp;
ret[i] = (a << 24) | (r << 16) | (g << | b;
}
return ret;
}
// CRC校验表,加速CRC计算
static long crcTable[];
static public void makeCrcTable()
{
long c;
int n, k;
crcTable = new long[256];
for (n = 0; n < 256; n++) {
c = (long) n;
for (k = 0; k < 8; k++) {
if ((c & 1) == 1) {
c = 0xEDB88320L ^ (c >> 1);
}
else {
c = c >> 1;
}
}
crcTable[n] = c;
}
}
/**
* 计算CRC校验码
*/
static public int updateCrcChunk(byte[] buf, int dataOffsetStart,int dataOffsetEnd)
{
long c = 0xFFFFFFFFL;
for (int i = dataOffsetStart; i < dataOffsetEnd; i++)
c = (crcTable[(int) ((c ^ buf[i]) & 0xFF)] ^ (c >> );
return (int) (c ^ 0xFFFFFFFFL);
}
}
//-----------------------------------------------------Code------END----------------------------------------------
//2
游戏帧速度处理
保持游戏在不同性能手机平台的流畅性很重要,这就需要针对不通硬件设置相应合适的帧速度。假设一般
手机用户能接受的游戏帧速为40/s 则相关参数和代码如下:
//====================================================
// 游戏帧速度调整
//====================================================
private long framerate= 1000/40; // 设置帧速为每秒40帧,(25 ms / frame)
private long frameStart; // 帧开始的时间time the frame begin
private long frameCount = 0; // 这一秒的帧数
private long elapsedTime; // 一帧消耗的时间
private long totalElapsedTime = 0; // 多帧累计消耗的时间
private long reportedFramerate; // 实际计算出来的帧速
public void updateFrame()
{
frameStart = System.currentTimeMillis();
repaint();
elapsedTime = System.currentTimeMillis() - frameStart;
//帧速同步
try {
if( elapsedTime < framerate ) {
Thread.sleep(framerate - elapsedTime);
}else {
Thread.sleep(5);
}
}catch( Exception e ) {
e.printStackTrace();
}
//更新实际的帧速
++ frameCount;
totalElapsedTime += (System.currentTimeMillis() - frameStart);
if( totalElapsedTime > 1000 ) {
reportedFramerate = (long)((double)frameCount/(double)
totalElapsedTime*1000.0);
//在调试时可屏幕中显示帧速
//g.drawString("fps: " + reportedFramerate,x,y,0);
frameCount = 0;
totalElapsedTime = 0;
}
}
////3
J2me中实现淡入淡出效果
飘飘白云(l_zhaohui@163.com)
在J2me中实现淡入淡出效果,据我所知至少有三种方法。
第一种是取得需要变换图片的像素,依次设置每个象素的alpha通道值,让它在0~100之间变化。
第二种是修改图片的调色板数据,让其在调色板原始数据到255之间变化。
第三种,其实也是利用上面的办法,先描画图片,然后在图片上覆盖一个黑色矩形,改变这个黑色矩形透明度就可以实现淡入淡出的效果。
前两种方法相比较的话,第一种方法运算量是比较大的,而且第一种方法由于midp1.0不支持alpha通道,在一些手机上无法实现。
下面给出第二种方法的示例,在我们开始之前,应该熟悉png文件格式,如果还不是很明白的话,可以google一下,或者查看前面的帖子中的相关连接。
代码很清楚,下面是源代码:
------------CODE__START-----------------------
import java.io.DataInputStream;
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
/**
* Discription : 修改调色板数据实现淡入淡出效果
* Author : 飘飘白云(l_zhaohui@163.com)
* Created date : 2006/07/13 18:06:39
* Modified history :
*/
public class PngFadeInOut extends MIDlet
{
GameCanvas canvas;
Display display;
public PngFadeInOut()
{
super();
try {
display = Display.getDisplay(this);
canvas = new GameCanvas(this);
} catch (Exception e) {}
}
protected void startApp() throws MIDletStateChangeException
{
if( canvas != null ){
display.setCurrent(canvas);
canvas.start();
}
}
protected void pauseApp()
{
canvas.isExit = true;
}
protected void destroyApp( boolean arg0) throws MIDletStateChangeException
{
}
}
class GameCanvas extends Canvas
{
static String imageName = "/fadeInOut.png";
public static final int MAX_TARDINESS = 30;
//---------------------------------------------------
PngFadeInOut app;
boolean isInited = false;
public boolean isExit = false;
Image offImage = null;
public Graphics g = null;
int scrW;
int scrH;
//---------------------------------------------------
int gameMode;
int subMode;
static final int smInit = 0;
static final int smProc = 1;
static final int smEnd = 2;
static final int gmStart = 0;
//---------------------------------------------------
Image testImage = null;
byte[] oldRGBData = null;
byte[] imgData = null;
static final int FADE_IN = 0;
static final int FADE_OUT = 1;
static final int FADE_STEP = 1;
static int fadeType = FADE_IN;
//---------------------------------------------------
//---Timer process
//---------------------------------------------------
TimerTask task;
Timer timer;
int timerDelayTime;
public boolean isTimerRunning;
void startTimer(int delayTime){
timerDelayTime = delayTime;
isTimerRunning = true;
timer = new Timer();
task = new ProcessingRunner();
timer.scheduleAtFixedRate(task, 0, delayTime);
}
public void stopTimer(){
if( task != null )
task.cancel();
isTimerRunning = false;
timer = null;
task = null;
}
//-----------------------------------------------------
class ProcessingRunner extends TimerTask
{
public void run()
{
if( isExit ) {
stopTimer();
app.notifyDestroyed();
}
if (!isTimerRunning || System.currentTimeMillis() -
scheduledExecutionTime() >= timerDelayTime)
{
return;
}
gameMain();
gameDraw();
repaint();
serviceRepaints();
System.gc();
}
}
//-------------------------------------------------
public GameCanvas(MIDlet midlet)
{
app = (PngFadeInOut)midlet;
}
public void start()
{
setFullScreenMode(true);
while (!isShown())
;
if( !isInited ) {
gameInit();
isInited = true;
}
startTimer(MAX_TARDINESS);
}
void gameInit()
{
scrW = getWidth();
scrH = getHeight();
if(g == null || offImage == null) {
offImage = Image.createImage(scrW, scrH);
g = offImage.getGraphics();
}
changeGameMode(gmStart);
}
public void changeGameMode(int gm){
gameMode = gm;
subMode = smInit;
}
//---------------------------------------------------
// 游戏主逻辑处理
//---------------------------------------------------
void gameMain()
{
if( gameMode == gmStart )
{
if( subMode == smInit )
{
// 取得图片的二进制数据
imgData = loadFile(imageName);
//保存原始图片的调色板rgb数据
oldRGBData = getPlteRGBData(imgData);
if( fadeType == FADE_IN) {
// 设置调色板颜色为白色,淡入效果
for(int i = rgbDataStartPos; i < rgbDataStartPos + rgbDataLength;i++ ) {
imgData[i] = (byte)255;
}
}
// 生成新的图片
testImage = createFadeImage(imgData,rgbDataStartPos,rgbDataLength);
subMode = smProc ;
}
else if( subMode == smProc )
{
try {
// 淡入淡出效果处理
fade(fadeType);
testImage = createFadeImage(imgData,rgbDataStartPos,rgbDataLength);
}
catch ( Exception e ) {
println("Create fade image error. \n" + e);
}
}
}
}
//---------------------------------------------------
// 游戏描画处理
//---------------------------------------------------
protected void paint( Graphics _g){
if( offImage != null)
_g.drawImage(offImage, 0, 0, 0);
}
void gameDraw()
{
if( gameMode == gmStart )
{
if( subMode == smProc ){
g.setColor(0xffffff);
g.fillRect(0,0,scrW,scrH);
g.drawImage(testImage,20,140,0);
}
}
}
/**
* 淡入淡出效果处理
* @param type
*/
public void fade( int type)
{
int m = 0;
int n = 0;
// 淡入
if ( type == FADE_IN ) {
for( int i = 0; i < rgbDataLength; i++ )
{
m = imgData[rgbDataStartPos + i] & 0x000000ff;
n = oldRGBData[i] & 0x000000ff;
m = m < 0? m + 256 : m;
n = n < 0? n + 256 : n;
m = m > n ? m - FADE_STEP : n;
imgData[rgbDataStartPos + i] = (byte)(m & 0xff);
}
}
// 淡出
else if ( type == FADE_OUT ) {
for( int i = 0; i < rgbDataLength; i++ )
{
m = imgData[rgbDataStartPos + i] & 0x000000ff;
m = m < 0? m + 256 : m;
m = m < 255 ? m + FADE_STEP : 255;
imgData[rgbDataStartPos + i] = (byte)(m & 0xff);
}
}
}
/**
* 这里不是单纯创建一幅图片,crc32校验码也在这个函数里更新
* @param data
* @param startPos 需要校验的数据的起始位置
* @return
*/
public Image createFadeImage(byte[] data,int startPos,int length)
{
if (crcTable == null)
makeCrcTable();
int endPos = startPos + length ;
int crc = updateCrcChunk( data, startPos -8,endPos );
data[endPos + 0] = (byte)(crc >> 24 & 0x000000FF);
data[endPos + 1] = (byte)(crc >> 16 & 0x000000FF);
data[endPos + 2] = (byte)(crc >> 8 & 0x000000FF);
data[endPos + 3] = (byte)(crc & 0x000000FF);
return Image.createImage(data,0,data.length);
}
static int rgbDataStartPos = 0;
static int rgbDataLength = 0;
/**
* @param imgData png图片的二进制数据
* @return 调色板颜色数据的字节数组
*/
public byte[] getPlteRGBData(byte[] imgData)
{
if (imgData == null || imgData.length <= 1)
return null;
// PLTE chunk数据域的类型标识
// see http://www.w3.org/TR/PNG/#11PLTE
String[] sPLTE = {"50", "4c", "54", "45"};
int i;
int pos = 0;
//定位调色板数据的位置
for (i = 0; i < imgData.length; i++)
{
if (Integer.toHexString(imgData[i]).equals(sPLTE[0])
&& Integer.toHexString(imgData[i + 1]).equals(sPLTE[1])
&& Integer.toHexString(imgData[i + 2]).equals(sPLTE[2])
&& Integer.toHexString(imgData[i + 3]).equals(sPLTE[3]))
{
pos = i;
break;
}
}
pos -= 4;
// 取得PLTE chunk的字节数据长度.
int imageNbColors = (
((imgData[pos] << 24) & 0xff000000)
| ((imgData[pos + 1] << 16) & 0x00ff0000)
| ((imgData[pos + 2] << 8 ) & 0x0000ff00)
| ((imgData[pos + 3] ) & 0x000000ff));
//8 = 数据长度(4个字节) + 类型标识(4个字节)
pos += 8;
// 设置全局变量
rgbDataStartPos = pos;
rgbDataLength = imageNbColors;
//复制数据
byte[] data = new byte[rgbDataLength ];
for( i = 0 ; i < rgbDataLength ; i++){
data[i] = imgData[i + rgbDataStartPos ];
}
return data;
}
// CRC校验查找表,加速CRC32运算
static long crcTable[];
static public void makeCrcTable()
{
long c;
int n, k;
crcTable = new long[256];
for (n = 0; n < 256; n++) {
c = (long) n;
for (k = 0; k < 8; k++) {
if ((c & 1) == 1) {
c = 0xEDB88320L ^ (c >> 1);
}
else {
c = c >> 1;
}
}
crcTable[n] = c;
}
}
/**
* 计算crc32校验码
*/
static public int updateCrcChunk(byte[] buf, int dataOffsetStart,int dataOffsetEnd)
{
long c = 0xFFFFFFFFL;
for (int i = dataOffsetStart; i < dataOffsetEnd; i++)
c = (crcTable[(int) ((c ^ buf[i]) & 0xFF)] ^ (c >> );
return (int) (c ^ 0xFFFFFFFFL);
}
/**
* 读取文件的二进制数据
* @param fileName 文件名
* @return 文件二进制数据
*/
byte[] loadFile(String fileName)
{
DataInputStream isr = null;
byte[] bData;
try {
try {
isr = new DataInputStream(getClass().getResourceAsStream(fileName));
}
catch (Exception e) {
}
int res_size = 0;
while (isr.read() != -1)
res_size++;
if (isr.markSupported())
isr.reset();
else {
isr.close();
isr = new DataInputStream(getClass().getResourceAsStream(fileName));
}
bData = new byte[res_size];
int read_bytes = isr.read(bData, 0, res_size);
isr.close();
System.gc();
return bData;
}
catch (Exception e) {
println("Error>>loadFile " + fileName);
return null;
}
}
static void println(String str){
System.out.println( str );
}
}
----------------------CODE__END-------------------------------
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/liu01983zm/archive/2006/08/30/1143132.aspx