1. PNG文件格式
PNG图像格式文件(或者称为数据流)由一个8字节的PNG文件署名(PNG file signature)域和按照特定结构组织的3个以上的数据块(chunk)组成。
PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。
4个标准数据块:
(1)文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。文件头数据块由13字节组成。
域的名称 | 字节数 | 说明 |
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 |
Compression method | 1 byte | 压缩方法(LZ77派生算法) |
Filter method | 1 byte | 滤波器方法 |
Interlace method | 1 byte | 隔行扫描方法: 0:非隔行扫描 1: Adam7(由Adam M. Costello开发的7 |
(2) 调色板数据块PLTE(palette chunk):它包含有与索引彩色图像((indexed-color image))相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。真彩色的PNG数据流也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。
表6-09 调色板数据块结构
域的名称 | 字节数 | 说明 |
Red | 1 byte | 0 = 黑,255 = 红 |
Green | "> 0 = 黑,255 = 绿 | |
Blue | 1 byte | 0 = 黑,255 = 蓝 |
调色板实际是一个彩色索引查找表,它的表项数目可以是1~256中的一个数,每个表项有3字节,因此调色板数据块所包含的最大字节数为768。
(3) 图像数据块IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。
(4) 图像结束数据IEND(image trailer chunk):它用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部。
除了表示数据块开始的IHDR必须放在最前面, 表示PNG文件结束的IEND数据块放在最后面之外,其他数据块的存放顺序没有限制。
PNG的IDAT使用从LZ77派生的无损数据压缩算法,大概意思就是将数据作为一个数组,进行字符串匹配 ,从0开始向后移动,在每个位置上都寻找它前面的区域中和当前位置开始的串至少三个相匹配的串,并从这些串中找到最长的匹配的串。然后将会被一个<匹配长度,到匹配串开头的距离>对替换。
所以解析IDAT就是关键了。
这里LIBPNG用的是zlib程序库作为压缩引擎。zlib可以从它的官方网站下载。
zlib 的使用在sourceproject可以下载个例子看看:) 那里有显示的例子
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1838572
2. libpng
libpng是一个跨平台的png解码库,方便易用。我这里不说它怎么移植,基本上如果是支持fopen之类函数的平台都可以支持。其实他的移植非常简单,zlib可能麻烦一点,但相信一般人都能搞定。主要是文件读写和错误处理的试配。
这里只提供一个使用libpng得到文件RGB(A)数据的例子,没加任何的错误处理:
void readpng__001(char* name)
{
// 前边几句是扯淡,初始化各种结构
FILE* file = fopen(name, "rb");
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
png_infop info_ptr = png_create_info_struct(png_ptr);
setjmp(png_jmpbuf(png_ptr));
// 这句很重要
png_init_io(png_ptr, file);
// 读文件了
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
// 得到文件的宽高色深
int m_width = png_get_image_width(PngPtr, InfoPtr);
int m_height = png_get_image_height(PngPtr, InfoPtr);
int color_type = png_get_color_type(PngPtr, InfoPtr);
// 申请个内存玩玩,这里用的是c++语法,甭想再c上编过
int size = m_height * m_width * 4;
unsigned char* bgra = new unsigned char[size];
int pos = 0;
// row_pointers里边就是传说中的rgba数据了
png_bytep* row_pointers = png_get_rows(PngPtr, InfoPtr);
// 拷贝!!注意,如果你读取的png没有A通道,就要3位3位的读。还有就是注意字节对其的问题,最简单的就是别用不能被4整除的宽度就行了。读过你实在想用,就要在这里加上相关的对齐处理。
for(int i = 0; i < m_height; i++)
{
for(int j = 0; j < (4 * m_width); j += 4)
{
bgra[pos++] = row_pointers[i][j + 2]; // blue
bgra[pos++] = row_pointers[i][j + 1]; // green
bgra[pos++] = row_pointers[i][j]; // red
bgra[pos++] = row_pointers[i][j + 3]; // alpha
}
}
// 好了,你可以用这个数据作任何的事情了。。。把它显示出来或者打印出来都行。
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
fclose(file);
return;
}
http://hi.baidu.com/ljp2010/blog/item/c2bfa2eca501442f62d09f1f.html