From 71a371d6cd99129a894fc6fd471d85f414e45106 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Wed, 5 Aug 2015 23:21:28 +0100 Subject: [PATCH] Pull out format normaliser. Fix #18 --- .eslintrc | 2 +- lib/format-normaliser.js | 89 ++++++++++++++++++++++++++++++++++++++++ lib/parser-async.js | 12 ++++-- lib/parser-sync.js | 19 +++++++-- lib/parser.js | 77 ---------------------------------- lib/png.js | 5 +++ test/convert-images.js | 20 +++++---- 7 files changed, 131 insertions(+), 93 deletions(-) create mode 100644 lib/format-normaliser.js diff --git a/.eslintrc b/.eslintrc index 3cb7332..2672cce 100644 --- a/.eslintrc +++ b/.eslintrc @@ -129,7 +129,7 @@ "generator-star-spacing": 0, "guard-for-in": 1, "handle-callback-err": 2, - "id-length": [2, {"min": 3, "max": 20, "exceptions":["x", "y", "i", "j", "ex", "up"]}], + "id-length": [2, {"min": 3, "max": 25, "exceptions":["x", "y", "i", "j", "ex", "up"]}], "indent": [1, 2, {"SwitchCase": 1}], "init-declarations": 0, "key-spacing": [1, { "beforeColon": false, "afterColon": true }], diff --git a/lib/format-normaliser.js b/lib/format-normaliser.js new file mode 100644 index 0000000..378373f --- /dev/null +++ b/lib/format-normaliser.js @@ -0,0 +1,89 @@ +'use strict'; + +function dePalette(indata, outdata, width, height, palette) { + var pxPos = 0; + // use values from palette + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var color = palette[indata[pxPos]]; + + if (!color) { + throw new Error('index ' + indata[pxPos] + ' not in palette'); + } + + for (var i = 0; i < 4; i++) { + outdata[pxPos + i] = color[i]; + } + pxPos += 4; + } + } +} + +function replaceTransparentColor(indata, outdata, width, height, transColor) { + var pxPos = 0; + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var makeTrans = false; + + if (transColor.length === 1) { + if (transColor[0] === indata[pxPos]) { + makeTrans = true; + } + } + else if (transColor[0] === indata[pxPos] && transColor[1] === indata[pxPos + 1] && transColor[2] === indata[pxPos + 2]) { + makeTrans = true; + } + if (makeTrans) { + for (var i = 0; i < 4; i++) { + outdata[pxPos + i] = 0; + } + } + pxPos += 4; + } + } +} + +function scaleDepth(indata, outdata, width, height, depth) { + var maxOutSample = 255; + var maxInSample = Math.pow(2, depth) - 1; + var pxPos = 0; + + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + for (var i = 0; i < 4; i++) { + outdata[pxPos + i] = Math.floor((indata[pxPos + i] * maxOutSample) / maxInSample + 0.5); + } + pxPos += 4; + } + } +} + +module.exports = function(indata, imageData) { + + var depth = imageData.depth; + var width = imageData.width; + var height = imageData.height; + var colorType = imageData.colorType; + var transColor = imageData.transColor; + var palette = imageData.palette; + + var outdata = indata; // only different for 16 bits + + if (colorType === 3) { // paletted + dePalette(indata, outdata, width, height, palette); + } + else { + if (transColor) { + replaceTransparentColor(indata, outdata, width, height, transColor); + } + // if it needs scaling + if (depth !== 8) { + // if we need to change the buffer size + if (depth === 16) { + outdata = new Buffer(width * height * 4); + } + scaleDepth(indata, outdata, width, height, depth); + } + } + return outdata; +}; diff --git a/lib/parser-async.js b/lib/parser-async.js index c8e2354..abdceda 100644 --- a/lib/parser-async.js +++ b/lib/parser-async.js @@ -1,13 +1,12 @@ 'use strict'; - var util = require('util'); var zlib = require('zlib'); var ChunkStream = require('./chunkstream'); var FilterAsync = require('./filter-async'); var Parser = require('./parser'); var bitmapper = require('./bitmapper'); - +var formatNormaliser = require('./format-normaliser'); var ParserAsync = module.exports = function(options) { ChunkStream.call(this); @@ -98,7 +97,14 @@ ParserAsync.prototype._complete = function(filteredData, width, height) { this._depth, this._interlace); - bitmapData = this._parser.reverseFiltered(bitmapData, this._depth, width, height); + // todo not bitmap data any more + bitmapData = formatNormaliser(bitmapData, { + depth: this._depth, // TODO always store in this format + width: width, + height: height, + colorType: this._parser._colorType, + palette: this._parser._palette, + transColor: this._parser._transColor }); } catch (ex) { this._handleError(ex); diff --git a/lib/parser-sync.js b/lib/parser-sync.js index 54a0c65..6b2bd9c 100644 --- a/lib/parser-sync.js +++ b/lib/parser-sync.js @@ -6,6 +6,7 @@ var SyncReader = require('./sync-reader'); var FilterSync = require('./filter-sync'); var Parser = require('./parser'); var bitmapper = require('./bitmapper'); +var formatNormaliser = require('./format-normaliser'); var ParserSync = module.exports = function(buffer, options) { @@ -33,6 +34,10 @@ var ParserSync = module.exports = function(buffer, options) { var data = zlib.inflateSync(inflateData); + if (!data || !data.length) { + throw new Error('bad png - invalid inflate data response'); + } + data = FilterSync.process( data, this._width, this._height, @@ -41,13 +46,19 @@ var ParserSync = module.exports = function(buffer, options) { this._interlace, this._options ); - +// todo do not store _data this._data = bitmapper.dataToBitMap(data, this._width, this._height, this._bpp, this._depth, this._interlace); - // todo yuck - this.data = this._parser.reverseFiltered(this._data, this._depth, this._width, this._height); + + this.data = formatNormaliser(this._data, { + depth: this._depth, // TODO always store in this format + width: this._width, + height: this._height, + colorType: this._parser._colorType, + palette: this._parser._palette, + transColor: this._parser._transColor }); }; ParserSync.prototype._handleError = function(err) { @@ -73,5 +84,5 @@ ParserSync.prototype._createData = function(width, height, bpp, depth, interlace this._height = height; this._depth = depth; this._interlace = interlace; - return this._data; + return this._data; // todo do not return }; diff --git a/lib/parser.js b/lib/parser.js index 288cf92..b2730f5 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -276,7 +276,6 @@ Parser.prototype._parseIDAT = function(length, data) { } }; - Parser.prototype._handleIEND = function(length) { this.read(length, this._parseIEND.bind(this)); }; @@ -289,79 +288,3 @@ Parser.prototype._parseIEND = function(data) { this.finished(); }; - -Parser.prototype.reverseFiltered = function(indata, depth, width, height) { - - var outdata = indata; // only different for 16 bits - - var pxLineLength = width << 2; - var x, y, i, pxPos, pxRowPos; - - if (this._colorType === 3) { // paletted - //TODO abstract loop? - // use values from palette - for (y = 0; y < height; y++) { - pxRowPos = y * pxLineLength; - - for (x = 0; x < width; x++) { - pxPos = pxRowPos + (x << 2); - var color = this._palette[indata[pxPos]]; - - if (!color) { - throw new Error('index ' + indata[pxPos] + ' not in palette'); - } - - for (i = 0; i < 4; i++) { - indata[pxPos + i] = color[i]; - } - } - } - } - else { - if (this._transColor) { - for (y = 0; y < height; y++) { - pxRowPos = y * pxLineLength; - - for (x = 0; x < width; x++) { - pxPos = pxRowPos + (x << 2); - var makeTrans = false; - //console.log(pxPos); - if (this._transColor.length === 1) { - if (this._transColor[0] === indata[pxPos]) { - makeTrans = true; - } - } - else if (this._transColor[0] === indata[pxPos] && this._transColor[1] === indata[pxPos + 1] && this._transColor[2] === indata[pxPos + 2]) { - makeTrans = true; - } - if (makeTrans) { - for (i = 0; i < 4; i++) { - indata[pxPos + i] = 0; - } - } - } - } - } - if (depth !== 8) { - if (depth === 16) { - outdata = new Buffer(width * height * 4); - } - //console.log("adjusting"); - var maxOutSample = 255; - var maxInSample = Math.pow(2, depth) - 1; - - for (y = 0; y < height; y++) { - pxRowPos = y * pxLineLength; - - for (x = 0; x < width; x++) { - pxPos = pxRowPos + (x << 2); - for (i = 0; i < 4; i++) { - outdata[pxPos + i] = Math.floor((indata[pxPos + i] * maxOutSample) / maxInSample + 0.5); - } - } - } - } - } - return outdata; -}; - diff --git a/lib/png.js b/lib/png.js index 6e3b0c0..65cd6a0 100755 --- a/lib/png.js +++ b/lib/png.js @@ -49,6 +49,11 @@ PNG.sync = PNGSync; PNG.prototype.pack = function() { + if (!this.data || !this.data.length) { + this.emit('error', 'No data provided'); + return this; + } + process.nextTick(function() { this._packer.pack(this.data, this.width, this.height); }.bind(this)); diff --git a/test/convert-images.js b/test/convert-images.js index 0ec147a..f2b23be 100644 --- a/test/convert-images.js +++ b/test/convert-images.js @@ -15,9 +15,15 @@ module.exports = function(done) { var completed = 0; var expected = files.length * 2; - function complete() { + var anyFailures = false; + function complete(isSuccessful) { completed++; + anyFailures = anyFailures || !isSuccessful; if (expected === completed) { + if (anyFailures) { + process.exit(1); + return; + } done(); } } @@ -41,13 +47,13 @@ module.exports = function(done) { console.log(e.stack); } syncError = true; - complete(); + complete(expectedError); } if (!syncError) { if (expectedError) { console.log("Sync: Error expected, parsed fine ..", file); - complete(); + complete(false); } else { var outpng = new PNG(); @@ -58,7 +64,7 @@ module.exports = function(done) { outpng.pack() .pipe(fs.createWriteStream(__dirname + '/outsync/' + file) .on("finish", function () { - complete(); + complete(true); })); } } @@ -69,7 +75,7 @@ module.exports = function(done) { if (!expectedError) { console.log("Async: Unexpected error parsing.." + file, err); } - complete(); + complete(expectedError); }) .on('parsed', function () { @@ -82,11 +88,9 @@ module.exports = function(done) { .pipe( fs.createWriteStream(__dirname + '/out/' + file) .on("finish", function() { - complete(); + complete(true); })); - }); - }); }); }