📄 palettedimage.java
字号:
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.microedition.lcdui.Image;
/**
* PalettedImage调色板图片类,使用PNG-8图片,可更换其中的颜色。 <br>
* 注意,除了更换调色板中的颜色之外,这个类实际上是不可更改的Image。
*
* @author flyingghost
* @version 1.0
*/
public final class PalettedImage {
private Image image;//生成的图片
private byte[] imgData;//图片字节数据
private int paletteOffset;//调色板偏移量
private int CRCOffset;//CRC校验码偏移量
private int paletteColors;//调色板颜色数
private boolean needRebuild;//改变,需要重建立图片
private PalettedImage() {
}
/**
* 从字节数组中创建PalettedImage
*
* @param data
* 存放png图片的字节数组
* @return PalettedImage对象
* @throws NullPointerException
* 当data数组为空时
*/
public static PalettedImage createPalettedImage(byte[] data) {
if (data == null) {
throw new NullPointerException();
}
PalettedImage pImage = null;
pImage = new PalettedImage();
pImage.imgData = data;
pImage.analyze();
pImage.image = Image.createImage(pImage.imgData, 0,
pImage.imgData.length);
return pImage;
}
/**
* 从图片文件中创建PalettedImage
*
* @param filename
* png图片文件路径
* @return PalettedImage对象
* @throws NullPointerException
* 当filename为空时
* @throws IllegalArgumentException
* 当路径无效的时候
* @throws IOException
* 当发生IO错误时
*/
public static PalettedImage createPalettedImage(String filename)
throws IOException {
if (filename == null) {
throw new NullPointerException();
}
PalettedImage pImage = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
is = filename.getClass().getResourceAsStream(filename);
if (is == null) {//创建不成功
throw new IllegalArgumentException();
}
baos = new ByteArrayOutputStream();
int ch = 0;
while ((ch = is.read()) != -1) {
baos.write(ch);
}
}
catch (IOException ioe) {
throw ioe;
}
finally {
if (baos != null) {
try {
baos.close();
}
catch (IOException e) {
}
}
if (is != null) {
try {
is.close();
}
catch (IOException e) {
}
}
}
pImage = createPalettedImage(baos.toByteArray());
return pImage;
}
/**
* 取得生成的Image对象
*
* @return Image对象
*/
public Image getImage() {
if (needRebuild) {
rebuild();
}
return image;
}
/**
* 设置调色板
*
* @param colors
* 调色板的所有颜色数组
* @throws NullPointerException
* 如果颜色数组为空
* @throws IllegalArgumentException
* 颜色数组长度和颜色个数不符
*/
public void setPalette(int[] colors) {
if (colors == null) {
throw new NullPointerException();
}
if (colors.length != paletteColors) {//长度不符
throw new IllegalArgumentException();
}
for (int i = 0, offset = paletteOffset; i < colors.length; i++, offset += 3) {
imgData[offset] = (byte) ((colors[i] >> 16) & 0xff);
imgData[offset + 1] = (byte) ((colors[i] >> 8) & 0xff);
imgData[offset + 2] = (byte) ((colors[i]) & 0xff);
}
needRebuild = true;
}
/**
* 取得调色板的颜色数
*
* @return 颜色数
*/
public int getColorCount() {
return paletteColors;
}
/**
* 设置某种颜色
*
* @param index
* 颜色索引号
* @param color
* 新的颜色值,以0xRRGGBB的格式
* @throws IllegalArgumentException
* 当索引号不在范围内时
*/
public void setColor(int index, int color) {
if (index < 0 || index >= paletteColors) {
throw new IllegalArgumentException();
}
int offset = paletteOffset + index * 3;
imgData[offset] = (byte) ((color >> 16) & 0xff);
imgData[offset + 1] = (byte) ((color >> 8) & 0xff);
imgData[offset + 2] = (byte) ((color) & 0xff);
needRebuild = true;
}
/**
* 替换某种颜色
*
* @param oldColor
* 要替换的颜色值,0xRRGGBB格式
* @param newColor
* 新的颜色值,0xRRGGBB格式
*/
public void replaceColor(int oldColor, int newColor) {
byte rr = (byte) ((oldColor >> 16) & 0xff);
byte gg = (byte) ((oldColor >> 8) & 0xff);
byte bb = (byte) (oldColor & 0xff);
for (int i = 0, offset = paletteOffset; i < paletteColors; i++, offset += 3) {
if (rr == imgData[offset] && gg == imgData[offset + 1]
&& bb == imgData[offset + 2]) {//找到
imgData[offset] = (byte) ((newColor >> 16) & 0xff);
imgData[offset + 1] = (byte) ((newColor >> 8) & 0xff);
imgData[offset + 2] = (byte) (newColor & 0xff);
needRebuild = true;
break;
}
}
}
/**
* 取得调色板颜色
*
* @param index
* 颜色索引号
* @return int形式的颜色值
* @throws IllegalArgumentException
* 当索引号不在范围内时
*/
public int getColor(int index) {
if (index < 0 || index >= paletteColors) {
throw new IllegalArgumentException();
}
int offset = paletteOffset + index * 3;
return ((imgData[offset] & 0xFF) << 16)
| ((imgData[offset + 1] & 0xFF) << 8)
| (imgData[offset + 2] & 0xFF);
}
private void rebuild() {//根据新的字节数据重建Image
CRCChecksum();//重新计算校验和
image = Image.createImage(imgData, 0, imgData.length);
needRebuild = false;
System.gc();
}
private void analyze() {//分析调色板相关资料
int offset = 8;//跳过8字节的PNG头,遍历每个块
int chunkLen = 0;
while (imgData[offset + 4] != 0x50 || imgData[offset + 5] != 0x4c
|| imgData[offset + 6] != 0x54 || imgData[offset + 7] != 0x45) {//offset指向的块名称不是PLTE
chunkLen = readInt(offset);
offset += (4 + 4 + chunkLen + 4);
}
chunkLen = readInt(offset);//块长
paletteColors = chunkLen / 3;//颜色数
paletteOffset = offset + 8;//调色板偏移量
CRCOffset = offset + 8 + chunkLen;//CRC校验码偏移量
}
private int readInt(int offset) {//读一个int
return ((imgData[offset] & 0xFF) << 24)
| ((imgData[offset + 1] & 0xFF) << 16)
| ((imgData[offset + 2] & 0xFF) << 8)
| (imgData[offset + 3] & 0xFF);
}
private void CRCChecksum() {//求得新的校验和
int checksum = CRCUtil.checksum(imgData, paletteOffset - 4,
paletteColors * 3 + 4);
imgData[CRCOffset] = (byte) ((checksum >> 24) & 0xff);
imgData[CRCOffset + 1] = (byte) ((checksum >> 16) & 0xff);
imgData[CRCOffset + 2] = (byte) ((checksum >> 8) & 0xff);
imgData[CRCOffset + 3] = (byte) ((checksum) & 0xff);
}
}
final class CRCUtil {
private static int[] crc_table;//CRC 表
private static void make_crc_table() {
int c;
int n, k;
crc_table = new int[256];
for (n = 0; n < 256; n++) {
c = n;
for (k = 0; k < 8; k++) {
if ((c & 1) == 1)
c = 0xedb88320 ^ (c >>> 1);
else
c = c >>> 1;
}
crc_table[n] = c;
}
}
private static int update_crc(byte[] buf, int off, int len) {
int c = 0xffffffff;
int n;
if (crc_table == null) {
make_crc_table();
}
for (n = off; n < len + off; n++) {
c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >>> 8);
}
return c;
}
static int checksum(byte[] buf, int off, int len) {
return update_crc(buf, off, len) ^ 0xffffffff;
}
CRCUtil() {
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -