'use strict';
|
|
var interlaceUtils = require('./interlace');
|
var paethPredictor = require('./paeth-predictor');
|
|
function getByteWidth(width, bpp, depth) {
|
var byteWidth = width * bpp;
|
if (depth !== 8) {
|
byteWidth = Math.ceil(byteWidth / (8 / depth));
|
}
|
return byteWidth;
|
}
|
|
var Filter = module.exports = function(bitmapInfo, dependencies) {
|
|
var width = bitmapInfo.width;
|
var height = bitmapInfo.height;
|
var interlace = bitmapInfo.interlace;
|
var bpp = bitmapInfo.bpp;
|
var depth = bitmapInfo.depth;
|
|
this.read = dependencies.read;
|
this.write = dependencies.write;
|
this.complete = dependencies.complete;
|
|
this._imageIndex = 0;
|
this._images = [];
|
if (interlace) {
|
var passes = interlaceUtils.getImagePasses(width, height);
|
for (var i = 0; i < passes.length; i++) {
|
this._images.push({
|
byteWidth: getByteWidth(passes[i].width, bpp, depth),
|
height: passes[i].height,
|
lineIndex: 0
|
});
|
}
|
}
|
else {
|
this._images.push({
|
byteWidth: getByteWidth(width, bpp, depth),
|
height: height,
|
lineIndex: 0
|
});
|
}
|
|
// when filtering the line we look at the pixel to the left
|
// the spec also says it is done on a byte level regardless of the number of pixels
|
// so if the depth is byte compatible (8 or 16) we subtract the bpp in order to compare back
|
// a pixel rather than just a different byte part. However if we are sub byte, we ignore.
|
if (depth === 8) {
|
this._xComparison = bpp;
|
}
|
else if (depth === 16) {
|
this._xComparison = bpp * 2;
|
}
|
else {
|
this._xComparison = 1;
|
}
|
};
|
|
Filter.prototype.start = function() {
|
this.read(this._images[this._imageIndex].byteWidth + 1, this._reverseFilterLine.bind(this));
|
};
|
|
Filter.prototype._unFilterType1 = function(rawData, unfilteredLine, byteWidth) {
|
|
var xComparison = this._xComparison;
|
var xBiggerThan = xComparison - 1;
|
|
for (var x = 0; x < byteWidth; x++) {
|
var rawByte = rawData[1 + x];
|
var f1Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
|
unfilteredLine[x] = rawByte + f1Left;
|
}
|
};
|
|
Filter.prototype._unFilterType2 = function(rawData, unfilteredLine, byteWidth) {
|
|
var lastLine = this._lastLine;
|
|
for (var x = 0; x < byteWidth; x++) {
|
var rawByte = rawData[1 + x];
|
var f2Up = lastLine ? lastLine[x] : 0;
|
unfilteredLine[x] = rawByte + f2Up;
|
}
|
};
|
|
Filter.prototype._unFilterType3 = function(rawData, unfilteredLine, byteWidth) {
|
|
var xComparison = this._xComparison;
|
var xBiggerThan = xComparison - 1;
|
var lastLine = this._lastLine;
|
|
for (var x = 0; x < byteWidth; x++) {
|
var rawByte = rawData[1 + x];
|
var f3Up = lastLine ? lastLine[x] : 0;
|
var f3Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
|
var f3Add = Math.floor((f3Left + f3Up) / 2);
|
unfilteredLine[x] = rawByte + f3Add;
|
}
|
};
|
|
Filter.prototype._unFilterType4 = function(rawData, unfilteredLine, byteWidth) {
|
|
var xComparison = this._xComparison;
|
var xBiggerThan = xComparison - 1;
|
var lastLine = this._lastLine;
|
|
for (var x = 0; x < byteWidth; x++) {
|
var rawByte = rawData[1 + x];
|
var f4Up = lastLine ? lastLine[x] : 0;
|
var f4Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
|
var f4UpLeft = x > xBiggerThan && lastLine ? lastLine[x - xComparison] : 0;
|
var f4Add = paethPredictor(f4Left, f4Up, f4UpLeft);
|
unfilteredLine[x] = rawByte + f4Add;
|
}
|
};
|
|
Filter.prototype._reverseFilterLine = function(rawData) {
|
|
var filter = rawData[0];
|
var unfilteredLine;
|
var currentImage = this._images[this._imageIndex];
|
var byteWidth = currentImage.byteWidth;
|
|
if (filter === 0) {
|
unfilteredLine = rawData.slice(1, byteWidth + 1);
|
}
|
else {
|
|
unfilteredLine = new Buffer(byteWidth);
|
|
switch (filter) {
|
case 1:
|
this._unFilterType1(rawData, unfilteredLine, byteWidth);
|
break;
|
case 2:
|
this._unFilterType2(rawData, unfilteredLine, byteWidth);
|
break;
|
case 3:
|
this._unFilterType3(rawData, unfilteredLine, byteWidth);
|
break;
|
case 4:
|
this._unFilterType4(rawData, unfilteredLine, byteWidth);
|
break;
|
default:
|
throw new Error('Unrecognised filter type - ' + filter);
|
}
|
}
|
|
this.write(unfilteredLine);
|
|
currentImage.lineIndex++;
|
if (currentImage.lineIndex >= currentImage.height) {
|
this._lastLine = null;
|
this._imageIndex++;
|
currentImage = this._images[this._imageIndex];
|
}
|
else {
|
this._lastLine = unfilteredLine;
|
}
|
|
if (currentImage) {
|
// read, using the byte width that may be from the new current image
|
this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this));
|
}
|
else {
|
this._lastLine = null;
|
this.complete();
|
}
|
};
|