compare.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // compare
  2. var exec = require('child_process').exec;
  3. var utils = require('./utils');
  4. /**
  5. * Compare two images uses graphicsmagicks `compare` command.
  6. *
  7. * gm.compare(img1, img2, 0.4, function (err, equal, equality) {
  8. * if (err) return handle(err);
  9. * console.log('The images are equal: %s', equal);
  10. * console.log('There equality was %d', equality);
  11. * });
  12. *
  13. * @param {String} orig Path to an image.
  14. * @param {String} compareTo Path to another image to compare to `orig`.
  15. * @param {Number|Object} [options] Options object or the amount of difference to tolerate before failing - defaults to 0.4
  16. * @param {Function} cb(err, Boolean, equality, rawOutput)
  17. */
  18. module.exports = exports = function (proto) {
  19. function compare(orig, compareTo, options, cb) {
  20. orig = utils.escape(orig);
  21. compareTo = utils.escape(compareTo);
  22. var isImageMagick = this._options && this._options.imageMagick;
  23. // compare binary for IM is `compare`, for GM it's `gm compare`
  24. var bin = isImageMagick ? '' : 'gm ';
  25. var execCmd = bin + 'compare -metric mse ' + orig + ' ' + compareTo;
  26. var tolerance = 0.4
  27. // outputting the diff image
  28. if (typeof options === 'object') {
  29. if (options.file) {
  30. if (typeof options.file !== 'string') {
  31. throw new TypeError('The path for the diff output is invalid');
  32. }
  33. // graphicsmagick defaults to red
  34. var highlightColorOption = options.highlightColor
  35. ? ' -highlight-color ' + options.highlightColor + ' '
  36. : ' ';
  37. var highlightStyleOption = options.highlightStyle
  38. ? ' -highlight-style ' + options.highlightStyle + ' '
  39. : ' ';
  40. var diffFilename = utils.escape(options.file);
  41. // For IM, filename is the last argument. For GM it's `-file <filename>`
  42. var diffOpt = isImageMagick ? diffFilename : ('-file ' + diffFilename);
  43. execCmd += highlightColorOption + highlightStyleOption + ' ' + diffOpt;
  44. }
  45. if (options.tolerance) {
  46. if (typeof options.tolerance !== 'number') {
  47. throw new TypeError('The tolerance value should be a number');
  48. }
  49. tolerance = options.tolerance;
  50. }
  51. } else {
  52. // For ImageMagick diff file is required but we don't care about it, so null it out
  53. isImageMagick && (execCmd += ' null:');
  54. if (typeof options == 'function') {
  55. cb = options; // tolerance value not provided, flip the cb place
  56. } else {
  57. tolerance = options
  58. }
  59. }
  60. exec(execCmd, function (err, stdout, stderr) {
  61. // ImageMagick returns err code 2 if err, 0 if similar, 1 if dissimilar
  62. if (isImageMagick) {
  63. if (!err) {
  64. return cb(null, 0 <= tolerance, 0, stdout);
  65. }
  66. if (err.code === 1) {
  67. err = null;
  68. stdout = stderr;
  69. }
  70. }
  71. if (err) {
  72. return cb(err);
  73. }
  74. // Since ImageMagick similar gives err code 0 and no stdout, there's really no matching
  75. // Otherwise, output format for IM is `12.00 (0.123)` and for GM it's `Total: 0.123`
  76. var regex = isImageMagick ? /\((\d+\.?[\d\-\+e]*)\)/m : /Total: (\d+\.?\d*)/m;
  77. var match = regex.exec(stdout);
  78. if (!match) {
  79. err = new Error('Unable to parse output.\nGot ' + stdout);
  80. return cb(err);
  81. }
  82. var equality = parseFloat(match[1]);
  83. cb(null, equality <= tolerance, equality, stdout);
  84. });
  85. }
  86. if (proto) {
  87. proto.compare = compare;
  88. }
  89. return compare;
  90. };