diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d880ab..d729221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # image-diff changelog +1.5.0 - Added `image-diff` executable + +1.4.0 - Moved from `crop` to `extent`. Fixes #32 indirectly + 1.3.0 - Fixed support for fractional differences via @jacobp100 in #29 1.2.0 - Added support for showing shadow image in diff image via @064678147 in #25 diff --git a/README.md b/README.md index c217a31..88ea89a 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,37 @@ Create an differential image between multiple images - For example, if an image is `+` and we diff with `-`, then the image will have `|` be red but also contain a faded `-` - By default, this options is `false` meaning a shadow will not be drawn +### CLI usage +We offer an `image-diff` executable to diff from the CLI. When images match, its exit code will be `0`. When they don't match, then it will be non-zero (e.g. `1`). + +``` +$ image-diff --help + + Usage: image-diff [options] [diff-image] + + Options: + + -h, --help output usage information + -V, --version output the version number + --shadow Draw a shadow of unchanges parts on diff image + +``` + +Example usage: + +```bash +# Images don't match +image-diff checkerboard.png white.png diff.png +echo $? +# 1 +# We can look at `diff.png` for the diff result + +# Images do match +image-diff checkerboard.png white.png +echo $? +# 0 +``` + ## Contributing In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint via [grunt](https://github.com/gruntjs/grunt) and test via `npm test`. diff --git a/bin/image-diff b/bin/image-diff new file mode 100755 index 0000000..5f2a5a9 --- /dev/null +++ b/bin/image-diff @@ -0,0 +1,39 @@ +#!/usr/bin/env node +// Load in our dependencies +var program = require('commander'); +var pkg = require('../package.json'); +var imageDiff = require('../'); + +// Configure our CLI +program.name = pkg.name; +program + .version(pkg.version) + .usage('[options] [diff-image]') + .option('--shadow', 'Draw a shadow of unchanges parts on diff image') + .action(function handleRun (actualImage, expectedImage, diffImage, program) { + // If there is no program, then assume diffImage was left out + if (program === undefined) { + program = diffImage; + diffImage = undefined; + } + + // Run our diff + imageDiff({ + actualImage: actualImage, + diffImage: diffImage, + expectedImage: expectedImage, + shadow: program.shadow + }, function handleImageDiff (err, imagesAreSame) { + // If there was an error, throw it + if (err) { + throw err; + } + + // Otherwise, exit based on the result + var exitCode = imagesAreSame ? 0 : 1; + process.exit(exitCode); + }); + }); + +// Parse our CLI arguments +program.parse(process.argv); diff --git a/lib/image-diff.js b/lib/image-diff.js index 14b4786..1c56aad 100644 --- a/lib/image-diff.js +++ b/lib/image-diff.js @@ -1,4 +1,5 @@ // Load in our dependencies +var assert = require('assert'); var fs = require('fs'); var path = require('path'); var async = require('async'); @@ -8,25 +9,25 @@ var mkdirp = require('mkdirp'); var tmp = require('tmp'); // Define custom resize function -// Taken from https://github.com/twolfson/twolfson.com/blob/3.4.0/test/perceptual-tests/twolfson.com_test.js#L88-L107 -// TODO: Make image resizing its own library -// DEV: This does not pollute gm.prototype -gm.prototype.fillFromTo = function (params) { +function transparentExtent(gm, params) { + // Assert we received our parameters + assert.notEqual(params.width, undefined); + assert.notEqual(params.height, undefined); + // Fill in new space with white background // TODO: Parameterize background color (should be considered 'transparent' color in this case) - this.borderColor('transparent'); - this.border(Math.max(params.toWidth - params.fromWidth, 0), Math.max(params.toHeight - params.fromHeight, 0)); + gm.background('transparent'); // Anchor image to upper-left // TODO: Parameterize anchor point - this.gravity('SouthEast'); + gm.gravity('NorthWest'); // Specify new image size - this.crop(params.toWidth, params.toHeight, 0, 0); + gm.extent(params.width, params.height); - // Return this instance - return this; -}; + // Return gm instance for a fluent interface + return gm; +} function ImageDiff() { } @@ -150,11 +151,9 @@ ImageDiff.prototype = { // Otherwise, resize the image actualTmpPath = filepath; - gm(actualPath).fillFromTo({ - fromWidth: actualSize.width, - fromHeight: actualSize.height, - toWidth: maxWidth, - toHeight: maxHeight + transparentExtent(gm(actualPath), { + width: maxWidth, + height: maxHeight }).write(actualTmpPath, cb); }); }, @@ -171,11 +170,9 @@ ImageDiff.prototype = { gm(maxWidth, maxHeight, 'transparent').write(expectedTmpPath, cb); // Otherwise, resize the image } else { - gm(expectedPath).fillFromTo({ - fromWidth: expectedSize.width, - fromHeight: expectedSize.height, - toWidth: maxWidth, - toHeight: maxHeight + transparentExtent(gm(expectedPath), { + width: maxWidth, + height: maxHeight }).write(expectedTmpPath, cb); } }); diff --git a/package.json b/package.json index d7baf60..499e2fb 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,9 @@ "url": "https://github.com/uber/image-diff/blob/master/LICENSE-MIT" } ], + "bin": { + "image-diff": "bin/image-diff" + }, "main": "lib/image-diff", "engines": { "node": ">= 0.8.0" @@ -31,6 +34,7 @@ "dependencies": { "async": "~0.2.9", "buffered-spawn": "~1.1.1", + "commander": "~2.9.0", "gm": "~1.13.3", "mkdirp": "~0.3.5", "tmp": "0.0.23" diff --git a/test/image-diff_test.js b/test/image-diff_test.js index 65a774e..d4e54ca 100644 --- a/test/image-diff_test.js +++ b/test/image-diff_test.js @@ -117,6 +117,18 @@ describe('image-diff', function () { }); }); + describe('diffing the same image where 1 has a transparent background', function () { + runImageDiff({ + actualImage: __dirname + '/test-files/checkerboard-transparent.png', + expectedImage: __dirname + '/test-files/checkerboard.png', + diffImage: __dirname + '/actual-files/different-transparent.png' + }); + + it('asserts images are the different', function () { + assert.strictEqual(this.imagesAreSame, false); + }); + }); + describe('diffing images with shadow on', function() { runImageDiff({ actualImage: __dirname + '/test-files/checkerboard-excess.png', diff --git a/test/test-files/checkerboard-transparent.png b/test/test-files/checkerboard-transparent.png new file mode 100644 index 0000000..3c13854 Binary files /dev/null and b/test/test-files/checkerboard-transparent.png differ