'use strict';
|
|
var paethPredictor = require('./paeth-predictor');
|
|
function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) {
|
|
for (var x = 0; x < byteWidth; x++) {
|
rawData[rawPos + x] = pxData[pxPos + x];
|
}
|
}
|
|
function filterSumNone(pxData, pxPos, byteWidth) {
|
|
var sum = 0;
|
var length = pxPos + byteWidth;
|
|
for (var i = pxPos; i < length; i++) {
|
sum += Math.abs(pxData[i]);
|
}
|
return sum;
|
}
|
|
function filterSub(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
|
|
for (var x = 0; x < byteWidth; x++) {
|
|
var left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
var val = pxData[pxPos + x] - left;
|
|
rawData[rawPos + x] = val;
|
}
|
}
|
|
function filterSumSub(pxData, pxPos, byteWidth, bpp) {
|
|
var sum = 0;
|
for (var x = 0; x < byteWidth; x++) {
|
|
var left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
var val = pxData[pxPos + x] - left;
|
|
sum += Math.abs(val);
|
}
|
|
return sum;
|
}
|
|
function filterUp(pxData, pxPos, byteWidth, rawData, rawPos) {
|
|
for (var x = 0; x < byteWidth; x++) {
|
|
var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
var val = pxData[pxPos + x] - up;
|
|
rawData[rawPos + x] = val;
|
}
|
}
|
|
function filterSumUp(pxData, pxPos, byteWidth) {
|
|
var sum = 0;
|
var length = pxPos + byteWidth;
|
for (var x = pxPos; x < length; x++) {
|
|
var up = pxPos > 0 ? pxData[x - byteWidth] : 0;
|
var val = pxData[x] - up;
|
|
sum += Math.abs(val);
|
}
|
|
return sum;
|
}
|
|
function filterAvg(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
|
|
for (var x = 0; x < byteWidth; x++) {
|
|
var left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
var val = pxData[pxPos + x] - ((left + up) >> 1);
|
|
rawData[rawPos + x] = val;
|
}
|
}
|
|
function filterSumAvg(pxData, pxPos, byteWidth, bpp) {
|
|
var sum = 0;
|
for (var x = 0; x < byteWidth; x++) {
|
|
var left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
var val = pxData[pxPos + x] - ((left + up) >> 1);
|
|
sum += Math.abs(val);
|
}
|
|
return sum;
|
}
|
|
function filterPaeth(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
|
|
for (var x = 0; x < byteWidth; x++) {
|
|
var left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
var upleft = pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
|
var val = pxData[pxPos + x] - paethPredictor(left, up, upleft);
|
|
rawData[rawPos + x] = val;
|
}
|
}
|
|
function filterSumPaeth(pxData, pxPos, byteWidth, bpp) {
|
var sum = 0;
|
for (var x = 0; x < byteWidth; x++) {
|
|
var left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
|
var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
var upleft = pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
|
var val = pxData[pxPos + x] - paethPredictor(left, up, upleft);
|
|
sum += Math.abs(val);
|
}
|
|
return sum;
|
}
|
|
var filters = {
|
0: filterNone,
|
1: filterSub,
|
2: filterUp,
|
3: filterAvg,
|
4: filterPaeth
|
};
|
|
var filterSums = {
|
0: filterSumNone,
|
1: filterSumSub,
|
2: filterSumUp,
|
3: filterSumAvg,
|
4: filterSumPaeth
|
};
|
|
module.exports = function(pxData, width, height, options, bpp) {
|
|
var filterTypes;
|
if (!('filterType' in options) || options.filterType === -1) {
|
filterTypes = [0, 1, 2, 3, 4];
|
}
|
else if (typeof options.filterType === 'number') {
|
filterTypes = [options.filterType];
|
}
|
else {
|
throw new Error('unrecognised filter types');
|
}
|
|
if (options.bitDepth === 16) {
|
bpp *= 2;
|
}
|
var byteWidth = width * bpp;
|
var rawPos = 0;
|
var pxPos = 0;
|
var rawData = new Buffer((byteWidth + 1) * height);
|
|
var sel = filterTypes[0];
|
|
for (var y = 0; y < height; y++) {
|
|
if (filterTypes.length > 1) {
|
// find best filter for this line (with lowest sum of values)
|
var min = Infinity;
|
|
for (var i = 0; i < filterTypes.length; i++) {
|
var sum = filterSums[filterTypes[i]](pxData, pxPos, byteWidth, bpp);
|
if (sum < min) {
|
sel = filterTypes[i];
|
min = sum;
|
}
|
}
|
}
|
|
rawData[rawPos] = sel;
|
rawPos++;
|
filters[sel](pxData, pxPos, byteWidth, rawData, rawPos, bpp);
|
rawPos += byteWidth;
|
pxPos += byteWidth;
|
}
|
return rawData;
|
};
|