c++ 处理png文件(1)获取各个chunk及crc32测试
笔记,记录,自用
参考 :PNG文件格式详解 【CRC32校验算法进行校验】 GZIP file format specification version 4.3
#include<iostream>
#include<fstream>
#include<string>
#include <iomanip>
#include <cstdint>
#include<vector>
using namespace std;
/// @brief crc32校验
/// @param data 指向数据起始位置的指针
/// @param dataLen 数据的字节数
/// @param ori_crc 给出的crc
/// @param crcLen 默认crc长度为4字节
/// @return bool 给出的crc和算出的crc是否一致
bool crc32(unsigned char * data, int dataLen, unsigned char * ori_crc, int crcLen=4){
unsigned long crc_table[256];
unsigned long c=0,o=0;
int n, k;
for (n = 0; n < 256; n++) {
c = (unsigned long) n;
for (k = 0; k < 8; k++) {
if (c & 1) {
c = 0xedb88320L ^ (c >> 1);
} else {
c = c >> 1;
}
}
crc_table[n] = c;
}
c = 0xffffffffL;
for (n = 0; n < dataLen; n++) {
c = crc_table[(c ^ data[n]) & 0xff] ^ (c >> 8);
}
c = c ^ 0xffffffffL;
// std::cout << "crc=0x" << std::setfill('0') << std::setw(8) << std::hex << c << std::endl;
o |= ((unsigned long)(*(ori_crc)) & 0xff) << 24;
o |= ((unsigned long)(*(ori_crc+1)) & 0xff) << 16;
o |= ((unsigned long)(*(ori_crc+2)) & 0xff) << 8;
o |= (unsigned long)(*(ori_crc+3)) & 0xff;
return c == o;
}
/// @brief chunk类
class chunk{
public:
char cName[5];
unsigned int start;
unsigned int len;
/// @brief 构造函数
/// @param name chunk块的“符号”
/// @param s 块起始位置
/// @param l 块内容的长度
chunk(char * name,unsigned int s,unsigned int l){
cName[0]=name[0]; cName[1]=name[1];
cName[2]=name[2]; cName[3]=name[3];
cName[4]='\0';
start = s; len = l;
}
};
/// @brief 自己的文件类
class MYFILE{
public:
unsigned char * byte;
bool isRead = false;
unsigned int fileLen=0;
vector<chunk> v;
/// @brief 构造函数
/// @param filePath 文件的路径/文件名
MYFILE(string filePath){
std::ifstream fs;
fs.open(filePath, ios_base::binary|ios::ate);
if(!fs)
{
std::cerr << "无法打开文件" << std::endl;
isRead = false;
return;
}
fileLen = fs.tellg();
byte = new unsigned char[fileLen+1];
fs.seekg(0, ios::beg);
fs.read((char *)byte, fileLen);
fs.close();
isRead = true;
}
/// @brief 将文件中从某字节开始的向后四字节转为unsigned long
/// @param n 开始字节到文件开头的长度
/// @return unsigned long
unsigned long nextU32(unsigned int n){
unsigned int re=0;
re |= uIntAt(n) << 24;
re |= uIntAt(n+1) << 16;
re |= uIntAt(n+2) << 8;
re |= uIntAt(n+3);
return re;
}
/// @brief 对PNG文件分析,将各个chunk找出来并保存到vector中
void analyze(){
int i=8;
unsigned int len;
while(i<fileLen){
len = nextU32(i);
v.push_back(chunk((char *)(byte+i+4),i,len));
i += 4+4+len+4;
// 如果是IEND块,结束
if(nextU32(i-4) == 0x49454E44) break;
}
}
/// @brief 简单输出一下都有什么chunk和每个chunk的起始位置及内容的长度
void showChunks(){
cout << "chunk: start :len\n";
for(chunk c:v){
cout << c.cName << " :" << setw(10) << c.start << ":" << c.len << endl;
}
}
/// @brief 返回指向文件第n字节保存位置的指针
/// @param n unsigned int 到文件开头的字节数
/// @return unsigned char *
unsigned char * ptrAt(unsigned int n){
if(n < fileLen)return byte+n;
else return nullptr;
}
/// @brief 返回文件第n字节转成的unsigned int
/// @param n unsigned int 到文件开头的字节数
/// @return unsigned int
unsigned int uIntAt(unsigned int n){
if(n < fileLen)return (unsigned int)(*(byte+n)) & 0xff;
else return 0;
}
/// @brief 输出文件第n字节的16进制表示
/// @param n unsigned int 到文件开头的字节数
void printHexAt(unsigned int n){
unsigned int nn = (unsigned int)(*(byte+n)) & 0xff;
std::cout << std::setfill('0') << std::setw(2) << std::hex << nn;
}
/// @brief 判断这个文件是不是PNG文件
/// @return bool 是否为PNG文件
bool isPngFile(){
return (uIntAt(0)==0x89)&(uIntAt(1)==0x50)&(uIntAt(2)==0x4e)&(uIntAt(3)==0x47)&(uIntAt(4)==0x0d)&(uIntAt(5)==0x0a)&(uIntAt(6)==0x1a)&(uIntAt(7)==0x0a);
}
/// @brief 如果为png文件,返回图片的宽
/// @return unsigned int
unsigned int getPngwidth(){
unsigned int n=0;
n |= uIntAt(19);
n |= uIntAt(18)<<8;
n |= uIntAt(17)<<16;
n |= uIntAt(16)<<24;
return n;
}
/// @brief 如果为png文件,返回图片的高
/// @return unsigned int
unsigned int getPngHeight(){
unsigned int n=0;
n |= uIntAt(23);
n |= uIntAt(22)<<8;
n |= uIntAt(21)<<16;
n |= uIntAt(20)<<24;
return n;
}
/// @brief 检测Png文件的IHDR块的crc对不对
/// @return bool
bool IHDRcrc(){return crc32(byte+12,17,byte+29);}
~MYFILE(){
delete byte;
}
};
int main(){
MYFILE mf("1.png");
if(mf.isRead){
if(mf.isPngFile()){
cout << "0.png is a png file! \n";
cout << "width=" << mf.getPngwidth();
cout << " height=" << mf.getPngHeight();
cout << " bitDepth=" << mf.uIntAt(24) << endl;
if(mf.IHDRcrc()){
cout << "IHDR crc is right\n";
} else {cout << "IHDR crc is wrong\n";}
mf.analyze();
mf.showChunks();
}
else {cout << "0.png isn't a png file\n";}
for(int i=1;i<=160;++i){
mf.printHexAt(i-1);
cout << " ";
if(i%16 == 0)cout << endl;
}
}
return 0;
}
一个示例输出:
0.png is a png file!
width=533 height=365 bitDepth=8
IHDR crc is right
chunk: start :len
IHDR : 8:13
sRGB : 33:1
gAMA : 46:4
pHYs : 62:9
IDAT : 83:25516
IEND : 25611:0
89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52
00 00 02 15 00 00 01 6d 08 06 00 00 00 b0 7d 98
4f 00 00 00 01 73 52 47 42 00 ae ce 1c e9 00 00
00 04 67 41 4d 41 00 00 b1 8f 0b fc 61 05 00 00
00 09 70 48 59 73 00 00 0e c3 00 00 0e c3 01 c7
6f a8 64 00 00 63 ac 49 44 41 54 78 5e ed dd 7f
70 14 d7 99 2f fc af c0 80 bd 46 24 d8 bb 11 12
d8 d8 c0 e0 8d ac c5 06 61 5f 5b 72 d9 c9 9a 10
24 e4 a0 10 47 ce de ec 16 f1 ae 23 b9 28 af d0
6e 95 6b 49 60 53 5b 79 21 c1 4b ed 2d 49 e5 ba