public void LoadImage(String strUrl) {
DataInputStream inputDate = null;
try {
inputDate = new DataInputStream(new FileInputStream(strUrl));
System.out.println(inputDate.available());
inputDate.skipBytes(8); //跳过文件头
/**IHDR数据块**/
inputDate.skipBytes(4); //Chunk length
inputDate.skipBytes(4); //0x49484452为Chunk Type "IHDR"
m_width = inputDate.readInt(); //宽度
m_height = inputDate.readInt(); //高度
m_depth = inputDate.readByte(); //位深度
inputDate.skipBytes(4); //压缩时舍掉的4byte,默认0x03000000
inputDate.skipBytes(4); //跳过IHDR文件CRC信息
SkipDate(inputDate,3); //跳过3个数据块
m_PLTE = SaveDate(inputDate); //保存PLTE数据块
SkipDate(inputDate, 6); //跳过6个数据块
m_IDAT = SaveDate(inputDate); //保存IDAT数据块
inputDate.close();
} catch (Exception ex) {
System.out.println("错误读入" + ex);
}
} /**保存当前数据块**/
private byte[] SaveDate(DataInputStream inputDate) throws IOException {
//跳过3个数据块
int tmplength = inputDate.readInt(); //读取Chunk length
System.out.println("*******************保存数据块*******************"+tmplength);
inputDate.skipBytes(4); //跳过数据块Chunk Type Code
byte[] tmparray = new byte[tmplength];
for (int i = 0; i < tmparray.length; ++i) {
tmparray[i] = inputDate.readByte(); //位深度
}
inputDate.skipBytes(4); //跳过IHDR文件CRC信息
return tmparray;
} /**跳过指定数量的数据块**/
private void SkipDate(DataInputStream inputDate, int num) throws IOException {
System.out.println("============跳过数据=============="+num);
for (int i = num; i > 0; --i) { //cHRM 基色和白色点数据块
int tmplength = inputDate.readInt(); //cHRM信息length
System.out.println(tmplength);
inputDate.skipBytes(8 + tmplength); //跳过IHDR文件CRC信息]
System.out.println(i);
}
}
//以上是我写的提取有效数据的代码,但是每次运行之后,读取宽度和高度是正确的,但是从保存第一个数据块开始就由于数组过大,把后面的数据全部读取完毕了,所以后面的关键数据块没有数据可以读取了!
读取的数据块排列顺序是下面这个表
DataInputStream inputDate = null;
try {
inputDate = new DataInputStream(new FileInputStream(strUrl));
System.out.println(inputDate.available());
inputDate.skipBytes(8); //跳过文件头
/**IHDR数据块**/
inputDate.skipBytes(4); //Chunk length
inputDate.skipBytes(4); //0x49484452为Chunk Type "IHDR"
m_width = inputDate.readInt(); //宽度
m_height = inputDate.readInt(); //高度
m_depth = inputDate.readByte(); //位深度
inputDate.skipBytes(4); //压缩时舍掉的4byte,默认0x03000000
inputDate.skipBytes(4); //跳过IHDR文件CRC信息
SkipDate(inputDate,3); //跳过3个数据块
m_PLTE = SaveDate(inputDate); //保存PLTE数据块
SkipDate(inputDate, 6); //跳过6个数据块
m_IDAT = SaveDate(inputDate); //保存IDAT数据块
inputDate.close();
} catch (Exception ex) {
System.out.println("错误读入" + ex);
}
} /**保存当前数据块**/
private byte[] SaveDate(DataInputStream inputDate) throws IOException {
//跳过3个数据块
int tmplength = inputDate.readInt(); //读取Chunk length
System.out.println("*******************保存数据块*******************"+tmplength);
inputDate.skipBytes(4); //跳过数据块Chunk Type Code
byte[] tmparray = new byte[tmplength];
for (int i = 0; i < tmparray.length; ++i) {
tmparray[i] = inputDate.readByte(); //位深度
}
inputDate.skipBytes(4); //跳过IHDR文件CRC信息
return tmparray;
} /**跳过指定数量的数据块**/
private void SkipDate(DataInputStream inputDate, int num) throws IOException {
System.out.println("============跳过数据=============="+num);
for (int i = num; i > 0; --i) { //cHRM 基色和白色点数据块
int tmplength = inputDate.readInt(); //cHRM信息length
System.out.println(tmplength);
inputDate.skipBytes(8 + tmplength); //跳过IHDR文件CRC信息]
System.out.println(i);
}
}
//以上是我写的提取有效数据的代码,但是每次运行之后,读取宽度和高度是正确的,但是从保存第一个数据块开始就由于数组过大,把后面的数据全部读取完毕了,所以后面的关键数据块没有数据可以读取了!
读取的数据块排列顺序是下面这个表
解决方案 »
- 泛型类中如何得到泛型T的class
- protected的访问权限
- 怎样用ant将hibernate用GB2312编译?
- JTable 标题设置
- 请教!
- 有点不明白这个for循环。
- 菜鸟问题,如何关闭JDialog啊?
- 强烈推荐:很多好书,包括中文版的《thinking in java》,下载速度暴快!!
- 我在用JAVA命令执行编译好的CLASS文件时提示 EXCEPTION IN THREAD MAIN java.lang.noclassdeffounderError之类的信息,不知是何原因,请高手帮忙,谢谢 ,哪里有JDK1。3的国际版下载?
- 如何使用log4j,让生成的日志文件以当天日期为名称
- 这段代码有一点不明白,请高手帮帮忙,谢谢!
- java窗体问题?
Length (长度) 4字节 指定数据块中数据域的长度,其长度不超过(231-1)字节
Chunk Type Code (数据块类型码) 4字节 数据块类型码由ASCII字母(A-Z和a-z)组成
Chunk Data (数据块数据) 可变长度 存储按照Chunk Type Code指定的数据
CRC (循环冗余检测) 4字节 存储用来检测是否有错误的循环冗余码
我也没看太明白,那个Length 是Chunk Data 的长度还是 这4个东西的总和得实验下要是总和的话可以直接用,不是的话还得+上另外3个4字节吧
关于压缩部分——不用重复发明轮子。你要做的事情,现成的API已经搞定了。加密,不是很理解,在png文件内部压缩吗?读取这个文件的就只能你自己的程序了。
名称 字节数 说明
Length(长度) 4字节 指定数据块中数据域的长度,其长度不超过
(231-1)字节
Chunk Type Code(数据块类型码) 4字节 数据块类型码由ASCII字母(A-Z和a-z)组成
Chunk Data(数据块数据) 可变长度 存储按照Chunk Type Code指定的数据
CRC(循环冗余检测) 4字节 存储用来检测是否有错误的循环冗余码
Chunk Data(数据块数据)的长度,我的理解是Length(长度)里面保存的长度!
按这个理解
int tmplength = inputDate.readInt(); //cHRM信息length
System.out.println(tmplength);
inputDate.skipBytes(8 + tmplength); //跳过IHDR文件CRC信息]
这个语句应该可以跳过一个数据块的吧!跳过数据块貌似也是正常的!
从这里开始
int tmplength = inputDate.readInt(); //读取Chunk length
System.out.println("*******************保存数据块*******************"+tmplength);
inputDate.skipBytes(4); //跳过数据块Chunk Type Code
byte[] tmparray = new byte[tmplength];
for (int i = 0; i < tmparray.length; ++i) {
tmparray[i] = inputDate.readByte(); //位深度
}
inputDate.skipBytes(4); //跳过IHDR文件CRC信息
我是先保存这个当前数据块长度,然后跳过Chunk Type Code4个字节
然后再读取数据域的数据for;最后再跳过CRC数据4个字节,问题貌似就出在这里,第一次读取的length大的离谱,所以导致后面没有数据可以读取了!
Width 4 bytes 图像宽度,以像素为单位
Height 4 bytes 图像高度,以像素为单位
Bit depth 1 byte 图像深度:
索引彩色图像:1,2,4或8
灰度图像:1,2,4,8或16
真彩色图像:8或16
ColorType 1 byte 颜色类型:
0:灰度图像, 1,2,4,8或16
2:真彩色图像,8或16
3:索引彩色图像,1,2,4或8
4:带α通道数据的灰度图像,8或16
6:带α通道数据的真彩色图像,8或16
Compression method 1 byte 压缩方法(LZ77派生算法)
Filter method 1 byte 滤波器方法
Interlace method 1 byte 隔行扫描方法:
0:非隔行扫描
1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法) IHDR是13字节,你跳了15。http://www.j2medev.com/Article/ShowArticle.asp?ArticleID=232
参考下
inputDate.skipBytes(4); //Chunk length
inputDate.skipBytes(4); //0x49484452为Chunk Type "IHDR"
m_width = inputDate.readInt(); //宽度
m_height = inputDate.readInt(); //高度
m_depth = inputDate.readByte(); //位深度
inputDate.skipBytes(4); //压缩时舍掉的4byte,默认0x03000000
inputDate.skipBytes(4); //跳过IHDR文件CRC信息
4个4 =16。
m_height = inputDate.readInt(); //高度 4byte
m_depth = inputDate.readByte(); //位深度 1byte
inputDate.skipBytes(4); //压缩时舍掉的4byte,默认0x03000000 4byte
13个啊,哪里跳过了15个?请指教
public void read() throws Exception{
DataInputStream inputDate = new DataInputStream(new FileInputStream("d:\\1.png"));
byte[] bytes=new byte[inputDate.available()];
inputDate.read(bytes);
byte[] IDATbyte ="IDAT".getBytes();
byte[] IENDbyte ="IEND".getBytes(); int sgin = 0;//查找标志
sgin = find(bytes,IDATbyte,0);//查找第一个IDAT块标志
int IDATStrar = sgin-4;//向前4位,为第一个IDAT块的起始位置
int temp = sgin;
do{
sgin = temp;
temp = find(bytes,IDATbyte,sgin+4);//向前4为跳过"IDAT"
}while(temp!=-1);//IDAT块为连续块,直接查找到最后一个IDAT int IDATDataLength = ((bytes[sgin-4]&0x000000ff)<<24)|((bytes[sgin-4+1]&0x000000ff)<<16)|((bytes[sgin-4+2]&0x000000ff)<<8)|(bytes[sgin-4+3]&0x000000ff);//byte转int
int IDATEnd =sgin +3 + IDATDataLength +4;//数据块的结构 +3是因为sgin指向"IDAT"的I了
sgin = find(bytes,IENDbyte,IDATEnd);//IEND块在IDAT后
int IENDStrar = sgin-4;//向前4位,为IEND块的起始位置
int IENDDataLength = ((bytes[sgin-4]&0x000000ff)<<24)|((bytes[sgin-4+1]&0x000000ff)<<16)|((bytes[sgin-4+2]&0x000000ff)<<8)|(bytes[sgin-4+3]&0x000000ff);//byte转int
int IENDEnd =sgin +3 + IENDDataLength +4;//数据块的结构 +3是因为sgin指向"IEND"的I了
System.out.println("IDAT在bytes中的位置为bytes["+IDATStrar+"]-bytes["+IDATEnd+"]");
System.out.println("IEND在bytes中的位置为bytes["+IENDStrar+"]-bytes["+IENDEnd+"]");
}
/**
* 在bytes 中查找 findbytes
* @param bytes (byte[]) 目标
* @param findbytes (byte[]) 查找条件
* @param start (int) 查找起始位置实
* @return int bytes中findbytes的起始位置
*/
private int find( byte[] bytes,byte[] findbytes,int start){
if(bytes!=null && bytes.length>0 && findbytes!=null && findbytes.length>0 && bytes.length>start && start>=0){
for(int i = start;i<bytes.length;i++){
for(int j = 0;j<findbytes.length;j++){
if(bytes[i+j] != findbytes[j]){//不符合直接break
break;
}else{
if(j == findbytes.length-1){//完全符合findbytes中的字符
return i;//返回字符起始位置
}
}
}
}
}
return -1;//没查到
}
public static void main(String args[]){
try {
new test().read();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
我用byte[]做的,你实验下吧,流做的那个我也找不到原因。
这么做的目的是PNG文件厘米有很多信息是就算不同图片也是雷同的,所有就可以砍掉,从这个角度是压缩了PNG原始文件,从而节省出手机游戏开发时那珍贵的byte,砍掉这些数据的同时已经算是简单的加密了!一般人是不会把你的图片解码出来,然而为了真正的加密,就需要混淆真实数据等等!这样在内部范围内,就有了自己的加密手段!
DataInputStream inputDate = null;
try {
inputDate = new DataInputStream(new FileInputStream(strUrl));
int length = 0;
System.out.println(length = inputDate.available());
pngByteArray = new byte[length];
inputDate.read(pngByteArray);
String tmpstr="";
byte[] tmparraycHRM = "cHRM".getBytes();
byte[] tmparraygAMA = "gAMA".getBytes();
byte[] tmparraycsBIT = "sBIT".getBytes();
byte[] tmparrayIHDR = "IHDR".getBytes();
byte[] tmparrayPLTE = "PLTE".getBytes();
byte[] tmparrayIDAT = "IDAT".getBytes(); int a = 0;
if ((a = FindData(tmparrayIHDR)) != -1) {
System.out.println("IHDR数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("IHDR没有出现");
}
if ((a = FindData(tmparraycHRM)) != -1) {
System.out.println("cHRM数据块出现位置" + (a - 4));
} else {
System.out.println("cHRM没有出现");
}
if ((a = FindData(tmparraygAMA)) != -1) {
System.out.println("gAMA数据块出现位置" + (a - 4));
} else {
System.out.println("gAMA没有出现");
}
if ((a = FindData(tmparraycsBIT)) != -1) {
System.out.println("sBIT数据块出现位置" + (a - 4));
} else {
System.out.println("sBIT没有出现");
}
if ((a = FindData(tmparrayPLTE)) != -1) {
System.out.println("PLTE数据块出现位置" + (a - 4));
} else {
System.out.println("PLTER没有出现");
}
if ((a = FindData(tmparrayIDAT)) != -1) {
System.out.println("IDAT数据块出现位置" + (a - 4));
} else {
System.out.println("IDAT没有出现");
}
inputDate.close();
} catch (Exception ex) {
System.out.println("错误读入" + ex);
}
}
验证结果:
83423
IHDR数据块出现位置8
cHRM数据块出现位置2705
gAMA没有出现
sBIT没有出现
PLTER没有出现
IDAT数据块出现位置2749
/------------------数据块表---------------------/
数据块符号 数据块名称 多数据块 可选否 位置限制
IHDR 文件头数据块 否 否 第一块
cHRM 基色和白色点数据块 否 是 在PLTE和IDAT之前
gAMA 图像γ数据块 否 是 在PLTE和IDAT之前
sBIT 样本有效位数据块 否 是 在PLTE和IDAT之前
PLTE 调色板数据块 否 是 在IDAT之前
bKGD 背景颜色数据块 否 是 在PLTE之后IDAT之前
hIST 图像直方图数据块 否 是 在PLTE之后IDAT之前
tRNS 图像透明数据块 否 是 在PLTE之后IDAT之前
oFFs (专用公共数据块) 否 是 在IDAT之前
pHYs 物理像素尺寸数据块 否 是 在IDAT之前
sCAL (专用公共数据块) 否 是 在IDAT之前
IDAT 图像数据块 是 否 与其他IDAT连续
tIME 图像最后修改时间数据块 否 是 无限制
tEXt 文本信息数据块 是 是 无限制
zTXt 压缩文本数据块 是 是 无限制
fRAc (专用公共数据块) 是 是 无限制
gIFg (专用公共数据块) 是 是 无限制
gIFt (专用公共数据块) 是 是 无限制
gIFx (专用公共数据块) 是 是 无限制
IEND 图像结束数据 否 否 最后一个数据块
/:验证发现,验证结果是,真实PNG图片于此表不符!
/------------------数据块表---------------------/
数据块符号 数据块名称 多数据块 可选否 位置限制
IHDR 文件头数据块 否 否 第一块
cHRM 基色和白色点数据块 否 是 在PLTE和IDAT之前
gAMA 图像γ数据块 否 是 在PLTE和IDAT之前
sBIT 样本有效位数据块 否 是 在PLTE和IDAT之前
PLTE 调色板数据块 否 是 在IDAT之前
bKGD 背景颜色数据块 否 是 在PLTE之后IDAT之前
hIST 图像直方图数据块 否 是 在PLTE之后IDAT之前
tRNS 图像透明数据块 否 是 在PLTE之后IDAT之前
oFFs (专用公共数据块) 否 是 在IDAT之前
pHYs 物理像素尺寸数据块 否 是 在IDAT之前
sCAL (专用公共数据块) 否 是 在IDAT之前
IDAT 图像数据块 是 否 与其他IDAT连续
tIME 图像最后修改时间数据块 否 是 无限制
tEXt 文本信息数据块 是 是 无限制
zTXt 压缩文本数据块 是 是 无限制
fRAc (专用公共数据块) 是 是 无限制
gIFg (专用公共数据块) 是 是 无限制
gIFt (专用公共数据块) 是 是 无限制
gIFx (专用公共数据块) 是 是 无限制
IEND 图像结束数据 否 否 最后一个数据块
/:仔细观察这个表就发现,表内其实有一部分数据块的位置没有限制,这就导致,你所遇到的PNG图片,你根本不知道,在PLTE或者IDAT之前到底有几个数据块,或者根本就没有PLTE,所以导致按照表跳过数据会失败的一塌糊涂!
werosh 非常感谢你的启发和帮助
以下是我验证的代码和结果:
public void ReadImage(String strUrl) {
DataInputStream inputDate = null;
try {
inputDate = new DataInputStream(new FileInputStream(strUrl));
int length = 0;
System.out.println(length = inputDate.available());
pngByteArray = new byte[length];
inputDate.read(pngByteArray);
String tmpstr = "";
byte[] tmparraycHRM = "cHRM".getBytes();
byte[] tmparraygAMA = "gAMA".getBytes();
byte[] tmparraycsBIT = "sBIT".getBytes();
byte[] tmparrayIHDR = "IHDR".getBytes();
byte[] tmparrayPLTE = "PLTE".getBytes();
byte[] tmparrayIDAT = "IDAT".getBytes();
byte[] tmparrayIEND = "IEND".getBytes();
byte[] tmparraygIFx = "gIFx".getBytes();
byte[] tmparraycgIFt = "gIFt".getBytes();
byte[] tmparraygIFg = "gIFg".getBytes();
byte[] tmparrayfRAc = "fRAc".getBytes();
byte[] tmparrayzTXt = "zTXt".getBytes();
byte[] tmparraytEXt = "tEXt".getBytes();
byte[] tmparraytIME = "tIME".getBytes();
byte[] tmparraycsCAL = "sCAL".getBytes();
byte[] tmparraypHYs = "pHYs".getBytes();
byte[] tmparrayoFFs = "oFFs".getBytes();
byte[] tmparraytRNS = "tRNS".getBytes();
byte[] tmparrayhIST = "hIST".getBytes();
byte[] tmparraybKGD = "bKGD".getBytes();
int a = 0;
if ((a = FindData(tmparrayIHDR)) != -1) {
System.out.println("IHDR数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("IHDR没有出现");
}
if ((a = FindData(tmparraypHYs)) != -1) {
System.out.println("pHYs数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("pHYs没有出现");
}
if ((a = FindData(tmparraycHRM)) != -1) {
System.out.println("cHRM数据块出现位置" + (a - 4));
} else {
System.out.println("cHRM没有出现");
}
if ((a = FindData(tmparrayPLTE)) != -1) {
System.out.println("PLTE数据块出现位置" + (a - 4));
} else {
System.out.println("PLTE没有出现");
}
if ((a = FindData(tmparrayIDAT)) != -1) {
System.out.println("IDAT数据块出现位置" + (a - 4));
} else {
System.out.println("IDAT没有出现");
}
if ((a = FindData(tmparraycgIFt)) != -1) {
System.out.println("gIFt数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("gIFt没有出现");
}
if ((a = FindData(tmparraygIFx)) != -1) {
System.out.println("gIFx数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("gIFx没有出现");
}
if ((a = FindData(tmparrayIEND)) != -1) {
System.out.println("IEND数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("IEND没有出现");
}
//
if ((a = FindData(tmparraytIME)) != -1) {
System.out.println("tIME数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("tIME没有出现");
}
if ((a = FindData(tmparraytEXt)) != -1) {
System.out.println("tEXt数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("tEXt没有出现");
}
if ((a = FindData(tmparrayzTXt)) != -1) {
System.out.println("zTXt数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("zTXt没有出现");
}
if ((a = FindData(tmparrayfRAc)) != -1) {
System.out.println("fRAc数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("fRAc没有出现");
}
if ((a = FindData(tmparraygIFg)) != -1) {
System.out.println("gIFg数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("gIFg没有出现");
}
//
if ((a = FindData(tmparraybKGD)) != -1) {
System.out.println("bKGD数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("bKGD没有出现");
}
if ((a = FindData(tmparrayhIST)) != -1) {
System.out.println("hIST数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("hIST没有出现");
}
if ((a = FindData(tmparraytRNS)) != -1) {
System.out.println("tRNS数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("tRNS没有出现");
}
if ((a = FindData(tmparrayoFFs)) != -1) {
System.out.println("oFFs数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("oFFs没有出现");
} if ((a = FindData(tmparraycsCAL)) != -1) {
System.out.println("sCAL数据块出现位置" + (a - 4));
// System.out.println("IHDR数据块总长度" + (a - 4));
} else {
System.out.println("sCAL没有出现");
} if ((a = FindData(tmparraygAMA)) != -1) {
System.out.println("gAMA数据块出现位置" + (a - 4));
} else {
System.out.println("gAMA没有出现");
}
if ((a = FindData(tmparraycsBIT)) != -1) {
System.out.println("sBIT数据块出现位置" + (a - 4));
} else {
System.out.println("sBIT没有出现");
}
inputDate.close();
} catch (Exception ex) {
System.out.println("错误读入" + ex);
}
}
private int FindData(byte[] tmparray) {
for (int i = 0; i < pngByteArray.length; ++i) {
if (tmparray[0] == pngByteArray[i]) {
int j = 1;
for (j = 1; j < tmparray.length; ++j) {
if (tmparray[j] != pngByteArray[i + j]) {
break;
}
}
if (j == tmparray.length) {
return i;
}
}
}
return -1;
}
//:结果
IHDR数据块出现位置8
pHYs没有出现
cHRM没有出现
PLTE数据块出现位置70
IDAT数据块出现位置130
gIFt没有出现
gIFx没有出现
IEND数据块出现位置659
tIME没有出现
tEXt数据块出现位置33
zTXt没有出现
fRAc没有出现
gIFg没有出现
bKGD没有出现
hIST没有出现
tRNS没有出现
oFFs没有出现
sCAL没有出现
gAMA没有出现
sBIT没有出现
备注:用不同的PNG图片验证你会发现数据块数量和位置非常诡异!