写上篇博客前对Node的Stream的官方文档扫了一遍,之后还想继续使用Stream写些demo,就选择了写个小程序使用Node读取解析PNG图片(想的是如果可以方便地解析、生成PNG图片,那就可以很方便地生成验证码图片发给前端),结果就把自己坑了。。。PNG还是比较复杂的(以前 数字图像处理 的课中接触的主要就是bmp、tiff,要么就直接用OpenCV、GDAL直接读取各种格式的图片,还没有仔细看过PNG的具体格式),由于时间关系我只解析了“非隔行扫描、非索引颜色、FilterMethod为0”的PNG图片-_-||
使用Node的fs.createReadStream()可以创建一个文件读取流,在这里我使用的是Paused模式(Paused模式和Flowing模式可以看上一篇的介绍),通过stream.read()方法可以比较精细地读取readable流中的数据:
this.path = path;
this.stream = fs.createReadStream(this.path);
//使用paused模式
this.stream.pause();
this.stream.once('readable', ()=>{
//使用stream.read()消耗readable数据流
// ......
});
关于PNG的格式,有很多博客都写得比较详细的,但是几乎所有的文章都略过了IDAT数据块中的data解压方法、滤波方法,当时还是在PNG官方文档中弄明白的。这里先给出文档链接:W3C - Portable Network Graphics (PNG) Specification (Second Edition)
PNG 全称是 Portable Network Graphics,即“便携式网络图形”,是一种无损压缩的位图图形格式。其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。
PNG文件结构
一个完整的PNG数据都是以一个PNG signature开头和一系列数据块(chunk)组成,其中第一个chunk为IHDR,最后一个chunk为IEDN。
PNG结构: |
---|
signature |
chunk (IHDR) |
… |
chunk |
… |
chunk (IEDN) |
官方文档的描述是:This signature indicates that the remainder of the datastream contains a single PNG image, consisting of a series of chunks beginning with an IHDR chunk and ending with an IEND chunk.
PNG Signature
PNG signature 位于PNG文件的最开头,占8个字节,每个字节用十进制可以表示为 [137, 80, 78, 71, 13, 10, 26, 10] ,通过下面的函数可以验证signature的正确性:
checkSignature(){
//PNG的Signature长度为8字节, 1Byte = 8bit
let buffer = this.stream.read(8);
let signature = [137, 80, 78, 71, 13, 10, 26, 10];
for(let i=0; i<signature.length; i++){
let v = buffer.readUInt8(i);
if(v !== signature[i])
throw new Error('It is not PNG file !');
}
return true;
}
PNG Chunk
PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了4个标准数据块(IHDR, PLTE, IDAT, IEND),每个PNG文件都必须包含它们(没有PLTE的话就默认为RGB色),PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。
下表就是PNG中数据块的类别,其中,关键数据块是前4个。
Chunk name | Multiple allowed | Ordering constraints | |
---|---|---|---|
IHDR | No | Shall be first | 文件头数据块 |
PLTE | No | Before first IDAT | 调色板数据块 |
IDAT | Yes | Multiple IDAT chunks shall be consecutive | 图像数据块 |
IEND | No | Shall be last | 图像结束数据 |
cHRM | No | Before PLTE and IDAT | 基色和白色点数据块 |
gAMA | No | Before PLTE and IDAT | 图像γ数据块 |
iCCP | No | Before PLTE and IDAT. If the iCCP chunk is present, the sRGB chunk should not be present. | ICCP |
sBIT | No | Before PLTE and IDAT | 样本有效位数据块 |
sRGB | No | Before PLTE and IDAT. If the sRGB chunk is present, the iCCP chunk should not be present. | 标准RPG颜色 |
bKGD | No | After PLTE; before IDAT | 背景颜色数据块 |
hIST | No | After PLTE; before IDAT | 图像直方图数据块 |
tRNS | No | After PLTE; before IDAT | 图像透明数据块 |
pHYs | No | Bef |