mirror of
https://github.com/danbulant/pngjs
synced 2026-07-07 12:10:59 +00:00
Implemented symetric PNG.sync.write to complement PNG.sync.read
This commit is contained in:
parent
42fd49f0b6
commit
ac815b87ee
3 changed files with 133 additions and 70 deletions
|
|
@ -3,16 +3,12 @@
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var Stream = require('stream');
|
var Stream = require('stream');
|
||||||
var zlib = require('zlib');
|
var zlib = require('zlib');
|
||||||
var filter = require('./filter-pack');
|
|
||||||
var CrcStream = require('./crc');
|
|
||||||
var constants = require('./constants');
|
var constants = require('./constants');
|
||||||
var bitPacker = require('./bitpacker');
|
var Packer = require('./packer');
|
||||||
|
|
||||||
var PackerAsync = module.exports = function(options) {
|
var PackerAsync = module.exports = function(options) {
|
||||||
Stream.call(this);
|
Stream.call(this);
|
||||||
|
|
||||||
this._options = options;
|
|
||||||
|
|
||||||
options.deflateChunkSize = options.deflateChunkSize || 32 * 1024;
|
options.deflateChunkSize = options.deflateChunkSize || 32 * 1024;
|
||||||
options.deflateLevel = options.deflateLevel != null ? options.deflateLevel : 9;
|
options.deflateLevel = options.deflateLevel != null ? options.deflateLevel : 9;
|
||||||
options.deflateStrategy = options.deflateStrategy != null ? options.deflateStrategy : 3;
|
options.deflateStrategy = options.deflateStrategy != null ? options.deflateStrategy : 3;
|
||||||
|
|
@ -28,6 +24,13 @@ var PackerAsync = module.exports = function(options) {
|
||||||
throw new Error('option bit depth:' + options.bitDepth + ' is not supported at present');
|
throw new Error('option bit depth:' + options.bitDepth + ' is not supported at present');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._packer = new Packer(options);
|
||||||
|
this._deflate = options.deflateFactory({
|
||||||
|
chunkSize: options.deflateChunkSize,
|
||||||
|
level: options.deflateLevel,
|
||||||
|
strategy: options.deflateStrategy
|
||||||
|
});
|
||||||
|
|
||||||
this.readable = true;
|
this.readable = true;
|
||||||
};
|
};
|
||||||
util.inherits(PackerAsync, Stream);
|
util.inherits(PackerAsync, Stream);
|
||||||
|
|
@ -36,79 +39,25 @@ util.inherits(PackerAsync, Stream);
|
||||||
PackerAsync.prototype.pack = function(data, width, height, gamma) {
|
PackerAsync.prototype.pack = function(data, width, height, gamma) {
|
||||||
// Signature
|
// Signature
|
||||||
this.emit('data', new Buffer(constants.PNG_SIGNATURE));
|
this.emit('data', new Buffer(constants.PNG_SIGNATURE));
|
||||||
this.emit('data', this._packIHDR(width, height, this._options.bitDepth, this._options.colorType));
|
this.emit('data', this._packer.packIHDR(width, height));
|
||||||
|
|
||||||
if (gamma) {
|
if (gamma) {
|
||||||
this.emit('data', this._packGAMA(gamma));
|
this.emit('data', this._packer.packGAMA(gamma));
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert to correct format for filtering (e.g. right bpp and bit depth)
|
var filteredData = this._packer.filterData(data, width, height);
|
||||||
var packedData = bitPacker(data, width, height, this._options);
|
|
||||||
|
|
||||||
// filter pixel data
|
|
||||||
var bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType];
|
|
||||||
var filteredData = filter(packedData, width, height, this._options, bpp);
|
|
||||||
|
|
||||||
// compress it
|
// compress it
|
||||||
var deflate = this._options.deflateFactory({
|
this._deflate.on('error', this.emit.bind(this, 'error'));
|
||||||
chunkSize: this._options.deflateChunkSize,
|
|
||||||
level: this._options.deflateLevel,
|
|
||||||
strategy: this._options.deflateStrategy
|
|
||||||
});
|
|
||||||
deflate.on('error', this.emit.bind(this, 'error'));
|
|
||||||
|
|
||||||
deflate.on('data', function(compressedData) {
|
this._deflate.on('data', function(compressedData) {
|
||||||
this.emit('data', this._packIDAT(compressedData));
|
this.emit('data', this._packer.packIDAT(compressedData));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
deflate.on('end', function() {
|
this._deflate.on('end', function() {
|
||||||
this.emit('data', this._packIEND());
|
this.emit('data', this._packer.packIEND());
|
||||||
this.emit('end');
|
this.emit('end');
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
deflate.end(filteredData);
|
this._deflate.end(filteredData);
|
||||||
};
|
|
||||||
|
|
||||||
PackerAsync.prototype._packChunk = function(type, data) {
|
|
||||||
|
|
||||||
var len = (data ? data.length : 0);
|
|
||||||
var buf = new Buffer(len + 12);
|
|
||||||
|
|
||||||
buf.writeUInt32BE(len, 0);
|
|
||||||
buf.writeUInt32BE(type, 4);
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
data.copy(buf, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.writeInt32BE(CrcStream.crc32(buf.slice(4, buf.length - 4)), buf.length - 4);
|
|
||||||
return buf;
|
|
||||||
};
|
|
||||||
|
|
||||||
PackerAsync.prototype._packGAMA = function(gamma) {
|
|
||||||
var buf = new Buffer(4);
|
|
||||||
buf.writeUInt32BE(Math.floor(gamma * constants.GAMMA_DIVISION), 0);
|
|
||||||
return this._packChunk(constants.TYPE_gAMA, buf);
|
|
||||||
};
|
|
||||||
|
|
||||||
PackerAsync.prototype._packIHDR = function(width, height, bitDepth, colorType) {
|
|
||||||
|
|
||||||
var buf = new Buffer(13);
|
|
||||||
buf.writeUInt32BE(width, 0);
|
|
||||||
buf.writeUInt32BE(height, 4);
|
|
||||||
buf[8] = bitDepth; // Bit depth
|
|
||||||
buf[9] = colorType; // colorType
|
|
||||||
buf[10] = 0; // compression
|
|
||||||
buf[11] = 0; // filter
|
|
||||||
buf[12] = 0; // interlace
|
|
||||||
|
|
||||||
return this._packChunk(constants.TYPE_IHDR, buf);
|
|
||||||
};
|
|
||||||
|
|
||||||
PackerAsync.prototype._packIDAT = function(data) {
|
|
||||||
return this._packChunk(constants.TYPE_IDAT, data);
|
|
||||||
};
|
|
||||||
|
|
||||||
PackerAsync.prototype._packIEND = function() {
|
|
||||||
return this._packChunk(constants.TYPE_IEND, null);
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,53 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = function(buffer, options) {
|
var zlib = require('zlib');
|
||||||
return null;
|
var constants = require('./constants');
|
||||||
|
var Packer = require('./packer');
|
||||||
|
|
||||||
|
module.exports = function(metaData, options) {
|
||||||
|
options = options || {};
|
||||||
|
options.deflateChunkSize = options.deflateChunkSize || 32 * 1024;
|
||||||
|
options.deflateLevel = options.deflateLevel != null ? options.deflateLevel : 9;
|
||||||
|
options.deflateStrategy = options.deflateStrategy != null ? options.deflateStrategy : 3;
|
||||||
|
options.inputHasAlpha = options.inputHasAlpha != null ? options.inputHasAlpha : true;
|
||||||
|
options.deflateFactory = options.deflateFactory || zlib.createDeflate;
|
||||||
|
options.bitDepth = options.bitDepth || 8;
|
||||||
|
options.colorType = (typeof options.colorType === 'number') ? options.colorType : constants.COLORTYPE_COLOR_ALPHA;
|
||||||
|
|
||||||
|
if (options.colorType !== constants.COLORTYPE_COLOR && options.colorType !== constants.COLORTYPE_COLOR_ALPHA) {
|
||||||
|
throw new Error('option color type:' + options.colorType + ' is not supported at present');
|
||||||
|
}
|
||||||
|
if (options.bitDepth !== 8) {
|
||||||
|
throw new Error('option bit depth:' + options.bitDepth + ' is not supported at present');
|
||||||
|
}
|
||||||
|
|
||||||
|
var packer = new Packer(options);
|
||||||
|
|
||||||
|
var chunks = [];
|
||||||
|
|
||||||
|
// Signature
|
||||||
|
chunks.push(new Buffer(constants.PNG_SIGNATURE));
|
||||||
|
|
||||||
|
// Header
|
||||||
|
chunks.push(packer.packIHDR(metaData.width, metaData.height));
|
||||||
|
|
||||||
|
if (metaData.gamma) {
|
||||||
|
chunks.push(packer.packGAMA(metaData.gamma));
|
||||||
|
}
|
||||||
|
|
||||||
|
var filteredData = packer.filterData(metaData.data, metaData.width, metaData.height);
|
||||||
|
|
||||||
|
// compress it
|
||||||
|
var compressedData = zlib.deflateSync(filteredData);
|
||||||
|
filteredData = null;
|
||||||
|
|
||||||
|
if (!compressedData || !compressedData.length) {
|
||||||
|
throw new Error('bad png - invalid compressed data response');
|
||||||
|
}
|
||||||
|
chunks.push(packer.packIDAT(compressedData));
|
||||||
|
|
||||||
|
// End
|
||||||
|
chunks.push(packer.packIEND());
|
||||||
|
|
||||||
|
return Buffer.concat(chunks);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
66
lib/packer.js
Normal file
66
lib/packer.js
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* Created by gusc on 15.3.12.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var constants = require('./constants');
|
||||||
|
var CrcStream = require('./crc');
|
||||||
|
var bitPacker = require('./bitpacker');
|
||||||
|
var filter = require('./filter-pack');
|
||||||
|
|
||||||
|
var Packer = module.exports = function(options) {
|
||||||
|
this._options = options;
|
||||||
|
};
|
||||||
|
|
||||||
|
Packer.prototype.filterData = function(data, width, height){
|
||||||
|
// convert to correct format for filtering (e.g. right bpp and bit depth)
|
||||||
|
var packedData = bitPacker(data, width, height, this._options);
|
||||||
|
|
||||||
|
// filter pixel data
|
||||||
|
var bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType];
|
||||||
|
var filteredData = filter(packedData, width, height, this._options, bpp);
|
||||||
|
return filteredData;
|
||||||
|
};
|
||||||
|
|
||||||
|
Packer.prototype._packChunk = function(type, data) {
|
||||||
|
|
||||||
|
var len = (data ? data.length : 0);
|
||||||
|
var buf = new Buffer(len + 12);
|
||||||
|
|
||||||
|
buf.writeUInt32BE(len, 0);
|
||||||
|
buf.writeUInt32BE(type, 4);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
data.copy(buf, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.writeInt32BE(CrcStream.crc32(buf.slice(4, buf.length - 4)), buf.length - 4);
|
||||||
|
return buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
Packer.prototype.packGAMA = function(gamma) {
|
||||||
|
var buf = new Buffer(4);
|
||||||
|
buf.writeUInt32BE(Math.floor(gamma * constants.GAMMA_DIVISION), 0);
|
||||||
|
return this._packChunk(constants.TYPE_gAMA, buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
Packer.prototype.packIHDR = function(width, height) {
|
||||||
|
|
||||||
|
var buf = new Buffer(13);
|
||||||
|
buf.writeUInt32BE(width, 0);
|
||||||
|
buf.writeUInt32BE(height, 4);
|
||||||
|
buf[8] = this._options.bitDepth; // Bit depth
|
||||||
|
buf[9] = this._options.colorType; // colorType
|
||||||
|
buf[10] = 0; // compression
|
||||||
|
buf[11] = 0; // filter
|
||||||
|
buf[12] = 0; // interlace
|
||||||
|
|
||||||
|
return this._packChunk(constants.TYPE_IHDR, buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
Packer.prototype.packIDAT = function(data) {
|
||||||
|
return this._packChunk(constants.TYPE_IDAT, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
Packer.prototype.packIEND = function() {
|
||||||
|
return this._packChunk(constants.TYPE_IEND, null);
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue