UNPKG

png-diff

Version:

Small PNG diff utility, written in pure JS for Node.

172 lines (145 loc) 5.08 kB
'use strict'; var PNG = require('pngjs').PNG; var Stream = require('stream'); var fs = require('fs'); var streamifier = require('streamifier'); var util = require('util'); function _getDimsMismatchErrMsg(dims1, dims2) { return util.format( 'Images not the same dimension. First: %sx%s. Second: %sx%s.', dims1[0], dims1[1], dims2[0], dims2[1] ); } function _turnPathOrStreamOrBufIntoStream(streamOrBufOrPath, done) { if (typeof streamOrBufOrPath === 'string') { streamOrBufOrPath = fs.createReadStream(streamOrBufOrPath).once('error', done); } if (streamOrBufOrPath instanceof Buffer) { streamOrBufOrPath = streamifier.createReadStream(streamOrBufOrPath).once('error', done); } if (!(streamOrBufOrPath instanceof Stream)) { return done(new Error('Argument needs to be a valid read path, stream or buffer.')); } done(null, streamOrBufOrPath); } function _turnPathsOrStreamsOrBufsIntoStreams(streamOrBufOrPath1, streamOrBufOrPath2, done) { _turnPathOrStreamOrBufIntoStream(streamOrBufOrPath1, function(err, res1) { if (err) return done(err); _turnPathOrStreamOrBufIntoStream(streamOrBufOrPath2, function(err, res2) { if (err) return done(err); done(null, res1, res2); }); }); } /** * option * outputIdenticalPixelsAsTransparent: false - turns off/on the highlighting feature */ function outputDiffStream( streamOrBufOrPath1, streamOrBufOrPath2, outputIdenticalPixelsAsTransparent, done ) { // first format, without option if (typeof outputIdenticalPixelsAsTransparent == 'function') { done = outputIdenticalPixelsAsTransparent; outputIdenticalPixelsAsTransparent = false; } _turnPathsOrStreamsOrBufsIntoStreams( streamOrBufOrPath1, streamOrBufOrPath2, function(err, stream1, stream2) { if (err) return done(err); // diff metric is either 0 or 1 for now. Might support outputting some diff // value in the future var diffMetric = 0; var writeStream = new PNG(); stream1.pipe(writeStream).once('error', done).on( 'parsed', function() { var data1 = this.data; var dims1 = [this.width, this.height]; if (outputIdenticalPixelsAsTransparent) { writeStream = new PNG({width: this.width, height: this.height}); } stream2.pipe(new PNG()).once('error', done).on( 'parsed', function() { var data2 = this.data; var dims2 = [this.width, this.height]; if (data1.length !== data2.length) { return done(new Error(_getDimsMismatchErrMsg(dims1, dims2))); } var i = 0; var data = writeStream.data; // chunk of 4 values: r g b a while (data1[i] != null) { // var r, g, b, a; if ( data1[i] !== data2[i] || data1[i + 1] !== data2[i + 1] || data1[i + 2] !== data2[i + 2] || data1[i + 3] !== data2[i + 3] ) { diffMetric = 1; // draw only differences on the output image if (outputIdenticalPixelsAsTransparent) { data[i] = data2[i]; data[i + 1] = data2[i + 1]; data[i + 2] = data2[i + 2]; data[i + 3] = data2[i + 3]; } else { // turn the diff pixels redder. No change to alpha var addRed = 60; if (data2[i] + addRed <= 255) { data[i] = data2[i] + addRed; data[i + 1] = Math.max(data2[i + 1] - addRed / 3, 0); data[i + 2] = Math.max(data2[i + 2] - addRed / 3, 0); } else { // too bright; subtract G and B instead data[i] = data2[i]; data[i + 1] = Math.max(data2[i + 1] - addRed, 0); data[i + 2] = Math.max(data2[i + 2] - addRed, 0); } } } i += 4; } return done(null, writeStream.pack(), diffMetric); } ); } ); } ); } function outputDiff( streamOrBufOrPath1, streamOrBufOrPath2, destPath, outputIdenticalPixelsAsTransparent, done ) { // first format, without option if (typeof outputIdenticalPixelsAsTransparent == 'function') { done = outputIdenticalPixelsAsTransparent; outputIdenticalPixelsAsTransparent = false; } outputDiffStream( streamOrBufOrPath1, streamOrBufOrPath2, outputIdenticalPixelsAsTransparent, function(err, res, diffMetric) { if (err) return done(err); res.pipe(fs.createWriteStream(destPath)).once('error', done).on( 'close', done.bind(null, null, diffMetric) ); } ); } module.exports = {outputDiff: outputDiff, outputDiffStream: outputDiffStream};