1 PNG文件格式
参考 PNG File Structure
PNG文件是由一个signature
和一系列chunk
组成的
PNG: [ signature(8) | chunkx n ]
1.1 PNG File Signature
signature
是由固定的8字节组成的
signature: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]
137 P N G \r \n 26 \n
1.2 PNG File Chunk Layout
所有的chunk包含Length
, Chunk Type
, Chunk Data
, CRC
四个字段, 按照以下结构排列
┌─────────────────────────────────────┐
| ↓
chunk: [ Length(4) | Chunk Type(4) | Chunk Data(length) | CRC(4) ]
↑ ↑ |
└───────────────┴────────────────┘
1.2.1 Length
Length
字段是一个四字节大小的无符号整数, 用来表示Chunk Data
的长度(以字节为单位)
1.2.2 Chunk Type
Chunk Type
是一个四字节大小字段, 可以看作是字符数组, 内容为'a'-'z'
和'A'-'Z'
的集合
每个字段的大小写均代表不同的含义
关键数据标记与辅助数据标记(第一个字节)
大写: 表示是关键数据块, 对解码器来讲, 必须要解析的块, 出问题的话, 必须向用户报告
小写: 表示是辅助数据块, 对解码器来讲, 可以忽略
公共数据标记与私有数据标记(第二个字节)
大写: 表示是公共数据块, PNG规范中定义了一些列公共区块, 参考Additional chunk types
小写: 表示是私有数据块, 解码器不会访问这些区块
保留数据标记(第三个字节)
大写: 保留标记, 目前版本的PNG都是大写
小写: 留作协议扩展
安全复制标记与非安全赋值标记(第四个字节)
大写: 复制不安全, 解码器不关心, 编辑器可能会对这个块修改
小写: 复制安全, 解码器不关心, 编辑器不论怎么修改都会将这个块复制到新图像中
1.2.3 Chunk Data
数据段, 由Chunk Type
决定数据格式, 可以为零
1.2.4 CRC
参考 CRC32学习
2 常见的Chunk类型
IHDR结构
第一个Chunk
域名 | 字节数(Byte) | 说明 |
---|---|---|
Width | 4 | 图像宽度,以像素为单位 |
Height | 4 | 图像高度,以像素为单位 |
Bit Depth | 1 | 图像深度: 索引彩色图像: 1,2,4或8 灰度图像: 1,2,4,8或16 真彩色图像: 8或16 |
Color Type | 1 | 颜色类型: 0: 灰度图像, 1,2,4,8或16 2:真彩色图像,8或16 3索引彩色图像,1,2,4或8 4: 带α通道数据的灰度图像,8或16 6: 带α通道数据的真彩色图像,8或16 |
Compression method | 1 | 压缩方法(LZ77派生算法) |
Filter Method | 1 | 滤波器方法 |
Interlace method | 1 | 隔行扫描方法: 0: 非隔行扫描 1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法) |
IDAT结构
IDAT数据块存储的格式取决于PNG的格式以及是否压缩。
PNG文件格式分为PNG-24和PNG-8,其最大的区别是PNG-24是用24位来保存一个像素值,是真彩色,而PNG-8是用8位索引值来在调色盘 中索引一个颜色,因为一个索引值的最大上限为2的8次方既128,故调色盘中颜色数最多为128种,
一旦使用了压缩我们必须使用zlib解压才能看见它的具体颜色。
IEND结构
最后一个Chunk
值得注意的是,IEND区块虽然也按照数据块的结构,但Chunk Data是没有的,所以是固定的12个字节: 0x00000000
, 0x49454e44
, 0xae426082
IEND数据块的长度总是0(00 00 00 00
,除非人为加入信息),数据标识总是IEND(49 45 4E 44
),因此,CRC码也总是AE 42 60 82
3 测试读取PNG图片
打印出每个Chunk
的Length
, Chunk Type
, CRC
. 由于Chunk Data
大小不固定因此不打印
/**
* file png.c
* 方便贴代码, 在一个文件里面实现, 这不是一个好习惯, 嗯
*
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
} PNG_CHUNK_TYPE;
typedef struct
{
} PNG_CHUNK;
typedef struct
{
} PNG;
int png_chunk_type_check(const PNG_CHUNK_TYPE *self);
int png_chunk_check(const PNG_CHUNK *self);
int png_check(const PNG_CHUNK *self);
int png_chunk_type_display(const PNG_CHUNK_TYPE *self);
int png_chunk_display(const PNG_CHUNK *self);
int png_display(const PNG_CHUNK *self);
PNG_CHUNK *png_get_first_chunk(const PNG *self);
PNG_CHUNK *png_get_next_chunk(const PNG_CHUNK *self);
int main(int argc, char const *argv[])
{
FILE *fp = NULL;
uint8_t buffer[10 * 1024] = {}; // 10MB大小缓存
PNG *png = NULL;
fp = fopen("test.png", "r");
fread(buffer, sizeof(uint8_t), sizeof(buffer), fp);
fclose(fp);
png = (PNG *)buffer;
png_check(png);
png_display(png);
return 0;
}
// TODO