mirror of
https://github.com/danbulant/pngjs
synced 2026-05-26 21:31:56 +00:00
Merge remote-tracking branch 'origin/sync'
This commit is contained in:
commit
4def219e1e
2 changed files with 369 additions and 9 deletions
361
lib/parser-base.js
Normal file
361
lib/parser-base.js
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
// Copyright (c) 2012 Kuba Niegowski
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
var util = require('util'),
|
||||
zlib = require('zlib'),
|
||||
CrcStream = require('./crc'),
|
||||
ChunkStream = require('./chunkstream'),
|
||||
constants = require('./constants'),
|
||||
Filter = require('./filter');
|
||||
|
||||
|
||||
var Parser = module.exports = function(options, dependencies) {
|
||||
|
||||
this._options = options;
|
||||
options.checkCRC = options.checkCRC !== false;
|
||||
|
||||
this._hasIHDR = false;
|
||||
this._hasIEND = false;
|
||||
|
||||
this._inflate = null;
|
||||
this._filter = null;
|
||||
this._crc = null;
|
||||
|
||||
// input flags/metadata
|
||||
this._palette = [];
|
||||
this._colorType = 0;
|
||||
|
||||
this._chunks = {};
|
||||
this._chunks[constants.TYPE_IHDR] = this._handleIHDR.bind(this);
|
||||
this._chunks[constants.TYPE_IEND] = this._handleIEND.bind(this);
|
||||
this._chunks[constants.TYPE_IDAT] = this._handleIDAT.bind(this);
|
||||
this._chunks[constants.TYPE_PLTE] = this._handlePLTE.bind(this);
|
||||
this._chunks[constants.TYPE_tRNS] = this._handleTRNS.bind(this);
|
||||
this._chunks[constants.TYPE_gAMA] = this._handleGAMA.bind(this);
|
||||
|
||||
this.writable = true;
|
||||
|
||||
//this.on('error', this._handleError.bind(this));
|
||||
//this._handleSignature();
|
||||
this.read = dependencies.read;
|
||||
this.error = dependencies.error;
|
||||
};
|
||||
util.inherits(Parser, ChunkStream);
|
||||
|
||||
|
||||
Parser.prototype._handleError = function() {
|
||||
|
||||
this.writable = false;
|
||||
|
||||
this.destroy();
|
||||
|
||||
if (this._inflate)
|
||||
this._inflate.destroy();
|
||||
};
|
||||
|
||||
Parser.prototype.start = function() {
|
||||
this.read(constants.PNG_SIGNATURE.length,
|
||||
this._parseSignature.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
Parser.prototype._parseSignature = function(data) {
|
||||
|
||||
var signature = constants.PNG_SIGNATURE;
|
||||
|
||||
for (var i = 0; i < signature.length; i++) {
|
||||
if (data[i] != signature[i]) {
|
||||
this.error(new Error('Invalid file signature'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.read(8, this._parseChunkBegin.bind(this));
|
||||
};
|
||||
|
||||
Parser.prototype._parseChunkBegin = function(data) {
|
||||
|
||||
// chunk content length
|
||||
var length = data.readUInt32BE(0);
|
||||
|
||||
// chunk type
|
||||
var type = data.readUInt32BE(4),
|
||||
name = '';
|
||||
for (var i = 4; i < 8; i++)
|
||||
name += String.fromCharCode(data[i]);
|
||||
|
||||
// console.log('chunk ', name, length);
|
||||
|
||||
// chunk flags
|
||||
var ancillary = !!(data[4] & 0x20), // or critical
|
||||
priv = !!(data[5] & 0x20), // or public
|
||||
safeToCopy = !!(data[7] & 0x20); // or unsafe
|
||||
|
||||
if (!this._hasIHDR && type != constants.TYPE_IHDR) {
|
||||
this.error(new Error('Expected IHDR on beggining'));
|
||||
return;
|
||||
}
|
||||
|
||||
// this._crc = new CrcStream();
|
||||
// this._crc.write(new Buffer(name));
|
||||
|
||||
if (this._chunks[type]) {
|
||||
return this._chunks[type](length);
|
||||
|
||||
} else if (!ancillary) {
|
||||
this.error(new Error('Unsupported critical chunk type ' + name));
|
||||
return;
|
||||
} else {
|
||||
this.read(length + 4, this._skipChunk.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
Parser.prototype._skipChunk = function(data) {
|
||||
this.read(8, this._parseChunkBegin.bind(this));
|
||||
};
|
||||
|
||||
Parser.prototype._handleChunkEnd = function() {
|
||||
this.read(4, this._parseChunkEnd.bind(this));
|
||||
};
|
||||
|
||||
Parser.prototype._parseChunkEnd = function(data) {
|
||||
|
||||
var fileCrc = data.readInt32BE(0),
|
||||
calcCrc = this._crc.crc32();
|
||||
|
||||
// check CRC
|
||||
if (this._options.checkCRC && calcCrc != fileCrc) {
|
||||
this.error(new Error('Crc error'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._hasIEND) {
|
||||
this.destroySoon();
|
||||
|
||||
} else {
|
||||
// todo allow _parseSignature to loop chinks?
|
||||
this.read(8, this._parseChunkBegin.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Parser.prototype._handleIHDR = function(length) {
|
||||
this.read(length, this._parseIHDR.bind(this));
|
||||
};
|
||||
Parser.prototype._parseIHDR = function(data) {
|
||||
|
||||
this._crc.write(data);
|
||||
|
||||
var width = data.readUInt32BE(0),
|
||||
height = data.readUInt32BE(4),
|
||||
depth = data[8],
|
||||
colorType = data[9], // bits: 1 palette, 2 color, 4 alpha
|
||||
compr = data[10],
|
||||
filter = data[11],
|
||||
interlace = data[12];
|
||||
|
||||
// console.log(' width', width, 'height', height,
|
||||
// 'depth', depth, 'colorType', colorType,
|
||||
// 'compr', compr, 'filter', filter, 'interlace', interlace
|
||||
// );
|
||||
|
||||
if (depth != 8) {
|
||||
this.error(new Error('Unsupported bit depth ' + depth));
|
||||
return;
|
||||
}
|
||||
if (!(colorType in colorTypeToBppMap)) {
|
||||
this.error(new Error('Unsupported color type'));
|
||||
return;
|
||||
}
|
||||
if (compr != 0) {
|
||||
this.error(new Error('Unsupported compression method'));
|
||||
return;
|
||||
}
|
||||
if (filter != 0) {
|
||||
this.error(new Error('Unsupported filter method'));
|
||||
return;
|
||||
}
|
||||
if (interlace != 0) {
|
||||
this.error(new Error('Unsupported interlace method'));
|
||||
return;
|
||||
}
|
||||
|
||||
this._colorType = colorType;
|
||||
|
||||
this._data = new Buffer(width * height * 4);
|
||||
this._filter = new Filter(
|
||||
width, height,
|
||||
colorTypeToBppMap[this._colorType],
|
||||
this._data,
|
||||
this._options
|
||||
);
|
||||
|
||||
this._hasIHDR = true;
|
||||
|
||||
this.metadata({
|
||||
width: width,
|
||||
height: height,
|
||||
palette: !!(colorType & constants.COLOR_PALETTE),
|
||||
color: !!(colorType & constants.COLOR_COLOR),
|
||||
alpha: !!(colorType & constants.COLOR_ALPHA),
|
||||
data: this._data
|
||||
});
|
||||
|
||||
this._handleChunkEnd();
|
||||
};
|
||||
|
||||
|
||||
Parser.prototype._handlePLTE = function(length) {
|
||||
this.read(length, this._parsePLTE.bind(this));
|
||||
};
|
||||
Parser.prototype._parsePLTE = function(data) {
|
||||
|
||||
this._crc.write(data);
|
||||
|
||||
var entries = Math.floor(data.length / 3);
|
||||
// console.log('Palette:', entries);
|
||||
|
||||
for (var i = 0; i < entries; i++) {
|
||||
this._palette.push([
|
||||
data.readUInt8(i * 3),
|
||||
data.readUInt8(i * 3 + 1),
|
||||
data.readUInt8(i * 3 + 2 ),
|
||||
0xff
|
||||
]);
|
||||
}
|
||||
|
||||
this._handleChunkEnd();
|
||||
};
|
||||
|
||||
Parser.prototype._handleTRNS = function(length) {
|
||||
this.read(length, this._parseTRNS.bind(this));
|
||||
};
|
||||
Parser.prototype._parseTRNS = function(data) {
|
||||
|
||||
this._crc.write(data);
|
||||
|
||||
// palette
|
||||
if (this._colorType == 3) {
|
||||
if (this._palette.length == 0) {
|
||||
this.error(new Error('Transparency chunk must be after palette'));
|
||||
return;
|
||||
}
|
||||
if (data.length > this._palette.length) {
|
||||
this.error(new Error('More transparent colors than palette size'));
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < this._palette.length; i++) {
|
||||
this._palette[i][3] = i < data.length ? data.readUInt8(i) : 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
// for colorType 0 (grayscale) and 2 (rgb)
|
||||
// there might be one gray/color defined as transparent
|
||||
|
||||
this._handleChunkEnd();
|
||||
};
|
||||
|
||||
Parser.prototype._handleGAMA = function(length) {
|
||||
this.read(length, this._parseGAMA.bind(this));
|
||||
};
|
||||
Parser.prototype._parseGAMA = function(data) {
|
||||
|
||||
this._crc.write(data);
|
||||
this.gamma(data.readUInt32BE(0) / 100000);
|
||||
|
||||
this._handleChunkEnd();
|
||||
};
|
||||
|
||||
Parser.prototype._handleIDAT = function(length) {
|
||||
this.read(-length, this._parseIDAT.bind(this, length));
|
||||
};
|
||||
Parser.prototype._parseIDAT = function(length, data) {
|
||||
|
||||
this._crc.write(data);
|
||||
|
||||
if (this._colorType == 3 && this._palette.length == 0)
|
||||
throw new Error('Expected palette not found');
|
||||
|
||||
if (!this._inflate) {
|
||||
this._inflate = zlib.createInflate();
|
||||
|
||||
this._inflate.on('error', this.emit.bind(this, 'error'));
|
||||
this._filter.on('complete', this._reverseFiltered.bind(this));
|
||||
|
||||
this._inflate.pipe(this._filter);
|
||||
}
|
||||
|
||||
this._inflate.write(data);
|
||||
length -= data.length;
|
||||
|
||||
if (length > 0)
|
||||
this._handleIDAT(length);
|
||||
else
|
||||
this._handleChunkEnd();
|
||||
};
|
||||
|
||||
|
||||
Parser.prototype._handleIEND = function(length) {
|
||||
this.read(length, this._parseIEND.bind(this));
|
||||
};
|
||||
Parser.prototype._parseIEND = function(data) {
|
||||
|
||||
this._crc.write(data);
|
||||
|
||||
// no more data to inflate
|
||||
this._inflate.end();
|
||||
|
||||
this._hasIEND = true;
|
||||
this._handleChunkEnd();
|
||||
};
|
||||
|
||||
|
||||
var colorTypeToBppMap = {
|
||||
0: 1,
|
||||
2: 3,
|
||||
3: 1,
|
||||
4: 2,
|
||||
6: 4
|
||||
};
|
||||
|
||||
Parser.prototype._reverseFiltered = function(data, width, height) {
|
||||
|
||||
if (this._colorType == 3) { // paletted
|
||||
|
||||
// use values from palette
|
||||
var pxLineLength = width << 2;
|
||||
|
||||
for (var y = 0; y < height; y++) {
|
||||
var pxRowPos = y * pxLineLength;
|
||||
|
||||
for (var x = 0; x < width; x++) {
|
||||
var pxPos = pxRowPos + (x << 2),
|
||||
color = this._palette[data[pxPos]];
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
data[pxPos + i] = color[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.emit('parsed', data);
|
||||
};
|
||||
17
package.json
17
package.json
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
"name": "node-png",
|
||||
"version": "0.4.3",
|
||||
"description": "Simple PNG encoder/decoder",
|
||||
"author": "Kuba Niegowski",
|
||||
"contributors": [],
|
||||
"homepage": "https://github.com/brighthas/node-pngjs/",
|
||||
"name": "pngjs2",
|
||||
"version": "1.0.0",
|
||||
"description": "Pure JS PNG encoder/decoder",
|
||||
"contributors": ["Kuba Niegowski", "Luke Page", "Pietajan De Potter", "Steven Sojka", "Alexandre Paré", "Gaurav Mali", "liangzeng"],
|
||||
"homepage": "https://github.com/lukeapage/pngjs2",
|
||||
"keywords": [
|
||||
"png"
|
||||
],
|
||||
|
|
@ -16,14 +15,14 @@
|
|||
"example": "examples"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "node test/test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/brighthas/node-pngjs.git"
|
||||
"url": "git://github.com/lukeapage/pngjs2.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/brighthas/node-pngjs/issues"
|
||||
"url": "https://github.com/lukeapage/pngjs2/issues"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue