diff --git a/example/LargeImage.png b/example/LargeImage.png new file mode 100644 index 0000000..e02a737 Binary files /dev/null and b/example/LargeImage.png differ diff --git a/example/SmallImage.png b/example/SmallImage.png new file mode 100644 index 0000000..5aed967 Binary files /dev/null and b/example/SmallImage.png differ diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..9c2a3fe --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,37 @@ +var gulp = require('gulp'); +var jasmine = require('gulp-jasmine'); +var gutil = require('gulp-util'); + +var srcFiles = 'resemble.js'; +var specFiles = 'resemble.spec.js'; + +function runTests(breakOnError) { + return gulp.src(specFiles) + .pipe(jasmine({ + includeStackTrace: true + })) + .on('error', errorHandler(breakOnError)); +} + +function errorHandler(breakOnError) { + return function(error) { + if (breakOnError) { + throw error; + } else { + gutil.log(gutil.colors.red('[Jasmine]'), error.toString()); + this.emit('end'); + } + } +} + +gulp.task('test', function() { + return runTests(true); +}); + +gulp.task('test:auto', function() { + runTests(false); + + return gulp.watch([srcFiles, specFiles], function() { + runTests(false); + }); +}); diff --git a/package.json b/package.json index a3563dc..3642b06 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "version": "0.0.4", "description": "Image analysis and comparison for nodejs", "main": "resemble.js", + "scripts": { + "test": "gulp test" + }, "repository": { "type": "git", "url": "https://github.com/lksv/node-resemble.js.git" @@ -21,5 +24,10 @@ "homepage": "https://github.com/lksv/node-resemble.js", "dependencies": { "pngjs2": "~1.0.0" + }, + "devDependencies": { + "gulp": "3.9.0", + "gulp-jasmine": "2.2.1", + "gulp-util": "3.0.7" } } diff --git a/resemble.js b/resemble.js index dee5d5e..0501c73 100644 --- a/resemble.js +++ b/resemble.js @@ -40,6 +40,7 @@ var _this = {}; }; var errorPixelTransformer = errorPixelTransform.flat; + var largeImageThreshold = 1200; _this['resemble'] = function( fileData ){ @@ -339,7 +340,7 @@ var _this = {}; var currentRectangle = null; var rectagnlesIdx = 0; - if( (width > 1200 || height > 1200) && ignoreAntialiasing){ + if(!!largeImageThreshold && ignoreAntialiasing && (width > largeImageThreshold || height > largeImageThreshold)){ skip = 6; } @@ -562,6 +563,10 @@ var _this = {}; pixelTransparency = options.transparency || pixelTransparency; + if (options.largeImageThreshold !== undefined) { + largeImageThreshold = options.largeImageThreshold; + } + return this; }; diff --git a/resemble.spec.js b/resemble.spec.js new file mode 100644 index 0000000..6c75947 --- /dev/null +++ b/resemble.spec.js @@ -0,0 +1,182 @@ +'use strict'; + +describe('node-resemble.js', function() { + var EXAMPLE_LARGE_IMAGE = 'example/LargeImage.png'; + var EXAMPLE_SMALL_IMAGE = 'example/SmallImage.png'; + var OPTIMISATION_SKIP_STEP = 6; + var DEFAULT_LARGE_IMAGE_THRESHOLD = 1200; + + var resemble = require('./resemble.js'); + + describe('largeImageThreshold', function() { + describe('when unset', function() { + describe('when ignoreAntialiasing is enabled', function() { + it('skips pixels when a dimension is larger than the default threshold (1200)', function(done) { + getLargeImageComparison().ignoreAntialiasing().onComplete(function(data) { + expectPixelsToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + + it('does not skip pixels when both dimensions are smaller than the default threshold (1200)', function(done) { + getSmallImageComparison().ignoreAntialiasing().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + }); + + describe('when ignoreAntialiasing is disabled', function() { + it('does not skip pixels when a dimension is larger than the default threshold (1200)', function(done) { + getLargeImageComparison().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + + it('does not skip pixels when both dimensions are smaller than the default threshold (1200)', function(done) { + getSmallImageComparison().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + }); + }); + + describe('when explicitly set', function() { + afterAll(function() { + resemble.outputSettings({largeImageThreshold: DEFAULT_LARGE_IMAGE_THRESHOLD}); + }); + + describe('when ignoreAntialiasing is enabled', function() { + it('skips pixels on images with a dimension larger than the given threshold', function(done) { + resemble.outputSettings({largeImageThreshold: 999}); + getSmallImageComparison().ignoreAntialiasing().onComplete(function(data) { + expectPixelsToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + + it('does not skip pixels on images with a dimension equal to the given threshold', function(done) { + resemble.outputSettings({largeImageThreshold: 1000}); + getSmallImageComparison().ignoreAntialiasing().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + + it('does not skip pixels on images with both dimensions smaller than the given threshold', function(done) { + resemble.outputSettings({largeImageThreshold: 1001}); + getSmallImageComparison().ignoreAntialiasing().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + }); + + describe('when ignoreAntialiasing is disabled', function() { + it('does not skip pixels on images with a dimension larger than the given threshold', function(done) { + resemble.outputSettings({largeImageThreshold: 999}); + getSmallImageComparison().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + + it('does not skip pixels on images with a dimension equal to the given threshold', function(done) { + resemble.outputSettings({largeImageThreshold: 1000}); + getSmallImageComparison().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + + it('does not skip pixels on images with both dimensions smaller than the given threshold', function(done) { + resemble.outputSettings({largeImageThreshold: 1001}); + getSmallImageComparison().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + }); + }); + + describe('when set to a falsy value', function() { + beforeEach(function() { + resemble.outputSettings({largeImageThreshold: 0}); + }); + + afterAll(function() { + resemble.outputSettings({largeImageThreshold: DEFAULT_LARGE_IMAGE_THRESHOLD}); + }); + + describe('when ignoreAntialiasing is enabled', function() { + it('does not skip pixels on images with a dimension larger than the default threshold (1200)', function(done) { + getLargeImageComparison().ignoreAntialiasing().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + + it('does not skip pixels on images with a dimension smaller than the default threshold (1200)', function(done) { + getSmallImageComparison().ignoreAntialiasing().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + }); + + describe('when ignoreAntialiasing is disabled', function() { + it('does not skip pixels on images with a dimension larger than the default threshold (1200)', function(done) { + getLargeImageComparison().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + + it('does not skip pixels on images with a dimension smaller than the default threshold (1200)', function(done) { + getSmallImageComparison().onComplete(function(data) { + expectPixelsNotToBeSkipped(data.getDiffImage(), OPTIMISATION_SKIP_STEP); + done(); + }); + }); + }); + }); + + function expectPixelsToBeSkipped(image, step) { + expect(getPixelForLocation(image, 1, step - 1).alpha).not.toBe(0); + expect(getPixelForLocation(image, 1, step).alpha).toBe(0); + expect(getPixelForLocation(image, 1, step + 1).alpha).not.toBe(0); + + expect(getPixelForLocation(image, step - 1, 1).alpha).not.toBe(0); + expect(getPixelForLocation(image, step, 1).alpha).toBe(0); + expect(getPixelForLocation(image, step + 1, 1).alpha).not.toBe(0); + + expect(getPixelForLocation(image, step, step).alpha).toBe(0); + } + + function expectPixelsNotToBeSkipped(image, step) { + expect(getPixelForLocation(image, 1, step).alpha).not.toBe(0); + expect(getPixelForLocation(image, step, 1).alpha).not.toBe(0); + expect(getPixelForLocation(image, step, step).alpha).not.toBe(0); + } + }); + + function getLargeImageComparison() { + return resemble(EXAMPLE_LARGE_IMAGE).compareTo(EXAMPLE_LARGE_IMAGE); + } + + function getSmallImageComparison() { + return resemble(EXAMPLE_SMALL_IMAGE).compareTo(EXAMPLE_SMALL_IMAGE); + } + + function getPixelForLocation(image, x, y) { + var index = (image.width * y + x) << 2; + return { + red: image.data[index], + green: image.data[index + 1], + blue: image.data[index + 2], + alpha: image.data[index + 3] + }; + } +});