PNG转YUV(自造轮子版)

前置知识

综述

png是由多个chunk组成,每个chunk保存了该png文件的某项信息。

其中数据部分被称作IDAT,里面存有png文件的图像信息。为了减小数据量,该块使用LZ77的衍生算法Deflate算法进行压缩,并使用zlib头进行包裹。

大致结构为:zlib头+deflate头+数据+校验码

被压缩压缩数据部分按照如下方式进行存储。
先存储长度数组,这个数组用于生成一颗霍夫曼树。
用生成的霍夫曼树对接下来的数据进行解码,获得长度码表和距离码表。
最后用长度码表和距离码表对数据进行恢复。

由于找不到合适图片且时间有限,该版本代码只适用于8bit带调色板并使用动态霍夫曼编码且IDAT仅有一块的PNG文件。

本次实验使用的文件如下:
实验用文件

过程

获取文件信息

声明所需要的信息。

struct fileHeader
{
    unsigned char head[8];
    void GetHead(ifstream& in) { in.read((char*)head, 8); }
};
struct iHDR
{
    unsigned int width, height;
    unsigned char bit_depth, color_type, compression_method, filter_method, interlace_method;
    void GetIHDR(unsigned char* buffer);
    void Print();
};
struct chunks
{
    unsigned int length;
    char type[4];
    string s_type = "";
    unsigned char* data;
    unsigned char CRC[4];
    chunks();
    void GetChunk(ifstream& in);
};

添加如下实现:

#include "header.h"

void iHDR::Print()
{
    cout << "width: " << (int)width << " height: " << height << endl;
    cout << "bit depth: " << (int)bit_depth << endl;
    cout << "color type: " << (int)color_type << endl;
    cout << "compression method: " << (int)compression_method << endl;
    cout << "interlace method : " << (int)interlace_method << endl;
}
void iHDR::GetIHDR(unsigned char* buffer)
{
    for (int i = 0; i < 4; ++i) { width = (width << 8) + buffer[i]; }
    for (int i = 0; i < 4; ++i) { height = (height << 8) + buffer[i + 4]; }
    int pos = 8;
    for (auto i : { &bit_depth, &color_type, &compression_method, &filter_method, &interlace_method })
        *i = buffer[pos++];
}
chunks::chunks()
{
    length = 0;
    memset(type, 0, sizeof(type));
    data = nullptr;
    s_type = "";
    memset(CRC, 0, sizeof(CRC));
}
void chunks::GetChunk(ifstream& in)
{
    unsigned char* buffer = new unsigned char[4];
    in.read((char*)buffer, 4);
    for (int i = 0; i < 4; ++i) { length = (length << 8) + buffer[i]; }
    in.read(type, 4);
    if (length != 0) 
    {
        data = new unsigned char[length];
        in.read((char*)data, length);
    }
    in.read((char*)CRC, 4);
    for (auto i : type) s_type += (int)i;
    return;
}

运行如下代码:

int main()
{
    ifstream in(in_path, ios::binary);
    if (!in.is_open())
    {
        cout << in_path << " open failed." << endl;
        exit(1);
    }
    fileHeader file_header;
    file_header.GetHead(in);
    int pos = 0;
    while(true)
    {
        chunks tmp;
        tmp.GetChunk(in);
        chunks_vector.push_back(tmp);
        mp[tmp.s_type].push_back(pos++);
        if (tmp.s_type == "IEND") break;
    }

    for (auto &i : mp)
    {
        cout << i.first << ": ";
        for (auto& j : i.second)
            cout << j << " ";
        cout << endl;
    }
    int position = mp["IHDR"][0];
    iHDR ihdr; ihdr.GetIHDR(chunks_vector[position].data);
    cout << endl;  ihdr.Print();
}

结果如下:
在这里插入图片描述
可以看到该png文件所拥有的数据块以及基本信息。
具体如下:

名称取值
Width80
Height80
Bit depth8位
ColorType3,带有调色板
Compression method0 ,deflate压缩算法
Filter method0,即滤波方法 0
Interlace method0,非隔行扫描

获取调色板信息

添加定义:

struct pLTE
{
    int r, g, b;
    static pLTE* GetPLTE(unsigned char* buffer, iHDR ihdr);
};

进行实现:

pLTE* pLTE::GetPLTE(unsigned char* buffer, iHDR ihdr)
{
    int plte_size = 1 << ihdr.bit_depth;
    pLTE* plte = new pLTE[plte_size];
    int pos = 0;
    for (int i = 0; i < plte_size; ++i)
    {
        for (auto j : { &plte[i].r, &plte[i].g, &plte[i].b })
            *j = buffer[pos++];
    }
    return plte;
}

继续添加如下代码获取调色板信息:

    position = mp["PLTE"][0];
    pLTE* plte = pLTE::GetPLTE(chunks_vector[position].data, ihdr);

对IDAT数据块进行解码

编写位处理工具

由于PNG文件的数据部分并不按照字节为单位而是以bit为单位进行信息的存储,所以需要编写一个为处理工具来对这些数据进行解码。
添加如下声明:

class dataReader
{
private:
    unsigned char* buffer;
    unsigned int bit_pos, byte_pos, pos_in_byte, length;
public:
    dataReader();
    void GetData(ifstream& in);
    void GetData(unsigned char* in_buffer, int length);
    unsigned int ReadNextBit();
    unsigned int ReadBits(int n);
    unsigned int ReadBitsR(int n);
    unsigned int ReadBytes(int n);
    void ReadBytes(unsigned char* buffer, int length);
    void GetPos();
};

进行实现:

#include "header.h"

void dataReader::GetPos()
{
    cout << "Byte pos: " << byte_pos << endl;
    cout << "Bit pos: " << bit_pos << endl;
    cout << "Pos in Byte: " << pos_in_byte << endl;
    cout << "total length: " << length << endl;
}
dataReader::dataReader()
{
    buffer = nullptr;
    bit_pos = byte_pos = pos_in_byte = length = 0;
}

void dataReader::GetData(ifstream& in)
{
    in.seekg(0, ios::end);
    length = in.tellg();
    in.seekg(0, ios::beg);
    buffer = new unsigned char[length];
    in.read((char*)buffer, length);
}
void dataReader::GetData( unsigned char* in_buffer, int length ) 
{ 
    buffer = in_buffer; 
    this->length = length;
}

unsigned int dataReader::ReadNextBit()
{
    if (byte_pos >= length)
    {
        cout << "Out of boundary." << endl;
        exit(1);
    }
    unsigned int ans = 0;
    ans = (buffer[byte_pos] & (1 << pos_in_byte)) >> pos_in_byte;
    bit_pos++; pos_in_byte++;
    if (pos_in_byte == 8)
    {
        pos_in_byte = 0;
        byte_pos++;
    }
    return ans;
}
unsigned int dataReader::ReadBits(int n)
{
    unsigned int ans = 0;
    for (int i = 0; i < n; ++i)
        ans = ans | (ReadNextBit() << i);
    return ans;
}
unsigned int dataReader::ReadBitsR(int n)
{
    unsigned int ans = 0;
    for (int i = 0; i < n; ++i)
        ans = (ans << 1) | ReadNextBit();
    return ans;
}
unsigned int dataReader::ReadBytes(int n)
{
    unsigned int ans = 0;
    for (int i = 0; i < n; ++i)
    {
        ans = (ans << 8) | ReadBits(8);
    }
    return ans;
}
void dataReader::ReadBytes(unsigned char* buffer, int length)
{
    for (int i = 0; i < length; ++i)
        buffer[i] = this->buffer[byte_pos++];
}

解码zlib和Defalte格式的文件头

为了方便,将二者合在一起进行处理。
添加如下定义:

struct IDATheader
{
    unsigned int CM, CINFO, FCHECK, FDICT, FLEVEL, DICTCHECK, CHECKSUM;
    unsigned int BFINAL, BTYPE;
    unsigned int LEN, NLEN;
    unsigned int NLIT, NDIST, NCLEN;
    void GetZlibHeader(dataReader& data_reader);
    void GetDeflateHeader(dataReader& data_reader);
    void Print();
};

进行实现:

void IDATheader::GetZlibHeader(dataReader &data_reader)
{
    CM = data_reader.ReadBits(4);
    CINFO = data_reader.ReadBits(4);
    FCHECK = data_reader.ReadBits(5);
    FDICT = data_reader.ReadBits(1);
    FLEVEL = data_reader.ReadBits(2);
    if (FDICT) DICTCHECK = data_reader.ReadBytes(4);

}
void IDATheader::GetDeflateHeader(dataReader& data_reader)
{
    BFINAL = data_reader.ReadNextBit();
    BTYPE = data_reader.ReadBits(2);
    switch (BTYPE)
    {
    case 0:
        break;
    case 1:
        break;
    case 2:
    {
        NLIT = data_reader.ReadBits(5) + 257;
        NDIST = data_reader.ReadBits(5) + 1;
        NCLEN = data_reader.ReadBits(4) + 4;
        break;
    }
    }
}
void IDATheader::Print()
{
    cout << "CM: " << CM << endl;
    cout << "CINFO: " << CINFO << endl;
    cout << "FDICT: " << FDICT << endl;
    cout << "BIFNAL: " << BFINAL << endl;
    cout << "BTYPE: " << BTYPE << endl;
    cout << "NLIt: " << NLIT << endl;
    cout << "NDIST: " << NDIST << endl;
    cout << "NCLEN: " << NCLEN << endl;
}

主程序中添加如下代码:

    IDATheader idat_header;
    position = mp["IDAT"][0];
    data_reader.GetData(chunks_vector[position].data, chunks_vector[position].length);
    idat_header.GetZlibHeader(data_reader); idat_header.GetDeflateHeader(data_reader);
    idat_header.Print();

可以获得IDAT数据块的信息:
在这里插入图片描述

构建霍夫曼树

添加声明:

class huffmanTree
{
private:
    int value;
    huffmanTree* left, * right;
public:
    huffmanTree() { value = -1; left = right = nullptr; }
    void Update(int index, unsigned int codes, unsigned int len);
    void Build(vector< unsigned int > codes, vector<unsigned int> length);
    int Decode();
    void Print(string s = "");
};

进行实现:

huffmanTree::huffmanTree() { value = -1; left = right = nullptr; }
void huffmanTree::Update(int index, unsigned int codes, unsigned int len)
{
    if (len == 0)
    {
        value = index;
        left = right = nullptr;
        return;
    }

    if (codes & (1u << (len - 1)))
    {
        if (nullptr == right)
            right = new huffmanTree;
        right->Update(index, codes, len - 1);
    }
    else
    {
        if (nullptr == left)
            left = new huffmanTree;
        left->Update(index, codes, len - 1);
    }
}
void huffmanTree::Build(vector< unsigned int > codes, vector<unsigned int> length)
{
    int len = codes.size();
    for (int i = 0; i < len; ++i) if (length[i]) Update(i, codes[i], length[i]);
}
int huffmanTree::Decode()
{
    if (value != -1) return value;
    auto sym = data_reader.ReadNextBit();
    if (sym) right->Decode();
    else left->Decode();
}
void huffmanTree::Print(string s)
{
    if (value != -1)
    {
        cout << value << "\t" << s << endl;
        return;
    }
    if (nullptr != right)
        right->Print(s + '1');
    if (nullptr != left)
        left->Print(s + '0');
}

获取霍夫曼码表

添加如下函数声明:

vector< unsigned int > GetCodes(vector<unsigned int> length);
vector< unsigned int > DecodeLength(int num);
void GetCodeLengthAphabet(int NCLEN);
void GetOherAlphabet(int NLIT, int NDIST);

定义三个霍夫曼表:

huffmanTree code_len_alphabet, len_alphabet, dis_alphabet;

进行实现:

vector< unsigned int > GetCodes(vector<unsigned int> length)
{
    int len = length.size();
    vector< unsigned int > frequent(len), next_code(len), codes(len);
    for (auto i : length) if (i) frequent[i] ++;
    for (int i = 1; i < len; ++i) next_code[i] = (next_code[i - 1] + frequent[i - 1]) << 1;
    for (int i = 0; i < len; ++i) if (length[i]) codes[i] = next_code[length[i]] ++;
    return codes;
}
vector< unsigned int > DecodeLength(int num)
{
    vector<unsigned int> length;
    while (length.size() != num)
    {
        auto sym = code_len_alphabet.Decode();
        unsigned int t = 0;
        if (sym <= 15) length.push_back(sym);
        else if (sym == 16)
        {
            t = data_reader.ReadBits(2) + 3;
            auto back = length.back();
            while (t--) length.push_back(back);
        }
        else if (sym == 17)
        {
            t = data_reader.ReadBits(3) + 3;
            while (t--) length.push_back(0);
        }
        else
        {
            t = data_reader.ReadBits(7) + 11;
            while (t--) length.push_back(0);
        }
    }
    return length;
}
void GetCodeLengthAphabet( int NCLEN )
{
    vector< int > index = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
    vector< unsigned int > length(19);
    for (int i = 0; i < NCLEN; ++i)
        length[index[i]] = data_reader.ReadBits(3);
    auto codes = GetCodes(length);
    code_len_alphabet.Build(codes, length);
}
void GetOherAlphabet( int NLIT, int NDIST )
{
    auto len_length = DecodeLength(NLIT);
    auto dis_length = DecodeLength(NDIST);
    auto len_codes = GetCodes(len_length);
    auto dis_codes = GetCodes(dis_length);
    len_alphabet.Build(len_codes, len_length);
    dis_alphabet.Build(dis_codes, dis_length);
}

在主函数中添加如下代码:

    GetCodeLengthAphabet(idat_header.NCLEN);
    GetOherAlphabet(idat_header.NLIT, idat_header.NDIST);
    dis_alphabet.Print();

以distance表为例,可以看到编码后的码表:
在这里插入图片描述

进行解码

添加声明:

vector< unsigned char > DecodeData();

进行实现:

unsigned int len_ex_bits[] = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 };
unsigned int len_base[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,0 };
unsigned int dis_ex_bits[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
unsigned int dis_base[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577 };
vector< unsigned char > DecodeData()
{
    vector< unsigned char > data;
    while (1)
    {
        auto sym = len_alphabet.Decode();
        if (sym == 256)
            return data;
        else
        {
            if (sym < 256) { data.push_back((unsigned char)sym); continue; }
            sym -= 257;
            auto len = len_base[sym] + data_reader.ReadBits(len_ex_bits[sym]);
            sym = dis_alphabet.Decode();
            auto dis = dis_base[sym] + data_reader.ReadBits(dis_ex_bits[sym]);
            for (int i = 0; i < len; ++i)
            {
                auto t = data[data.size() - dis];
                data.push_back(t);
            }
        }
    }
}

这是就得到了每个像素对应的调色板索引。

格式转换

添加声明:

void png2yuv(const string out_yuv_path, int width, int height, vector<unsigned char> data_buffer, pLTE* plte);

进行实现:

void png2yuv( const string out_yuv_path, int width, int height, vector<unsigned char> data_buffer, pLTE* plte)
{
    unsigned char* y, * u, * v;
    int y_size = width * height;
    int uv_size = y_size / 4;
    y = new unsigned char[y_size];
    u = new unsigned char[uv_size];
    v = new unsigned char[uv_size];
    int buffer_size = data_buffer.size();
    int uv_pos = 0, y_pos = 0;
    for (int i = 0; i < buffer_size; ++i)
    {
        if ((i + 1) % (width + 1) == 0) continue;
        int r, g, b; auto plte_tmp = plte[data_buffer[i]];
        r = plte_tmp.r, g = plte_tmp.g, b = plte_tmp.b;
        int h = i / (width + 1), w = i % (width + 1);
        y[y_pos++] = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
        if ((h & 1) || (w & 1)) continue;
        u[uv_pos] = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
        v[uv_pos++] = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
    }
    ofstream out(out_yuv_path, ios::binary);
    out.write((char*)y, y_size); out.write((char*)u, uv_size); out.write((char*)v, uv_size);
    out.close();
    for (auto i : { &y, &u, &v }) delete[] * i;
}

在主程序中添加:

    auto data = DecodeData(); 
    png2yuv(out_path, ihdr.width, ihdr.height, data, plte);

即可完成解码工作。

实验结果

在这里插入图片描述
如图,可见转换时成功的。

完整代码

头文件header.h

#pragma once
#include <iostream>
#include <cstdio>
#include <fstream>
#include <vector>
#include <map>
#include <algorithm>
#include <set>
using namespace std;


struct fileHeader
{
    unsigned char head[8];
    void GetHead(ifstream& in) { in.read((char*)head, 8); }
};
struct iHDR
{
    unsigned int width, height;
    unsigned char bit_depth, color_type, compression_method, filter_method, interlace_method;
    void GetIHDR(unsigned char* buffer);
    void Print();
};
struct chunks
{
    unsigned int length;
    char type[4];
    std::string s_type = "";
    unsigned char* data;
    unsigned char CRC[4];
    chunks();
    void GetChunk(ifstream& in);
};
struct pLTE
{
    int r, g, b;
    static pLTE* GetPLTE(unsigned char* buffer, iHDR ihdr);
};
class dataReader
{
private:
    unsigned char* buffer;
    unsigned int bit_pos, byte_pos, pos_in_byte, length;
public:
    dataReader();
    void GetData(ifstream& in);
    void GetData(unsigned char* in_buffer, int length);
    unsigned int ReadNextBit();
    unsigned int ReadBits(int n);
    unsigned int ReadBitsR(int n);
    unsigned int ReadBytes(int n);
    void ReadBytes(unsigned char* buffer, int length);
    void GetPos();
};
struct IDATheader
{
    unsigned int CM, CINFO, FCHECK, FDICT, FLEVEL, DICTCHECK, CHECKSUM;
    unsigned int BFINAL, BTYPE;
    unsigned int LEN, NLEN;
    unsigned int NLIT, NDIST, NCLEN;
    void GetZlibHeader(dataReader& data_reader);
    void GetDeflateHeader(dataReader& data_reader);
    void Print();
};

class huffmanTree
{
private:
    int value;
    huffmanTree* left, * right;
public:
    huffmanTree();
    void Update(int index, unsigned int codes, unsigned int len);
    void Build(vector< unsigned int > codes, vector<unsigned int> length);
    int Decode();
    void Print(string s = "");
};

vector< unsigned int > GetCodes(vector<unsigned int> length);
vector< unsigned int > DecodeLength(int num);
void GetCodeLengthAphabet(int NCLEN);
void GetOherAlphabet(int NLIT, int NDIST);

vector< unsigned char > DecodeData();

void png2yuv(const string out_yuv_path, int width, int height, vector<unsigned char> data_buffer, pLTE* plte);

data_reader.cpp

#include "header.h"

void dataReader::GetPos()
{
    cout << "Byte pos: " << byte_pos << endl;
    cout << "Bit pos: " << bit_pos << endl;
    cout << "Pos in Byte: " << pos_in_byte << endl;
    cout << "total length: " << length << endl;
}
dataReader::dataReader()
{
    buffer = nullptr;
    bit_pos = byte_pos = pos_in_byte = length = 0;
}

void dataReader::GetData(ifstream& in)
{
    in.seekg(0, ios::end);
    length = in.tellg();
    in.seekg(0, ios::beg);
    buffer = new unsigned char[length];
    in.read((char*)buffer, length);
}
void dataReader::GetData( unsigned char* in_buffer, int length ) 
{ 
    buffer = in_buffer; 
    this->length = length;
}

unsigned int dataReader::ReadNextBit()
{
    if (byte_pos >= length)
    {
        cout << "Out of boundary." << endl;
        exit(1);
    }
    unsigned int ans = 0;
    ans = (buffer[byte_pos] & (1 << pos_in_byte)) >> pos_in_byte;
    bit_pos++; pos_in_byte++;
    if (pos_in_byte == 8)
    {
        pos_in_byte = 0;
        byte_pos++;
    }
    return ans;
}
unsigned int dataReader::ReadBits(int n)
{
    unsigned int ans = 0;
    for (int i = 0; i < n; ++i)
        ans = ans | (ReadNextBit() << i);
    return ans;
}
unsigned int dataReader::ReadBitsR(int n)
{
    unsigned int ans = 0;
    for (int i = 0; i < n; ++i)
        ans = (ans << 1) | ReadNextBit();
    return ans;
}
unsigned int dataReader::ReadBytes(int n)
{
    unsigned int ans = 0;
    for (int i = 0; i < n; ++i)
    {
        ans = (ans << 8) | ReadBits(8);
    }
    return ans;
}
void dataReader::ReadBytes(unsigned char* buffer, int length)
{
    for (int i = 0; i < length; ++i)
        buffer[i] = this->buffer[byte_pos++];
}

png_tools.cpp

#include "header.h"

void iHDR::Print()
{
    cout << "width: " << (int)width << " height: " << height << endl;
    cout << "bit depth: " << (int)bit_depth << endl;
    cout << "color type: " << (int)color_type << endl;
    cout << "compression method: " << (int)compression_method << endl;
    cout << "interlace method : " << (int)interlace_method << endl;
}
void iHDR::GetIHDR(unsigned char* buffer)
{
    for (int i = 0; i < 4; ++i) { width = (width << 8) + buffer[i]; }
    for (int i = 0; i < 4; ++i) { height = (height << 8) + buffer[i + 4]; }
    int pos = 8;
    for (auto i : { &bit_depth, &color_type, &compression_method, &filter_method, &interlace_method })
        *i = buffer[pos++];
}
chunks::chunks()
{
    length = 0;
    memset(type, 0, sizeof(type));
    data = nullptr;
    s_type = "";
    memset(CRC, 0, sizeof(CRC));
}
void chunks::GetChunk(ifstream& in)
{
    unsigned char* buffer = new unsigned char[4];
    in.read((char*)buffer, 4);
    for (int i = 0; i < 4; ++i) { length = (length << 8) + buffer[i]; }
    in.read(type, 4);
    if (length != 0) 
    {
        data = new unsigned char[length];
        in.read((char*)data, length);
    }
    in.read((char*)CRC, 4);
    for (auto i : type) s_type += (int)i;
    return;
}
pLTE* pLTE::GetPLTE(unsigned char* buffer, iHDR ihdr)
{
    int plte_size = 1 << ihdr.bit_depth;
    pLTE* plte = new pLTE[plte_size];
    int pos = 0;
    for (int i = 0; i < plte_size; ++i)
    {
        for (auto j : { &plte[i].r, &plte[i].g, &plte[i].b })
            *j = buffer[pos++];
    }
    return plte;
}

void png2yuv( const string out_yuv_path, int width, int height, vector<unsigned char> data_buffer, pLTE* plte)
{
    unsigned char* y, * u, * v;
    int y_size = width * height;
    int uv_size = y_size / 4;
    y = new unsigned char[y_size];
    u = new unsigned char[uv_size];
    v = new unsigned char[uv_size];
    int buffer_size = data_buffer.size();
    int uv_pos = 0, y_pos = 0;
    for (int i = 0; i < buffer_size; ++i)
    {
        if ((i + 1) % (width + 1) == 0) continue;
        int r, g, b; auto plte_tmp = plte[data_buffer[i]];
        r = plte_tmp.r, g = plte_tmp.g, b = plte_tmp.b;
        int h = i / (width + 1), w = i % (width + 1);
        y[y_pos++] = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
        if ((h & 1) || (w & 1)) continue;
        u[uv_pos] = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
        v[uv_pos++] = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
    }
    ofstream out(out_yuv_path, ios::binary);
    out.write((char*)y, y_size); out.write((char*)u, uv_size); out.write((char*)v, uv_size);
    out.close();
    for (auto i : { &y, &u, &v }) delete[] * i;
}

IDAT_tools.cpp

#include "header.h"
extern dataReader data_reader;
huffmanTree code_len_alphabet, len_alphabet, dis_alphabet;
void IDATheader::GetZlibHeader(dataReader &data_reader)
{
    CM = data_reader.ReadBits(4);
    CINFO = data_reader.ReadBits(4);
    FCHECK = data_reader.ReadBits(5);
    FDICT = data_reader.ReadBits(1);
    FLEVEL = data_reader.ReadBits(2);
    if (FDICT) DICTCHECK = data_reader.ReadBytes(4);

}
void IDATheader::GetDeflateHeader(dataReader& data_reader)
{
    BFINAL = data_reader.ReadNextBit();
    BTYPE = data_reader.ReadBits(2);
    switch (BTYPE)
    {
    case 0:
        break;
    case 1:
        break;
    case 2:
    {
        NLIT = data_reader.ReadBits(5) + 257;
        NDIST = data_reader.ReadBits(5) + 1;
        NCLEN = data_reader.ReadBits(4) + 4;
        break;
    }
    }
}
void IDATheader::Print()
{
    cout << "CM: " << CM << endl;
    cout << "CINFO: " << CINFO << endl;
    cout << "FDICT: " << FDICT << endl;
    cout << "BIFNAL: " << BFINAL << endl;
    cout << "BTYPE: " << BTYPE << endl;
    cout << "NLIt: " << NLIT << endl;
    cout << "NDIST: " << NDIST << endl;
    cout << "NCLEN: " << NCLEN << endl;
}


huffmanTree::huffmanTree() { value = -1; left = right = nullptr; }
void huffmanTree::Update(int index, unsigned int codes, unsigned int len)
{
    if (len == 0)
    {
        value = index;
        left = right = nullptr;
        return;
    }

    if (codes & (1u << (len - 1)))
    {
        if (nullptr == right)
            right = new huffmanTree;
        right->Update(index, codes, len - 1);
    }
    else
    {
        if (nullptr == left)
            left = new huffmanTree;
        left->Update(index, codes, len - 1);
    }
}
void huffmanTree::Build(vector< unsigned int > codes, vector<unsigned int> length)
{
    int len = codes.size();
    for (int i = 0; i < len; ++i) if (length[i]) Update(i, codes[i], length[i]);
}
int huffmanTree::Decode()
{
    if (value != -1) return value;
    auto sym = data_reader.ReadNextBit();
    if (sym) right->Decode();
    else left->Decode();
}
void huffmanTree::Print(string s)
{
    if (value != -1)
    {
        cout << value << "\t" << s << endl;
        return;
    }
    if (nullptr != right)
        right->Print(s + '1');
    if (nullptr != left)
        left->Print(s + '0');
}

vector< unsigned int > GetCodes(vector<unsigned int> length)
{
    int len = length.size();
    vector< unsigned int > frequent(len), next_code(len), codes(len);
    for (auto i : length) if (i) frequent[i] ++;
    for (int i = 1; i < len; ++i) next_code[i] = (next_code[i - 1] + frequent[i - 1]) << 1;
    for (int i = 0; i < len; ++i) if (length[i]) codes[i] = next_code[length[i]] ++;
    return codes;
}
vector< unsigned int > DecodeLength(int num)
{
    vector<unsigned int> length;
    while (length.size() != num)
    {
        auto sym = code_len_alphabet.Decode();
        unsigned int t = 0;
        if (sym <= 15) length.push_back(sym);
        else if (sym == 16)
        {
            t = data_reader.ReadBits(2) + 3;
            auto back = length.back();
            while (t--) length.push_back(back);
        }
        else if (sym == 17)
        {
            t = data_reader.ReadBits(3) + 3;
            while (t--) length.push_back(0);
        }
        else
        {
            t = data_reader.ReadBits(7) + 11;
            while (t--) length.push_back(0);
        }
    }
    return length;
}
void GetCodeLengthAphabet( int NCLEN )
{
    vector< int > index = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
    vector< unsigned int > length(19);
    for (int i = 0; i < NCLEN; ++i)
        length[index[i]] = data_reader.ReadBits(3);
    auto codes = GetCodes(length);
    code_len_alphabet.Build(codes, length);
}
void GetOherAlphabet( int NLIT, int NDIST )
{
    auto len_length = DecodeLength(NLIT);
    auto dis_length = DecodeLength(NDIST);
    auto len_codes = GetCodes(len_length);
    auto dis_codes = GetCodes(dis_length);
    len_alphabet.Build(len_codes, len_length);
    dis_alphabet.Build(dis_codes, dis_length);
}

unsigned int len_ex_bits[] = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 };
unsigned int len_base[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,0 };
unsigned int dis_ex_bits[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
unsigned int dis_base[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577 };
vector< unsigned char > DecodeData()
{
    vector< unsigned char > data;
    while (1)
    {
        auto sym = len_alphabet.Decode();
        if (sym == 256)
            return data;
        else
        {
            if (sym < 256) { data.push_back((unsigned char)sym); continue; }
            sym -= 257;
            auto len = len_base[sym] + data_reader.ReadBits(len_ex_bits[sym]);
            sym = dis_alphabet.Decode();
            auto dis = dis_base[sym] + data_reader.ReadBits(dis_ex_bits[sym]);
            for (int i = 0; i < len; ++i)
            {
                auto t = data[data.size() - dis];
                data.push_back(t);
            }
        }
    }
}

main.cpp

#include "header.h"

const string in_path = "1.png";
const string out_path = "out.yuv";
map< string, vector<int> > mp;
vector<chunks> chunks_vector;
dataReader data_reader;
extern huffmanTree code_len_alphabet, len_alphabet, dis_alphabet;

int main()
{
    ifstream in(in_path, ios::binary);
    if (!in.is_open())
    {
        cout << in_path << " open failed." << endl;
        exit(1);
    }
    fileHeader file_header;
    file_header.GetHead(in);
    int pos = 0;
    while (true)
    {
        chunks tmp;
        tmp.GetChunk(in);
        chunks_vector.push_back(tmp);
        mp[tmp.s_type].push_back(pos++);
        if (tmp.s_type == "IEND") break;
    }

    for (auto& i : mp)
    {
        cout << i.first << ": ";
        for (auto& j : i.second)
            cout << j << " ";
        cout << endl;
    }
    int position = mp["IHDR"][0];
    iHDR ihdr; ihdr.GetIHDR(chunks_vector[position].data);
    cout << endl;  ihdr.Print();

    position = mp["PLTE"][0];
    pLTE* plte = pLTE::GetPLTE(chunks_vector[position].data, ihdr);

    IDATheader idat_header;
    position = mp["IDAT"][0];
    data_reader.GetData(chunks_vector[position].data, chunks_vector[position].length);
    idat_header.GetZlibHeader(data_reader); idat_header.GetDeflateHeader(data_reader);
    idat_header.Print();

    GetCodeLengthAphabet(idat_header.NCLEN);
    GetOherAlphabet(idat_header.NLIT, idat_header.NDIST);
    dis_alphabet.Print();
    auto data = DecodeData(); 
    png2yuv(out_path, ihdr.width, ihdr.height, data, plte);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值