fsk-imgoptimize
Version:
171 lines (161 loc) • 4.96 kB
JavaScript
var Writable = require('stream').Writable;
var Readable = require('stream').Readable;
var ImgOptimize = require('./imgOptimize.js');
var util = require('util');
var imgtypes = require('./constants.js');
util.inherits(GIFStream, ImgOptimize);
function GIFStream(options) {
if (!(this instanceof GIFStream)) {
return new GIFStream(options);
}
ImgOptimize.call(this);
}
GIFStream.prototype.handleSignature = function() {
this.addHandle(6, this.parseSignature.bind(this));
};
GIFStream.prototype.parseSignature = function(data) {
var sign = data.toString();
if (sign === 'GIF89a' || sign === 'GIF79a') {
this.push(data);
this.addHandle(7, this.parseLSD.bind(this));
} else {
throw new Error('unvalid file type');
}
};
//LSD===Logical Screen Description 逻辑屏幕描述块
GIFStream.prototype.parseLSD = function(data) {
this.push(data);
var packedField = data.readUIntBE(4, 1);
var g = packedField.toString(2).charAt(1);
//g === 1时表示有全局颜色表 读取后三位计算全局颜色表的大小
if (g === '1') {
var globalColorTableSize = (2 << (packedField & 7 )) * 3;
this.addHandle(globalColorTableSize, this.parseGCT.bind(this));
} else {
this.addHandle(1, this.parseOther.bind(this));
}
}
//GCT === global color table 全局颜色表
GIFStream.prototype.parseGCT = function(data) {
this.push(data);
this.addHandle(1, this.parseOther.bind(this));
};
GIFStream.prototype.parseOther = function(data) {
var sign = data.readUIntBE(0, data.length);
switch (sign) {
case 33 :
this.addHandle(1, this.parseExtChunk.bind(this));
break;
case 44:
this.push(data);
this.addHandle(9, this.parseImg.bind(this));
break;
case 59:
this.push(data);
this.end();
default:
break;
}
};
GIFStream.prototype.parseExtChunk = function(data) {
var sign = data.readUIntBE(0, data.length);
switch (sign) {
case 1: //扩展文本标识
this.push(new Buffer([0x21]));
this.push(data);
this.addHandle(13, this.parseTextExt.bind(this));
break;
case 255: //应用扩展
this.push(new Buffer([0x21]));
this.push(data);
this.addHandle(12, this.parseAppExt.bind(this));
break;
case 249: //图形控制扩展
this.push(new Buffer([0x21]));
this.push(data);
this.addHandle(6, this.parseGraphicControl.bind(this));
break;
case 254: //注释
//注释标识之后紧跟着就是 注释子数据块
//所以可以直接读取自数据块 并且忽略掉注释的数据
this.handleSubBlock(true);
break;
default:
break;
}
};
GIFStream.prototype.parseTextExt = function(data) {
this.push(data);
this.handleSubBlock(false);
}
GIFStream.prototype.parseAppExt = function(data){
this.push(data);
this.handleSubBlock();
}
GIFStream.prototype.handleSubBlock = function(ignore) {
var self = this;
this._queue.push({
length: 1,
callback: function(data) {
if (!ignore) {
self.push(data);
} else {
// console.log(data.readUIntBE(0, 1));
}
var length = data.readUIntBE(0, 1);
//如果length为0 则认为数据子块结束 继续读取下一个数据块
//否则继续调用handleSubBlock读取下一段数据子块
if(length) {
self._queue.push({
length: length,
callback: function(content) {
if (!ignore) {
self.push(content);
}
self.handleSubBlock(ignore);
}
});
} else {
self.addHandle(1, this.parseOther.bind(this));
}
}
});
};
GIFStream.prototype.parseGraphicControl = function(data) {
this.push(data);
this.addHandle(1, this.parseOther.bind(this));
}
GIFStream.prototype.parseImg = function(data) {
this.push(data);
var packedField = data.readUIntBE(8, 1);
var l = packedField.toString(2).charAt(1);
//若标识为1 则说明有局部颜色列表 解析之
if ( l === '1') {
var LocalColorTableSize = (2 << (packed & 7)) * 3;
this.addHandle(LocalColorTableSize, this.parseLCT.bind(this));
} else {
//等于说明没有局部颜色列表 直接读取LZW数据
this.addHandle(1, this.parseLZW.bind(this));
}
}
//LCT == local color table 局部颜色列表
GIFStream.prototype.parseLCT = function(data) {
this.push(data);
//解析完LCT继续读LZW
this.addHandle(1, this.parseLZW.bind(this));
};
GIFStream.prototype.parseLZW = function(data) {
this.push(data);
this.handleSubBlock();
};
module.exports = GIFStream;
// var fs = require('fs');
// fs.createReadStream('a.gif').pipe(new GIFStream()).pipe(fs.createWriteStream('9.gif'));
// // var buf = new Buffer([ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 ])
// fs.readFile('9.gif', function(err, data) {
// var pos = 0;
// while (pos < data.length){
// console.log(data.slice(pos, pos + 16));
// pos += 16;
// }
// });