mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Composites an overlay image with alpha channel into the input image (which must have alpha channel) using ‘over’ alpha compositing blend mode. This API requires both images to have the same dimensions. References: - http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending - https://github.com/jcupitt/ruby-vips/issues/28#issuecomment-9014826 See #97.
128 lines
4.9 KiB
JavaScript
Executable File
128 lines
4.9 KiB
JavaScript
Executable File
'use strict';
|
|
|
|
var path = require('path');
|
|
var assert = require('assert');
|
|
|
|
var sharp = require('../../index');
|
|
|
|
var 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
|
|
var fingerprint = function(image, callback) {
|
|
sharp(image)
|
|
.greyscale()
|
|
.normalise()
|
|
.resize(9, 8)
|
|
.ignoreAspectRatio()
|
|
.interpolateWith(sharp.interpolator.vertexSplitQuadraticBasisSpline)
|
|
.raw()
|
|
.toBuffer(function(err, data) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
var fingerprint = '';
|
|
for (var col = 0; col < 8; col++) {
|
|
var gradient = 0;
|
|
for (var row = 0; row < 8; row++) {
|
|
var left = data[row * 8 + col];
|
|
var right = data[row * 8 + col + 1];
|
|
fingerprint = fingerprint + (left < right ? '1' : '0');
|
|
}
|
|
}
|
|
callback(null, fingerprint);
|
|
}
|
|
});
|
|
};
|
|
|
|
module.exports = {
|
|
|
|
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/
|
|
|
|
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'),
|
|
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'),
|
|
|
|
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.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
|
|
inputSvg: getPath('Wikimedia-logo.svg'), // http://commons.wikimedia.org/wiki/File:Wikimedia-logo.svg
|
|
inputPsd: getPath('free-gearhead-pack.psd'), // https://dribbble.com/shots/1624241-Free-Gearhead-Vector-Pack
|
|
|
|
inputSvs: getPath('CMU-1-Small-Region.svs'), // http://openslide.cs.cmu.edu/download/openslide-testdata/Aperio/CMU-1-Small-Region.svs
|
|
|
|
outputJpg: getPath('output.jpg'),
|
|
outputPng: getPath('output.png'),
|
|
outputWebP: getPath('output.webp'),
|
|
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);
|
|
var distance = 0;
|
|
for (var i = 0; i < 64; i++) {
|
|
if (expectedFingerprint[i] !== actualFingerprint[i]) {
|
|
distance++;
|
|
}
|
|
}
|
|
|
|
if (distance > options.threshold) {
|
|
return callback(new Error('Maximum similarity distance: ' + options.threshold + '. Actual: ' + distance));
|
|
}
|
|
|
|
callback();
|
|
});
|
|
});
|
|
}
|
|
|
|
};
|