mirror of
https://github.com/danbulant/pngjs
synced 2026-07-08 20:50:35 +00:00
linting and tidy
This commit is contained in:
parent
2398b72c8f
commit
8155d300f0
13 changed files with 180 additions and 534 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"ecmaFeatures": {},
|
"ecmaFeatures": {},
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-alert": 1,
|
"no-alert": 2,
|
||||||
"no-array-constructor": 1,
|
"no-array-constructor": 1,
|
||||||
"no-bitwise": 0,
|
"no-bitwise": 0,
|
||||||
"no-caller": 2,
|
"no-caller": 2,
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
"no-empty": 2,
|
"no-empty": 2,
|
||||||
"no-empty-character-class": 2,
|
"no-empty-character-class": 2,
|
||||||
"no-empty-label": 1,
|
"no-empty-label": 1,
|
||||||
"no-eq-null": 1,
|
"no-eq-null": 0,
|
||||||
"no-eval": 2,
|
"no-eval": 2,
|
||||||
"no-ex-assign": 2,
|
"no-ex-assign": 2,
|
||||||
"no-extend-native": 0,
|
"no-extend-native": 0,
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
"no-func-assign": 2,
|
"no-func-assign": 2,
|
||||||
"no-implicit-coercion": 0,
|
"no-implicit-coercion": 0,
|
||||||
"no-implied-eval": 1,
|
"no-implied-eval": 1,
|
||||||
"no-inline-comments": 1,
|
"no-inline-comments": 0,
|
||||||
"no-inner-declarations": [2, "functions"],
|
"no-inner-declarations": [2, "functions"],
|
||||||
"no-invalid-regexp": 2,
|
"no-invalid-regexp": 2,
|
||||||
"no-invalid-this": 1,
|
"no-invalid-this": 1,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,34 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
var interlaceUtils = require('./interlace');
|
var interlaceUtils = require('./interlace');
|
||||||
|
|
||||||
|
var pixelBppMap = {
|
||||||
|
1: { // L
|
||||||
|
0: 0,
|
||||||
|
1: 0,
|
||||||
|
2: 0,
|
||||||
|
3: 0xff
|
||||||
|
},
|
||||||
|
2: { // LA
|
||||||
|
0: 0,
|
||||||
|
1: 0,
|
||||||
|
2: 0,
|
||||||
|
3: 1
|
||||||
|
},
|
||||||
|
3: { // RGB
|
||||||
|
0: 0,
|
||||||
|
1: 1,
|
||||||
|
2: 2,
|
||||||
|
3: 0xff
|
||||||
|
},
|
||||||
|
4: { // RGBA
|
||||||
|
0: 0,
|
||||||
|
1: 1,
|
||||||
|
2: 2,
|
||||||
|
3: 3
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function bitRetriever(data, depth) {
|
function bitRetriever(data, depth) {
|
||||||
|
|
||||||
var leftOver = [];
|
var leftOver = [];
|
||||||
|
|
@ -14,7 +43,6 @@ function bitRetriever(data, depth) {
|
||||||
switch(depth) {
|
switch(depth) {
|
||||||
default:
|
default:
|
||||||
throw new Error("unrecognised depth");
|
throw new Error("unrecognised depth");
|
||||||
break;
|
|
||||||
case 16:
|
case 16:
|
||||||
byte2 = data[i];
|
byte2 = data[i];
|
||||||
i++;
|
i++;
|
||||||
|
|
@ -74,8 +102,7 @@ exports.dataToBitMap = function(data, width, height, bpp, depth, interlace) {
|
||||||
if (depth <= 8) {
|
if (depth <= 8) {
|
||||||
pxData = new Buffer(width * height * 4);
|
pxData = new Buffer(width * height * 4);
|
||||||
} else {
|
} else {
|
||||||
// TODO: could be more effecient and use a buffer but change how we write to use 16 bit write methods with index * 2
|
pxData = new Uint16Array(width * height * 4);
|
||||||
pxData = new Array(width * height * 4);
|
|
||||||
}
|
}
|
||||||
var maxBit = Math.pow(2, depth) - 1;
|
var maxBit = Math.pow(2, depth) - 1;
|
||||||
var rawPos = 0;
|
var rawPos = 0;
|
||||||
|
|
@ -136,30 +163,3 @@ exports.dataToBitMap = function(data, width, height, bpp, depth, interlace) {
|
||||||
|
|
||||||
return pxData;
|
return pxData;
|
||||||
};
|
};
|
||||||
|
|
||||||
var pixelBppMap = {
|
|
||||||
1: { // L
|
|
||||||
0: 0,
|
|
||||||
1: 0,
|
|
||||||
2: 0,
|
|
||||||
3: 0xff
|
|
||||||
},
|
|
||||||
2: { // LA
|
|
||||||
0: 0,
|
|
||||||
1: 0,
|
|
||||||
2: 0,
|
|
||||||
3: 1
|
|
||||||
},
|
|
||||||
3: { // RGB
|
|
||||||
0: 0,
|
|
||||||
1: 1,
|
|
||||||
2: 2,
|
|
||||||
3: 0xff
|
|
||||||
},
|
|
||||||
4: { // RGBA
|
|
||||||
0: 0,
|
|
||||||
1: 1,
|
|
||||||
2: 2,
|
|
||||||
3: 3
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -138,22 +138,22 @@ ChunkStream.prototype._process = function() {
|
||||||
this._reads.shift(); // == read
|
this._reads.shift(); // == read
|
||||||
|
|
||||||
// first we need to peek into first buffer
|
// first we need to peek into first buffer
|
||||||
var buf = this._buffers[0];
|
var smallerBuf = this._buffers[0];
|
||||||
|
|
||||||
// ok there is more data than we need
|
// ok there is more data than we need
|
||||||
if (buf.length > read.length) {
|
if (smallerBuf.length > read.length) {
|
||||||
|
|
||||||
this._buffered -= read.length;
|
this._buffered -= read.length;
|
||||||
this._buffers[0] = buf.slice(read.length);
|
this._buffers[0] = smallerBuf.slice(read.length);
|
||||||
|
|
||||||
read.func.call(this, buf.slice(0, read.length));
|
read.func.call(this, smallerBuf.slice(0, read.length));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// ok this is less than maximum length so use it all
|
// ok this is less than maximum length so use it all
|
||||||
this._buffered -= buf.length;
|
this._buffered -= smallerBuf.length;
|
||||||
this._buffers.shift(); // == buf
|
this._buffers.shift(); // == smallerBuf
|
||||||
|
|
||||||
read.func.call(this, buf);
|
read.func.call(this, smallerBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (this._buffered >= read.length) {
|
} else if (this._buffered >= read.length) {
|
||||||
|
|
|
||||||
32
lib/crc.js
32
lib/crc.js
|
|
@ -20,6 +20,22 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var crcTable = [];
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
for (var i = 0; i < 256; i++) {
|
||||||
|
var c = i;
|
||||||
|
for (var j = 0; j < 8; j++) {
|
||||||
|
if (c & 1) {
|
||||||
|
c = 0xedb88320 ^ (c >>> 1);
|
||||||
|
} else {
|
||||||
|
c = c >>> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crcTable[i] = c;
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
var CrcCalculator = module.exports = function() {
|
var CrcCalculator = module.exports = function() {
|
||||||
this._crc = -1;
|
this._crc = -1;
|
||||||
};
|
};
|
||||||
|
|
@ -45,19 +61,3 @@ CrcCalculator.crc32 = function(buf) {
|
||||||
}
|
}
|
||||||
return crc ^ -1;
|
return crc ^ -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var crcTable = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < 256; i++) {
|
|
||||||
var c = i;
|
|
||||||
for (var j = 0; j < 8; j++) {
|
|
||||||
if (c & 1) {
|
|
||||||
c = 0xedb88320 ^ (c >>> 1);
|
|
||||||
} else {
|
|
||||||
c = c >>> 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
crcTable[i] = c;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ var FilterAsync = module.exports = function(width, height, Bpp, depth, interlace
|
||||||
var that = this;
|
var that = this;
|
||||||
this._filter = new Filter(width, height, Bpp, depth, interlace, options, {
|
this._filter = new Filter(width, height, Bpp, depth, interlace, options, {
|
||||||
read: this.read.bind(this),
|
read: this.read.bind(this),
|
||||||
complete: function(width, height) {
|
complete: function() {
|
||||||
that.emit('complete', Buffer.concat(buffers), width, height)
|
that.emit('complete', Buffer.concat(buffers), width, height)
|
||||||
},
|
},
|
||||||
write: function(buffer) {
|
write: function(buffer) {
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,14 @@ var SyncReader = require('./sync-reader'),
|
||||||
Filter = require('./filter');
|
Filter = require('./filter');
|
||||||
|
|
||||||
|
|
||||||
exports.process = function(buffer, width, height, Bpp, depth, interlace, options) {
|
exports.process = function(inBuffer, width, height, Bpp, depth, interlace, options) {
|
||||||
|
|
||||||
var buffers = [];
|
var outBuffers = [];
|
||||||
var reader = new SyncReader(buffer);
|
var reader = new SyncReader(inBuffer);
|
||||||
var filter = new Filter(width, height, Bpp, depth, interlace, options, {
|
var filter = new Filter(width, height, Bpp, depth, interlace, options, {
|
||||||
read: reader.read.bind(reader),
|
read: reader.read.bind(reader),
|
||||||
write: function(buffer) {
|
write: function(bufferPart) {
|
||||||
buffers.push(buffer);
|
outBuffers.push(bufferPart);
|
||||||
},
|
},
|
||||||
complete: function(){}
|
complete: function(){}
|
||||||
});
|
});
|
||||||
|
|
@ -39,5 +39,5 @@ exports.process = function(buffer, width, height, Bpp, depth, interlace, options
|
||||||
filter.start();
|
filter.start();
|
||||||
reader.process();
|
reader.process();
|
||||||
|
|
||||||
return Buffer.concat(buffers);
|
return Buffer.concat(outBuffers);
|
||||||
};
|
};
|
||||||
|
|
@ -30,6 +30,23 @@ function getByteWidth(width, bpp, depth) {
|
||||||
return byteWidth;
|
return byteWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function PaethPredictor(left, above, upLeft) {
|
||||||
|
|
||||||
|
var p = left + above - upLeft,
|
||||||
|
pLeft = Math.abs(p - left),
|
||||||
|
pAbove = Math.abs(p - above),
|
||||||
|
pUpLeft = Math.abs(p - upLeft);
|
||||||
|
|
||||||
|
if (pLeft <= pAbove && pLeft <= pUpLeft) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
if (pAbove <= pUpLeft) {
|
||||||
|
return above;
|
||||||
|
}
|
||||||
|
return upLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var Filter = module.exports = function(width, height, Bpp, depth, interlace, options, dependencies) {
|
var Filter = module.exports = function(width, height, Bpp, depth, interlace, options, dependencies) {
|
||||||
|
|
||||||
this._width = width;
|
this._width = width;
|
||||||
|
|
@ -97,26 +114,26 @@ Filter.prototype._reverseFilterLine = function(rawData) {
|
||||||
line[x] = rawByte;
|
line[x] = rawByte;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
var left = x > xBiggerThan ? line[x - xComparison] : 0;
|
var f1_left = x > xBiggerThan ? line[x - xComparison] : 0;
|
||||||
line[x] = rawByte + left;
|
line[x] = rawByte + f1_left;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
var up = this._lastLine ? this._lastLine[x] : 0;
|
var f2_up = this._lastLine ? this._lastLine[x] : 0;
|
||||||
line[x] = rawByte + up;
|
line[x] = rawByte + f2_up;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
var up = this._lastLine ? this._lastLine[x] : 0;
|
var f3_up = this._lastLine ? this._lastLine[x] : 0;
|
||||||
var left = x > xBiggerThan ? line[x - xComparison] : 0;
|
var f3_left = x > xBiggerThan ? line[x - xComparison] : 0;
|
||||||
var add = Math.floor((left + up) / 2);
|
var f3_add = Math.floor((f3_left + f3_up) / 2);
|
||||||
line[x] = rawByte + add;
|
line[x] = rawByte + f3_add;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
var up = this._lastLine ? this._lastLine[x] : 0;
|
var f4_up = this._lastLine ? this._lastLine[x] : 0;
|
||||||
var left = x > xBiggerThan ? line[x - xComparison] : 0;
|
var f4_left = x > xBiggerThan ? line[x - xComparison] : 0;
|
||||||
var upLeft = x > xBiggerThan && this._lastLine
|
var f4_upLeft = x > xBiggerThan && this._lastLine
|
||||||
? this._lastLine[x - xComparison] : 0;
|
? this._lastLine[x - xComparison] : 0;
|
||||||
var add = PaethPredictor(left, up, upLeft);
|
var f4_add = PaethPredictor(f4_left, f4_up, f4_upLeft);
|
||||||
line[x] = rawByte + add;
|
line[x] = rawByte + f4_add;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,17 +285,3 @@ Filter.prototype._filterPaeth = function(pxData, y, rawData) {
|
||||||
}
|
}
|
||||||
return sum;
|
return sum;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var PaethPredictor = function(left, above, upLeft) {
|
|
||||||
|
|
||||||
var p = left + above - upLeft,
|
|
||||||
pLeft = Math.abs(p - left),
|
|
||||||
pAbove = Math.abs(p - above),
|
|
||||||
pUpLeft = Math.abs(p - upLeft);
|
|
||||||
|
|
||||||
if (pLeft <= pAbove && pLeft <= pUpLeft) return left;
|
|
||||||
else if (pAbove <= pUpLeft) return above;
|
|
||||||
else return upLeft;
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,4 @@
|
||||||
exports.getImagePasses = function(width, height) {
|
'use strict';
|
||||||
var images = [];
|
|
||||||
var xLeftOver = width % 8;
|
|
||||||
var yLeftOver = height % 8;
|
|
||||||
var xRepeats = (width - xLeftOver) / 8;
|
|
||||||
var yRepeats = (width - yLeftOver) / 8;
|
|
||||||
for(var i = 0; i < imagePasses.length; i++) {
|
|
||||||
var pass = imagePasses[i];
|
|
||||||
var passWidth = xRepeats * pass.x.length;
|
|
||||||
var passHeight = yRepeats * pass.y.length;
|
|
||||||
for(var j = 0; j < pass.x.length; j++) {
|
|
||||||
if (pass.x[j] < xLeftOver) {
|
|
||||||
passWidth++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(j = 0; j < pass.y.length; j++) {
|
|
||||||
if (pass.y[j] < yLeftOver) {
|
|
||||||
passHeight++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (passWidth > 0 && passHeight > 0) {
|
|
||||||
images.push({ width: passWidth, height: passHeight, index: i});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return images;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getInterlaceIterator = function(width, height) {
|
|
||||||
return function(x, y, pass) {
|
|
||||||
var outerXLeftOver = x % imagePasses[pass].x.length;
|
|
||||||
var outerX = (((x - outerXLeftOver)/ imagePasses[pass].x.length) * 8) + imagePasses[pass].x[outerXLeftOver];
|
|
||||||
var outerYLeftOver = y % imagePasses[pass].y.length;
|
|
||||||
var outerY = (((y - outerYLeftOver) / imagePasses[pass].y.length) * 8) + imagePasses[pass].y[outerYLeftOver];
|
|
||||||
return (outerX * 4) + (outerY * width * 4);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Adam 7
|
// Adam 7
|
||||||
// 0 1 2 3 4 5 6 7
|
// 0 1 2 3 4 5 6 7
|
||||||
|
|
@ -82,4 +41,47 @@ var imagePasses = [
|
||||||
x: [0, 1, 2, 3, 4, 5, 6, 7],
|
x: [0, 1, 2, 3, 4, 5, 6, 7],
|
||||||
y: [1, 3, 5, 7]
|
y: [1, 3, 5, 7]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
exports.getImagePasses = function(width, height) {
|
||||||
|
var images = [];
|
||||||
|
var xLeftOver = width % 8;
|
||||||
|
var yLeftOver = height % 8;
|
||||||
|
var xRepeats = (width - xLeftOver) / 8;
|
||||||
|
var yRepeats = (width - yLeftOver) / 8;
|
||||||
|
for(var i = 0; i < imagePasses.length; i++) {
|
||||||
|
var pass = imagePasses[i];
|
||||||
|
var passWidth = xRepeats * pass.x.length;
|
||||||
|
var passHeight = yRepeats * pass.y.length;
|
||||||
|
for(var j = 0; j < pass.x.length; j++) {
|
||||||
|
if (pass.x[j] < xLeftOver) {
|
||||||
|
passWidth++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(j = 0; j < pass.y.length; j++) {
|
||||||
|
if (pass.y[j] < yLeftOver) {
|
||||||
|
passHeight++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (passWidth > 0 && passHeight > 0) {
|
||||||
|
images.push({ width: passWidth, height: passHeight, index: i});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return images;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getInterlaceIterator = function(width) {
|
||||||
|
return function(x, y, pass) {
|
||||||
|
var outerXLeftOver = x % imagePasses[pass].x.length;
|
||||||
|
var outerX = (((x - outerXLeftOver)/ imagePasses[pass].x.length) * 8) + imagePasses[pass].x[outerXLeftOver];
|
||||||
|
var outerYLeftOver = y % imagePasses[pass].y.length;
|
||||||
|
var outerY = (((y - outerYLeftOver) / imagePasses[pass].y.length) * 8) + imagePasses[pass].y[outerYLeftOver];
|
||||||
|
return (outerX * 4) + (outerY * width * 4);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -52,7 +52,7 @@ Packer.prototype.pack = function(data, width, height) {
|
||||||
// filter pixel data
|
// filter pixel data
|
||||||
//TODO {}
|
//TODO {}
|
||||||
var filter = new Filter(width, height, 4, 8, false, this._options, {});
|
var filter = new Filter(width, height, 4, 8, false, this._options, {});
|
||||||
var data = filter.filter(data);
|
var filteredData = filter.filter(data);
|
||||||
|
|
||||||
// compress it
|
// compress it
|
||||||
var deflate = zlib.createDeflate({
|
var deflate = zlib.createDeflate({
|
||||||
|
|
@ -62,8 +62,8 @@ Packer.prototype.pack = function(data, width, height) {
|
||||||
});
|
});
|
||||||
deflate.on('error', this.emit.bind(this, 'error'));
|
deflate.on('error', this.emit.bind(this, 'error'));
|
||||||
|
|
||||||
deflate.on('data', function(data) {
|
deflate.on('data', function(compressedData) {
|
||||||
this.emit('data', this._packIDAT(data));
|
this.emit('data', this._packIDAT(compressedData));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
deflate.on('end', function() {
|
deflate.on('end', function() {
|
||||||
|
|
@ -71,7 +71,7 @@ Packer.prototype.pack = function(data, width, height) {
|
||||||
this.emit('end');
|
this.emit('end');
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
deflate.end(data);
|
deflate.end(filteredData);
|
||||||
};
|
};
|
||||||
|
|
||||||
Packer.prototype._packChunk = function(type, data) {
|
Packer.prototype._packChunk = function(type, data) {
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ ParserAsync.prototype._createData = function(width, height, bpp, depth, interlac
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ParserAsync.prototype._finished = function(data) {
|
ParserAsync.prototype._finished = function() {
|
||||||
if (this.errord) {
|
if (this.errord) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,361 +0,0 @@
|
||||||
// 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);
|
|
||||||
};
|
|
||||||
|
|
@ -97,9 +97,9 @@ Parser.prototype._parseChunkBegin = function(data) {
|
||||||
//console.log('chunk ', name, length);
|
//console.log('chunk ', name, length);
|
||||||
|
|
||||||
// chunk flags
|
// chunk flags
|
||||||
var ancillary = !!(data[4] & 0x20), // or critical
|
var ancillary = !!(data[4] & 0x20); // or critical
|
||||||
priv = !!(data[5] & 0x20), // or public
|
// priv = !!(data[5] & 0x20), // or public
|
||||||
safeToCopy = !!(data[7] & 0x20); // or unsafe
|
// safeToCopy = !!(data[7] & 0x20); // or unsafe
|
||||||
|
|
||||||
if (!this._hasIHDR && type != constants.TYPE_IHDR) {
|
if (!this._hasIHDR && type != constants.TYPE_IHDR) {
|
||||||
this.error(new Error('Expected IHDR on beggining'));
|
this.error(new Error('Expected IHDR on beggining'));
|
||||||
|
|
@ -111,16 +111,17 @@ Parser.prototype._parseChunkBegin = function(data) {
|
||||||
|
|
||||||
if (this._chunks[type]) {
|
if (this._chunks[type]) {
|
||||||
return this._chunks[type](length);
|
return this._chunks[type](length);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (!ancillary) {
|
if (!ancillary) {
|
||||||
this.error(new Error('Unsupported critical chunk type ' + name));
|
this.error(new Error('Unsupported critical chunk type ' + name));
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
this.read(length + 4, this._skipChunk.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.read(length + 4, this._skipChunk.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser.prototype._skipChunk = function(data) {
|
Parser.prototype._skipChunk = function(/*data*/) {
|
||||||
this.read(8, this._parseChunkBegin.bind(this));
|
this.read(8, this._parseChunkBegin.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -218,9 +219,9 @@ Parser.prototype._parsePLTE = function(data) {
|
||||||
|
|
||||||
for (var i = 0; i < entries; i++) {
|
for (var i = 0; i < entries; i++) {
|
||||||
this._palette.push([
|
this._palette.push([
|
||||||
data.readUInt8(i * 3),
|
data[i * 3],
|
||||||
data.readUInt8(i * 3 + 1),
|
data[i * 3 + 1],
|
||||||
data.readUInt8(i * 3 + 2 ),
|
data[i * 3 + 2 ],
|
||||||
0xff
|
0xff
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +247,7 @@ Parser.prototype._parseTRNS = function(data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (var i = 0; i < this._palette.length; i++) {
|
for (var i = 0; i < this._palette.length; i++) {
|
||||||
this._palette[i][3] = i < data.length ? data.readUInt8(i) : 0xff;
|
this._palette[i][3] = i < data.length ? data[i] : 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,35 +312,34 @@ Parser.prototype.reverseFiltered = function(indata, depth, width, height) {
|
||||||
|
|
||||||
var outdata = indata; // only different for 16 bits
|
var outdata = indata; // only different for 16 bits
|
||||||
|
|
||||||
|
var pxLineLength = width << 2;
|
||||||
|
var x, y, i, pxPos, pxRowPos;
|
||||||
|
|
||||||
if (this._colorType == 3) { // paletted
|
if (this._colorType == 3) { // paletted
|
||||||
//console.log("paletted");
|
//TODO abstract loop?
|
||||||
// use values from palette
|
// use values from palette
|
||||||
var pxLineLength = width << 2;
|
for (y = 0; y < height; y++) {
|
||||||
|
pxRowPos = y * pxLineLength;
|
||||||
|
|
||||||
for (var y = 0; y < height; y++) {
|
for (x = 0; x < width; x++) {
|
||||||
var pxRowPos = y * pxLineLength;
|
pxPos = pxRowPos + (x << 2);
|
||||||
|
var color = this._palette[indata[pxPos]];
|
||||||
for (var x = 0; x < width; x++) {
|
|
||||||
var pxPos = pxRowPos + (x << 2),
|
|
||||||
color = this._palette[indata[pxPos]];
|
|
||||||
|
|
||||||
if (!color) {
|
if (!color) {
|
||||||
throw new Error("index " + indata[pxPos] + " not in palette");
|
throw new Error("index " + indata[pxPos] + " not in palette");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
indata[pxPos + i] = color[i];
|
indata[pxPos + i] = color[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var pxLineLength = width << 2;
|
|
||||||
|
|
||||||
if (this._transColor) {
|
if (this._transColor) {
|
||||||
for (var y = 0; y < height; y++) {
|
for (y = 0; y < height; y++) {
|
||||||
var pxRowPos = y * pxLineLength;
|
pxRowPos = y * pxLineLength;
|
||||||
|
|
||||||
for (var x = 0; x < width; x++) {
|
for (x = 0; x < width; x++) {
|
||||||
var pxPos = pxRowPos + (x << 2);
|
pxPos = pxRowPos + (x << 2);
|
||||||
var makeTrans = false;
|
var makeTrans = false;
|
||||||
//console.log(pxPos);
|
//console.log(pxPos);
|
||||||
if (this._transColor.length === 1) {
|
if (this._transColor.length === 1) {
|
||||||
|
|
@ -350,7 +350,7 @@ Parser.prototype.reverseFiltered = function(indata, depth, width, height) {
|
||||||
makeTrans = true;
|
makeTrans = true;
|
||||||
}
|
}
|
||||||
if (makeTrans) {
|
if (makeTrans) {
|
||||||
for (var i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
indata[pxPos + i] = 0;
|
indata[pxPos + i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -365,12 +365,12 @@ Parser.prototype.reverseFiltered = function(indata, depth, width, height) {
|
||||||
var maxOutSample = 255;
|
var maxOutSample = 255;
|
||||||
var maxInSample = Math.pow(2, depth) - 1;
|
var maxInSample = Math.pow(2, depth) - 1;
|
||||||
|
|
||||||
for (var y = 0; y < height; y++) {
|
for (y = 0; y < height; y++) {
|
||||||
var pxRowPos = y * pxLineLength;
|
pxRowPos = y * pxLineLength;
|
||||||
|
|
||||||
for (var x = 0; x < width; x++) {
|
for (x = 0; x < width; x++) {
|
||||||
var pxPos = pxRowPos + (x << 2);
|
pxPos = pxRowPos + (x << 2);
|
||||||
for (var i = 0; i < 4; i++)
|
for (i = 0; i < 4; i++)
|
||||||
outdata[pxPos + i] = Math.floor((indata[pxPos + i] * maxOutSample) / maxInSample + 0.5);
|
outdata[pxPos + i] = Math.floor((indata[pxPos + i] * maxOutSample) / maxInSample + 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,9 @@ var PNG = exports.PNG = function(options) {
|
||||||
this.data = this.width > 0 && this.height > 0
|
this.data = this.width > 0 && this.height > 0
|
||||||
? new Buffer(4 * this.width * this.height) : null;
|
? new Buffer(4 * this.width * this.height) : null;
|
||||||
|
|
||||||
if(options.fill && this.data){this.data.fill(0)};
|
if(options.fill && this.data){
|
||||||
|
this.data.fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
this.gamma = 0;
|
this.gamma = 0;
|
||||||
this.readable = this.writable = true;
|
this.readable = this.writable = true;
|
||||||
|
|
@ -81,10 +83,10 @@ PNG.prototype.parse = function(data, callback) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
var onParsed = null, onError = null;
|
var onParsed = null, onError = null;
|
||||||
|
|
||||||
this.once('parsed', onParsed = function(data) {
|
this.once('parsed', onParsed = function(parsedData) {
|
||||||
this.removeListener('error', onError);
|
this.removeListener('error', onError);
|
||||||
|
|
||||||
this.data = data;
|
this.data = parsedData;
|
||||||
callback(null, this);
|
callback(null, this);
|
||||||
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue