文章目录
前置知识
-
PNG格式
-
zlib文件格式
-
deflate文件格式
-
LZ77压缩算法
-
霍夫曼编码
-
字典树
综述
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文件所拥有的数据块以及基本信息。
具体如下:
名称 | 取值 |
---|---|
Width | 80 |
Height | 80 |
Bit depth | 8位 |
ColorType | 3,带有调色板 |
Compression method | 0 ,deflate压缩算法 |
Filter method | 0,即滤波方法 0 |
Interlace method | 0,非隔行扫描 |
获取调色板信息
添加定义:
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);
}