mirror of
https://github.com/danbulant/pngjs
synced 2026-06-22 00:01:55 +00:00
Tidy up filtering.
This commit is contained in:
parent
6607d71c04
commit
adf5216a49
12 changed files with 394 additions and 331 deletions
|
|
@ -4,8 +4,8 @@ clone_depth: 10
|
|||
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: 0.12
|
||||
- nodejs_version: 2.x
|
||||
- nodejs_version: '0.12'
|
||||
- nodejs_version: ''
|
||||
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
|
|
|
|||
186
lib/filter-pack.js
Normal file
186
lib/filter-pack.js
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
'use strict';
|
||||
|
||||
var paethPredictor = require('./paeth-predictor');
|
||||
|
||||
function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) {
|
||||
|
||||
pxData.copy(rawData, rawPos, pxPos, pxPos + byteWidth);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
for (var x = 0; x < byteWidth; x++) {
|
||||
|
||||
var left = x >= 4 ? pxData[pxPos + x - 4] : 0;
|
||||
var val = pxData[pxPos + x] - left;
|
||||
|
||||
rawData[rawPos + x] = val;
|
||||
}
|
||||
}
|
||||
|
||||
function filterSumSub(pxData, pxPos, byteWidth) {
|
||||
|
||||
var sum = 0;
|
||||
for (var x = 0; x < byteWidth; x++) {
|
||||
|
||||
var left = x >= 4 ? pxData[pxPos + x - 4] : 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) {
|
||||
|
||||
for (var x = 0; x < byteWidth; x++) {
|
||||
|
||||
var left = x >= 4 ? pxData[pxPos + x - 4] : 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) {
|
||||
|
||||
var sum = 0;
|
||||
for (var x = 0; x < byteWidth; x++) {
|
||||
|
||||
var left = x >= 4 ? pxData[pxPos + x - 4] : 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) {
|
||||
|
||||
for (var x = 0; x < byteWidth; x++) {
|
||||
|
||||
var left = x >= 4 ? pxData[pxPos + x - 4] : 0;
|
||||
var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
||||
var upleft = pxPos > 0 && x >= 4 ? pxData[pxPos + x - (byteWidth + 4)] : 0;
|
||||
var val = pxData[pxPos + x] - paethPredictor(left, up, upleft);
|
||||
|
||||
rawData[rawPos + x] = val;
|
||||
}
|
||||
}
|
||||
|
||||
function filterSumPaeth(pxData, pxPos, byteWidth) {
|
||||
|
||||
var sum = 0;
|
||||
for (var x = 0; x < byteWidth; x++) {
|
||||
|
||||
var left = x >= 4 ? pxData[pxPos + x - 4] : 0;
|
||||
var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
|
||||
var upleft = pxPos > 0 && x >= 4 ? pxData[pxPos + x - (byteWidth + 4)] : 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) {
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
var byteWidth = width << 2;
|
||||
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);
|
||||
|
||||
if (sum < min) {
|
||||
sel = filterTypes[i];
|
||||
min = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rawData[rawPos] = sel;
|
||||
rawPos++;
|
||||
filters[sel](pxData, pxPos, byteWidth, rawData, rawPos);
|
||||
rawPos += byteWidth;
|
||||
pxPos += byteWidth;
|
||||
}
|
||||
return rawData;
|
||||
};
|
||||
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
var util = require('util');
|
||||
var ChunkStream = require('./chunkstream');
|
||||
var Filter = require('./filter');
|
||||
var Filter = require('./filter-parse');
|
||||
|
||||
|
||||
var FilterAsync = module.exports = function(width, height, Bpp, depth, interlace, options) {
|
||||
var FilterAsync = module.exports = function(width, height, Bpp, depth, interlace) {
|
||||
ChunkStream.call(this);
|
||||
|
||||
var buffers = [];
|
||||
var that = this;
|
||||
this._filter = new Filter(width, height, Bpp, depth, interlace, options, {
|
||||
this._filter = new Filter(width, height, Bpp, depth, interlace, {
|
||||
read: this.read.bind(this),
|
||||
complete: function() {
|
||||
that.emit('complete', Buffer.concat(buffers), width, height);
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
var SyncReader = require('./sync-reader');
|
||||
var Filter = require('./filter');
|
||||
var Filter = require('./filter-parse');
|
||||
|
||||
|
||||
exports.process = function(inBuffer, width, height, Bpp, depth, interlace, options) {
|
||||
exports.process = function(inBuffer, width, height, Bpp, depth, interlace) {
|
||||
|
||||
var outBuffers = [];
|
||||
var reader = new SyncReader(inBuffer);
|
||||
var filter = new Filter(width, height, Bpp, depth, interlace, options, {
|
||||
var filter = new Filter(width, height, Bpp, depth, interlace, {
|
||||
read: reader.read.bind(reader),
|
||||
write: function(bufferPart) {
|
||||
outBuffers.push(bufferPart);
|
||||
169
lib/filter-parse.js
Normal file
169
lib/filter-parse.js
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
'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(width, height, Bpp, depth, interlace, dependencies) {
|
||||
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._Bpp = Bpp; //TODO rename
|
||||
this._depth = 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 = this._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.complete(this._width, this._height);
|
||||
}
|
||||
};
|
||||
308
lib/filter.js
308
lib/filter.js
|
|
@ -1,308 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var interlaceUtils = require('./interlace');
|
||||
|
||||
function getByteWidth(width, bpp, depth) {
|
||||
var byteWidth = width * bpp;
|
||||
if (depth !== 8) {
|
||||
byteWidth = Math.ceil(byteWidth / (8 / depth));
|
||||
}
|
||||
return byteWidth;
|
||||
}
|
||||
|
||||
function paethPredictor(left, above, upLeft) {
|
||||
|
||||
var paeth = left + above - upLeft;
|
||||
var pLeft = Math.abs(paeth - left);
|
||||
var pAbove = Math.abs(paeth - above);
|
||||
var pUpLeft = Math.abs(paeth - 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) {
|
||||
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._Bpp = Bpp; //TODO rename
|
||||
this._depth = depth;
|
||||
this._options = options;
|
||||
|
||||
if (!('filterType' in options) || options.filterType === -1) {
|
||||
options.filterType = [0, 1, 2, 3, 4];
|
||||
}
|
||||
else if (typeof options.filterType === 'number') {
|
||||
options.filterType = [options.filterType];
|
||||
}
|
||||
|
||||
this._filters = {
|
||||
0: this._filterNone.bind(this),
|
||||
1: this._filterSub.bind(this),
|
||||
2: this._filterUp.bind(this),
|
||||
3: this._filterAvg.bind(this),
|
||||
4: this._filterPaeth.bind(this)
|
||||
};
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.start = function() {
|
||||
this.read(this._images[this._imageIndex].byteWidth + 1, this._reverseFilterLine.bind(this));
|
||||
};
|
||||
|
||||
Filter.prototype._reverseFilterLine = function(rawData) {
|
||||
|
||||
var currentImage = this._images[this._imageIndex];
|
||||
var line = new Buffer(currentImage.byteWidth);
|
||||
|
||||
var filter = rawData[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.
|
||||
var xComparison;
|
||||
if (this._depth === 8) {
|
||||
xComparison = this._Bpp;
|
||||
}
|
||||
else if (this._depth === 16) {
|
||||
xComparison = this._Bpp * 2;
|
||||
}
|
||||
else {
|
||||
xComparison = 1;
|
||||
}
|
||||
var xBiggerThan = xComparison - 1;
|
||||
|
||||
for (var x = 0; x < currentImage.byteWidth; x++) {
|
||||
var rawByte = rawData[1 + x];
|
||||
switch (filter) {
|
||||
case 0:
|
||||
line[x] = rawByte;
|
||||
break;
|
||||
case 1:
|
||||
var f1Left = x > xBiggerThan ? line[x - xComparison] : 0;
|
||||
line[x] = rawByte + f1Left;
|
||||
break;
|
||||
case 2:
|
||||
var f2Up = this._lastLine ? this._lastLine[x] : 0;
|
||||
line[x] = rawByte + f2Up;
|
||||
break;
|
||||
case 3:
|
||||
var f3Up = this._lastLine ? this._lastLine[x] : 0;
|
||||
var f3Left = x > xBiggerThan ? line[x - xComparison] : 0;
|
||||
var f3Add = Math.floor((f3Left + f3Up) / 2);
|
||||
line[x] = rawByte + f3Add;
|
||||
break;
|
||||
case 4:
|
||||
var f4Up = this._lastLine ? this._lastLine[x] : 0;
|
||||
var f4Left = x > xBiggerThan ? line[x - xComparison] : 0;
|
||||
var f4UpLeft = x > xBiggerThan && this._lastLine
|
||||
? this._lastLine[x - xComparison] : 0;
|
||||
var f4Add = paethPredictor(f4Left, f4Up, f4UpLeft);
|
||||
line[x] = rawByte + f4Add;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unrecognised filter type - ' + filter);
|
||||
}
|
||||
|
||||
//if (x === 5) {
|
||||
// console.log("R", line[3], "G", line[4], "B", line[5]);
|
||||
//}
|
||||
}
|
||||
|
||||
this.write(line);
|
||||
|
||||
currentImage.lineIndex++;
|
||||
if (currentImage.lineIndex >= currentImage.height) {
|
||||
this._lastLine = null;
|
||||
this._imageIndex++;
|
||||
currentImage = this._images[this._imageIndex];
|
||||
}
|
||||
else {
|
||||
this._lastLine = line;
|
||||
}
|
||||
|
||||
if (currentImage) {
|
||||
this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this));
|
||||
}
|
||||
else {
|
||||
this.complete(this._width, this._height);
|
||||
}
|
||||
};
|
||||
|
||||
//TODO pull out
|
||||
Filter.prototype.filter = function(pxData) {
|
||||
|
||||
var rawData = new Buffer(((this._width << 2) + 1) * this._height);
|
||||
|
||||
for (var y = 0; y < this._height; y++) {
|
||||
|
||||
// find best filter for this line (with lowest sum of values)
|
||||
var filterTypes = this._options.filterType;
|
||||
var min = Infinity;
|
||||
var sel = 0;
|
||||
|
||||
for (var i = 0; i < filterTypes.length; i++) {
|
||||
var sum = this._filters[filterTypes[i]](pxData, y, null);
|
||||
if (sum < min) {
|
||||
sel = filterTypes[i];
|
||||
min = sum;
|
||||
}
|
||||
}
|
||||
|
||||
this._filters[sel](pxData, y, rawData);
|
||||
}
|
||||
return rawData;
|
||||
};
|
||||
|
||||
Filter.prototype._filterNone = function(pxData, y, rawData) {
|
||||
|
||||
var pxRowLength = this._width << 2;
|
||||
var rawRowLength = pxRowLength + 1;
|
||||
var sum = 0;
|
||||
|
||||
if (!rawData) {
|
||||
for (var x = 0; x < pxRowLength; x++) {
|
||||
sum += Math.abs(pxData[y * pxRowLength + x]);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
rawData[y * rawRowLength] = 0;
|
||||
pxData.copy(rawData, rawRowLength * y + 1, pxRowLength * y, pxRowLength * (y + 1));
|
||||
}
|
||||
|
||||
return sum;
|
||||
};
|
||||
|
||||
Filter.prototype._filterSub = function(pxData, y, rawData) {
|
||||
|
||||
var pxRowLength = this._width << 2;
|
||||
var rawRowLength = pxRowLength + 1;
|
||||
var sum = 0;
|
||||
|
||||
if (rawData) {
|
||||
rawData[y * rawRowLength] = 1;
|
||||
}
|
||||
|
||||
for (var x = 0; x < pxRowLength; x++) {
|
||||
|
||||
var left = x >= 4 ? pxData[y * pxRowLength + x - 4] : 0;
|
||||
var val = pxData[y * pxRowLength + x] - left;
|
||||
|
||||
if (!rawData) {
|
||||
sum += Math.abs(val);
|
||||
}
|
||||
else {
|
||||
rawData[y * rawRowLength + 1 + x] = val;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
|
||||
Filter.prototype._filterUp = function(pxData, y, rawData) {
|
||||
|
||||
var pxRowLength = this._width << 2;
|
||||
var rawRowLength = pxRowLength + 1;
|
||||
var sum = 0;
|
||||
|
||||
if (rawData) {
|
||||
rawData[y * rawRowLength] = 2;
|
||||
}
|
||||
|
||||
for (var x = 0; x < pxRowLength; x++) {
|
||||
|
||||
var up = y > 0 ? pxData[(y - 1) * pxRowLength + x] : 0;
|
||||
var val = pxData[y * pxRowLength + x] - up;
|
||||
|
||||
if (!rawData) {
|
||||
sum += Math.abs(val);
|
||||
}
|
||||
else {
|
||||
rawData[y * rawRowLength + 1 + x] = val;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
|
||||
Filter.prototype._filterAvg = function(pxData, y, rawData) {
|
||||
|
||||
var pxRowLength = this._width << 2;
|
||||
var rawRowLength = pxRowLength + 1;
|
||||
var sum = 0;
|
||||
|
||||
if (rawData) {
|
||||
rawData[y * rawRowLength] = 3;
|
||||
}
|
||||
|
||||
for (var x = 0; x < pxRowLength; x++) {
|
||||
|
||||
var left = x >= 4 ? pxData[y * pxRowLength + x - 4] : 0;
|
||||
var up = y > 0 ? pxData[(y - 1) * pxRowLength + x] : 0;
|
||||
var val = pxData[y * pxRowLength + x] - ((left + up) >> 1);
|
||||
|
||||
if (!rawData) {
|
||||
sum += Math.abs(val);
|
||||
}
|
||||
else {
|
||||
rawData[y * rawRowLength + 1 + x] = val;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
|
||||
Filter.prototype._filterPaeth = function(pxData, y, rawData) {
|
||||
|
||||
var pxRowLength = this._width << 2;
|
||||
var rawRowLength = pxRowLength + 1;
|
||||
var sum = 0;
|
||||
|
||||
if (rawData) {
|
||||
rawData[y * rawRowLength] = 4;
|
||||
}
|
||||
|
||||
for (var x = 0; x < pxRowLength; x++) {
|
||||
|
||||
var left = x >= 4 ? pxData[y * pxRowLength + x - 4] : 0;
|
||||
var up = y > 0 ? pxData[(y - 1) * pxRowLength + x] : 0;
|
||||
var upLeft = x >= 4 && y > 0 ? pxData[(y - 1) * pxRowLength + x - 4] : 0;
|
||||
var val = pxData[y * pxRowLength + x] - paethPredictor(left, up, upLeft);
|
||||
|
||||
if (!rawData) {
|
||||
sum += Math.abs(val);
|
||||
}
|
||||
else {
|
||||
rawData[y * rawRowLength + 1 + x] = val;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
var util = require('util');
|
||||
var Stream = require('stream');
|
||||
var zlib = require('zlib');
|
||||
var Filter = require('./filter');
|
||||
var filter = require('./filter-pack');
|
||||
var CrcStream = require('./crc');
|
||||
var constants = require('./constants');
|
||||
|
||||
|
|
@ -34,9 +34,7 @@ Packer.prototype.pack = function(data, width, height, gamma) {
|
|||
}
|
||||
|
||||
// filter pixel data
|
||||
//TODO {}
|
||||
var filter = new Filter(width, height, 4, 8, false, this._options, {});
|
||||
var filteredData = filter.filter(data);
|
||||
var filteredData = filter(data, width, height, this._options);
|
||||
|
||||
// compress it
|
||||
var deflate = zlib.createDeflate({
|
||||
|
|
|
|||
17
lib/paeth-predictor.js
Normal file
17
lib/paeth-predictor.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function paethPredictor(left, above, upLeft) {
|
||||
|
||||
var paeth = left + above - upLeft;
|
||||
var pLeft = Math.abs(paeth - left);
|
||||
var pAbove = Math.abs(paeth - above);
|
||||
var pUpLeft = Math.abs(paeth - upLeft);
|
||||
|
||||
if (pLeft <= pAbove && pLeft <= pUpLeft) {
|
||||
return left;
|
||||
}
|
||||
if (pAbove <= pUpLeft) {
|
||||
return above;
|
||||
}
|
||||
return upLeft;
|
||||
};
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
var util = require('util');
|
||||
var zlib = require('zlib');
|
||||
var ChunkStream = require('./chunkstream');
|
||||
var FilterAsync = require('./filter-async');
|
||||
var FilterAsync = require('./filter-parse-async');
|
||||
var Parser = require('./parser');
|
||||
var bitmapper = require('./bitmapper');
|
||||
var formatNormaliser = require('./format-normaliser');
|
||||
|
|
@ -65,8 +65,7 @@ ParserAsync.prototype._handleBitmapInfo = function(width, height, bpp, depth, in
|
|||
width, height,
|
||||
bpp,
|
||||
depth,
|
||||
interlace,
|
||||
this._options
|
||||
interlace
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
var zlib = require('zlib');
|
||||
var SyncReader = require('./sync-reader');
|
||||
var FilterSync = require('./filter-sync');
|
||||
var FilterSync = require('./filter-parse-sync');
|
||||
var Parser = require('./parser');
|
||||
var bitmapper = require('./bitmapper');
|
||||
var formatNormaliser = require('./format-normaliser');
|
||||
|
|
@ -16,7 +16,7 @@ module.exports = function(buffer, options) {
|
|||
err = _err_;
|
||||
}
|
||||
|
||||
var data, bpp, width, height, depth, interlace;
|
||||
var bpp, width, height, depth, interlace;
|
||||
function handleBitmapInfo(_width_, _height_, _bpp_, _depth_, _interlace_) {
|
||||
|
||||
bpp = _bpp_;
|
||||
|
|
@ -24,8 +24,6 @@ module.exports = function(buffer, options) {
|
|||
height = _height_;
|
||||
depth = _depth_;
|
||||
interlace = _interlace_;
|
||||
|
||||
data = new Buffer(width * height * 4);
|
||||
}
|
||||
|
||||
var metaData;
|
||||
|
|
@ -78,8 +76,7 @@ module.exports = function(buffer, options) {
|
|||
height,
|
||||
bpp,
|
||||
depth,
|
||||
interlace,
|
||||
options
|
||||
interlace
|
||||
);
|
||||
inflateData = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ var PNG = exports.PNG = function(options) {
|
|||
this.gamma = 0;
|
||||
this.readable = this.writable = true;
|
||||
|
||||
this._parser = new Parser(options || {});
|
||||
this._parser = new Parser(options);
|
||||
|
||||
this._parser.on('error', this.emit.bind(this, 'error'));
|
||||
this._parser.on('close', this._handleClose.bind(this));
|
||||
|
|
|
|||
|
|
@ -35,7 +35,12 @@ test('outputs background, created from scratch', function (t) {
|
|||
var out = fs.readFileSync(__dirname + '/bg.png');
|
||||
var ref = fs.readFileSync(__dirname + '/bg-ref.png');
|
||||
|
||||
t.ok(bufferEqual(out, ref), "compares with working file ok");
|
||||
var isBufferEqual = bufferEqual(out, ref);
|
||||
t.ok(isBufferEqual, "compares with working file ok");
|
||||
|
||||
if (!isBufferEqual) {
|
||||
console.log(out.length, ref.length);
|
||||
}
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue