c++ 处理png文件(1)获取各个chunk及crc32测试

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今夕何夕2112

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值