mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Reduce production (sub)depedency count from 93 to 50. Modernise dev tooling, e.g. use nyc, replace jshint with semistandard. Make 'npm test' command consistent across platforms.
177 lines
8.2 KiB
JavaScript
177 lines
8.2 KiB
JavaScript
'use strict';
|
|
|
|
const path = require('path');
|
|
const sharp = require('../../index');
|
|
const maxColourDistance = require('../../build/Release/sharp')._maxColourDistance;
|
|
|
|
// Helpers
|
|
const getPath = function (filename) {
|
|
return path.join(__dirname, filename);
|
|
};
|
|
|
|
// Generates a 64-bit-as-binary-string image fingerprint
|
|
// Based on the dHash gradient method - see http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
|
|
const fingerprint = function (image, callback) {
|
|
sharp(image)
|
|
.greyscale()
|
|
.normalise()
|
|
.resize(9, 8)
|
|
.ignoreAspectRatio()
|
|
.raw()
|
|
.toBuffer(function (err, data) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
let fingerprint = '';
|
|
for (let col = 0; col < 8; col++) {
|
|
for (let row = 0; row < 8; row++) {
|
|
const left = data[row * 8 + col];
|
|
const right = data[row * 8 + col + 1];
|
|
fingerprint = fingerprint + (left < right ? '1' : '0');
|
|
}
|
|
}
|
|
callback(null, fingerprint);
|
|
}
|
|
});
|
|
};
|
|
|
|
module.exports = {
|
|
|
|
inputJpgWithLandscapeExif1: getPath('Landscape_1.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithLandscapeExif2: getPath('Landscape_2.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithLandscapeExif3: getPath('Landscape_3.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithLandscapeExif4: getPath('Landscape_4.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithLandscapeExif5: getPath('Landscape_5.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithLandscapeExif6: getPath('Landscape_6.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithLandscapeExif7: getPath('Landscape_7.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithLandscapeExif8: getPath('Landscape_8.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
|
|
inputJpgWithPortraitExif1: getPath('Portrait_1.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithPortraitExif2: getPath('Portrait_2.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithPortraitExif3: getPath('Portrait_3.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithPortraitExif4: getPath('Portrait_4.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithPortraitExif5: getPath('Portrait_5.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithPortraitExif6: getPath('Portrait_6.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithPortraitExif7: getPath('Portrait_7.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
inputJpgWithPortraitExif8: getPath('Portrait_8.jpg'), // https://github.com/recurser/exif-orientation-examples
|
|
|
|
inputJpg: getPath('2569067123_aca715a2ee_o.jpg'), // http://www.flickr.com/photos/grizdave/2569067123/
|
|
inputJpgWithExif: getPath('Landscape_8.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_8.jpg
|
|
inputJpgWithExifMirroring: getPath('Landscape_5.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_5.jpg
|
|
inputJpgWithGammaHoliness: getPath('gamma_dalai_lama_gray.jpg'), // http://www.4p8.com/eric.brasseur/gamma.html
|
|
inputJpgWithCmykProfile: getPath('Channel_digital_image_CMYK_color.jpg'), // http://en.wikipedia.org/wiki/File:Channel_digital_image_CMYK_color.jpg
|
|
inputJpgWithCmykNoProfile: getPath('Channel_digital_image_CMYK_color_no_profile.jpg'),
|
|
inputJpgWithCorruptHeader: getPath('corrupt-header.jpg'),
|
|
inputJpgWithLowContrast: getPath('low-contrast.jpg'), // http://www.flickr.com/photos/grizdave/2569067123/
|
|
inputJpgLarge: getPath('giant-image.jpg'),
|
|
|
|
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
|
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
|
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
|
|
inputPngWithOneColor: getPath('2x2_fdcce6.png'),
|
|
inputPngWithTransparency16bit: getPath('tbgn2c16.png'), // http://www.schaik.com/pngsuite/tbgn2c16.png
|
|
inputPngOverlayLayer0: getPath('alpha-layer-0-background.png'),
|
|
inputPngOverlayLayer1: getPath('alpha-layer-1-fill.png'),
|
|
inputPngOverlayLayer2: getPath('alpha-layer-2-ink.png'),
|
|
inputPngOverlayLayer1LowAlpha: getPath('alpha-layer-1-fill-low-alpha.png'),
|
|
inputPngOverlayLayer2LowAlpha: getPath('alpha-layer-2-ink-low-alpha.png'),
|
|
inputPngAlphaPremultiplicationSmall: getPath('alpha-premultiply-1024x768-paper.png'),
|
|
inputPngAlphaPremultiplicationLarge: getPath('alpha-premultiply-2048x1536-paper.png'),
|
|
inputPngBooleanNoAlpha: getPath('bandbool.png'),
|
|
inputPngTestJoinChannel: getPath('testJoinChannel.png'),
|
|
|
|
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
|
|
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
|
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
|
|
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
|
|
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
|
|
inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg
|
|
inputSvgWithEmbeddedImages: getPath('struct-image-04-t.svg'), // https://dev.w3.org/SVG/profiles/1.2T/test/svg/struct-image-04-t.svg
|
|
|
|
inputJPGBig: getPath('flowers.jpeg'),
|
|
|
|
inputPngStripesV: getPath('stripesV.png'),
|
|
inputPngStripesH: getPath('stripesH.png'),
|
|
|
|
inputJpgBooleanTest: getPath('booleanTest.jpg'),
|
|
|
|
inputV: getPath('vfile.v'),
|
|
|
|
outputJpg: getPath('output.jpg'),
|
|
outputPng: getPath('output.png'),
|
|
outputWebP: getPath('output.webp'),
|
|
outputV: getPath('output.v'),
|
|
outputZoinks: getPath('output.zoinks'), // an 'unknown' file extension
|
|
|
|
// Path for tests requiring human inspection
|
|
path: getPath,
|
|
|
|
// Path for expected output images
|
|
expected: function (filename) {
|
|
return getPath(path.join('expected', filename));
|
|
},
|
|
|
|
// Verify similarity of expected vs actual images via fingerprint
|
|
// Specify distance threshold using `options={threshold: 42}`, default
|
|
// `threshold` is 5;
|
|
assertSimilar: function (expectedImage, actualImage, options, callback) {
|
|
if (typeof options === 'function') {
|
|
callback = options;
|
|
options = {};
|
|
}
|
|
|
|
if (typeof options === 'undefined' && options === null) {
|
|
options = {};
|
|
}
|
|
|
|
if (options.threshold === null || typeof options.threshold === 'undefined') {
|
|
options.threshold = 5; // ~7% threshold
|
|
}
|
|
|
|
if (typeof options.threshold !== 'number') {
|
|
throw new TypeError('`options.threshold` must be a number');
|
|
}
|
|
|
|
if (typeof callback !== 'function') {
|
|
throw new TypeError('`callback` must be a function');
|
|
}
|
|
|
|
fingerprint(expectedImage, function (err, expectedFingerprint) {
|
|
if (err) return callback(err);
|
|
fingerprint(actualImage, function (err, actualFingerprint) {
|
|
if (err) return callback(err);
|
|
let distance = 0;
|
|
for (let i = 0; i < 64; i++) {
|
|
if (expectedFingerprint[i] !== actualFingerprint[i]) {
|
|
distance++;
|
|
}
|
|
}
|
|
|
|
if (distance > options.threshold) {
|
|
return callback(new Error('Expected maximum similarity distance: ' + options.threshold + '. Actual: ' + distance + '.'));
|
|
}
|
|
|
|
callback();
|
|
});
|
|
});
|
|
},
|
|
|
|
assertMaxColourDistance: function (actualImagePath, expectedImagePath, acceptedDistance) {
|
|
if (typeof actualImagePath !== 'string') {
|
|
throw new TypeError('`actualImagePath` must be a string; got ' + actualImagePath);
|
|
}
|
|
if (typeof expectedImagePath !== 'string') {
|
|
throw new TypeError('`expectedImagePath` must be a string; got ' + expectedImagePath);
|
|
}
|
|
if (typeof acceptedDistance !== 'number') {
|
|
// Default threshold
|
|
acceptedDistance = 1;
|
|
}
|
|
const distance = maxColourDistance(actualImagePath, expectedImagePath);
|
|
if (distance > acceptedDistance) {
|
|
throw new Error('Expected maximum absolute distance of ' + acceptedDistance + ', actual ' + distance);
|
|
}
|
|
}
|
|
|
|
};
|