Drop support for versions of Node prior to v4.

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.
This commit is contained in:
Lovell Fuller 2016-10-26 10:07:42 +01:00
parent 3f5e38bb62
commit 36e636dca1
14 changed files with 348 additions and 387 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ packaging/libvips*
packaging/*.log
!packaging/build
.DS_Store
.nyc_output

View File

@ -1,4 +0,0 @@
node_modules
test/bench/node_modules
test/saliency/humanae/node_modules
coverage

View File

@ -1,12 +0,0 @@
{
"strict": true,
"node": true,
"maxparams": 4,
"maxcomplexity": 14,
"globals": {
"beforeEach": true,
"afterEach": true,
"describe": true,
"it": true
}
}

View File

@ -14,3 +14,4 @@ lib
include
packaging
preinstall.sh
.nyc_output

View File

@ -1,9 +1,8 @@
language: node_js
node_js:
- "0.10"
- "0.12"
- "4"
- "6"
- "7"
os:
- linux
- osx
@ -18,4 +17,5 @@ osx_image: xcode8
before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-4.8; fi
after_success:
- npm install coveralls
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

View File

@ -3,14 +3,13 @@ version: "{build}"
build: off
platform: x64
environment:
VIPS_WARNING: 0
matrix:
- nodejs_version: "0.12"
- nodejs_version: "4"
- nodejs_version: "6"
- nodejs_version: "7"
install:
- ps: Install-Product node $env:nodejs_version x64
- npm install -g npm@latest
- npm install
test_script:
- npm run-script test-win
- npm test

View File

@ -1,81 +1,58 @@
'use strict';
var fs = require('fs');
var path = require('path');
var zlib = require('zlib');
const fs = require('fs');
const os = require('os');
const path = require('path');
const zlib = require('zlib');
var semver = require('semver');
var request = require('request');
var tar = require('tar');
const caw = require('caw');
const got = require('got');
const semver = require('semver');
const tar = require('tar');
var tmp = require('os').tmpdir();
var distBaseUrl = 'https://dl.bintray.com/lovell/sharp/';
const distBaseUrl = 'https://dl.bintray.com/lovell/sharp/';
// Use NPM-provided environment variable where available, falling back to require-based method for Electron
var minimumLibvipsVersion = process.env.npm_package_config_libvips || require('./package.json').config.libvips;
const minimumLibvipsVersion = process.env.npm_package_config_libvips || require('./package.json').config.libvips;
var vipsHeaderPath = path.join(__dirname, 'include', 'vips', 'vips.h');
const platform = process.env.npm_config_platform || process.platform;
var platform = process.env.npm_config_platform || process.platform;
var arch = process.env.npm_config_arch || process.arch;
var arm_version = process.env.npm_config_armv || process.config.variables.arm_version;
if (arch === 'arch64' || arch === 'armhf') {
arch = 'arm';
if (arch === 'arch64') arm_version = '8';
}
const arch = process.env.npm_config_arch || process.arch;
// -- Helpers
// Does this file exist?
var isFile = function(file) {
var exists = false;
const isFile = function (file) {
try {
exists = fs.statSync(file).isFile();
return fs.statSync(file).isFile();
} catch (err) {}
return exists;
};
var unpack = function(tarPath, done) {
var extractor = tar.Extract({
path: __dirname
});
const unpack = function (tarPath, done) {
const extractor = tar.Extract({ path: __dirname });
if (done) {
extractor.on('end', done);
}
extractor.on('error', error);
extractor.on('end', function() {
if (!isFile(vipsHeaderPath)) {
error('Could not unpack ' + tarPath);
}
if (typeof done === 'function') {
done();
}
});
fs.createReadStream(tarPath).on('error', error)
fs.createReadStream(tarPath)
.on('error', error)
.pipe(zlib.Unzip())
.pipe(extractor);
};
var platformId = function() {
var id = [platform, arch].join('-');
if (arch === 'arm') {
switch(arm_version) {
case '8':
id = id + 'v8';
break;
case '7':
id = id + 'v7';
break;
default:
id = id + 'v6';
break;
const platformId = function () {
const platformId = [platform];
if (arch === 'arm' || arch === 'armhf' || arch === 'arch64') {
const armVersion = (arch === 'arch64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6';
platformId.push('armv' + armVersion);
} else {
platformId.push(arch);
}
}
return id;
return platformId.join('-');
};
// Error
var error = function(msg) {
const error = function (msg) {
if (msg instanceof Error) {
msg = msg.message;
}
@ -85,18 +62,19 @@ var error = function(msg) {
// -- Binary downloaders
module.exports.download_vips = function() {
module.exports.download_vips = function () {
// Has vips been installed locally?
const vipsHeaderPath = path.join(__dirname, 'include', 'vips', 'vips.h');
if (!isFile(vipsHeaderPath)) {
// Ensure Intel 64-bit or ARM
if (arch === 'ia32') {
error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
// Ensure glibc >= 2.15
var lddVersion = process.env.LDD_VERSION;
const lddVersion = process.env.LDD_VERSION;
if (lddVersion) {
if (/(glibc|gnu libc)/i.test(lddVersion)) {
var glibcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : '';
const glibcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : '';
if (glibcVersion && semver.lt(glibcVersion + '.0', '2.13.0')) {
error('glibc version ' + glibcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
@ -105,47 +83,47 @@ module.exports.download_vips = function() {
}
}
// Arch/platform-specific .tar.gz
var tarFilename = ['libvips', minimumLibvipsVersion, platformId()].join('-') + '.tar.gz';
var tarPath = path.join(__dirname, 'packaging', tarFilename);
if (isFile(tarPath)) {
unpack(tarPath);
const tarFilename = ['libvips', minimumLibvipsVersion, platformId()].join('-') + '.tar.gz';
const tarPathLocal = path.join(__dirname, 'packaging', tarFilename);
if (isFile(tarPathLocal)) {
unpack(tarPathLocal);
} else {
// Download to per-process temporary file
tarPath = path.join(tmp, process.pid + '-' + tarFilename);
var tmpFile = fs.createWriteStream(tarPath).on('finish', function() {
unpack(tarPath, function() {
const tarPathTemp = path.join(os.tmpdir(), process.pid + '-' + tarFilename);
const tmpFile = fs.createWriteStream(tarPathTemp).on('finish', function () {
unpack(tarPathTemp, function () {
// Attempt to remove temporary file
try {
fs.unlinkSync(tarPath);
fs.unlinkSync(tarPathTemp);
} catch (err) {}
});
});
var options = {
url: distBaseUrl + tarFilename
};
const gotOpt = {};
if (process.env.npm_config_https_proxy) {
// Use the NPM-configured HTTPS proxy
options.proxy = process.env.npm_config_https_proxy;
gotOpt.agent = caw(process.env.npm_config_https_proxy);
}
request(options).on('response', function(response) {
const url = distBaseUrl + tarFilename;
got.stream(url, gotOpt).on('response', function (response) {
if (response.statusCode !== 200) {
error(distBaseUrl + tarFilename + ' status code ' + response.statusCode);
error(url + ' status code ' + response.statusCode);
}
}).on('error', function(err) {
error('Download from ' + distBaseUrl + tarFilename + ' failed: ' + err.message);
}).on('error', function (err) {
error('Download of ' + url + ' failed: ' + err.message);
}).pipe(tmpFile);
}
}
};
module.exports.use_global_vips = function() {
var useGlobalVips = false;
var globalVipsVersion = process.env.GLOBAL_VIPS_VERSION;
module.exports.use_global_vips = function () {
const globalVipsVersion = process.env.GLOBAL_VIPS_VERSION;
if (globalVipsVersion) {
useGlobalVips = semver.gte(
const useGlobalVips = semver.gte(
globalVipsVersion,
minimumLibvipsVersion
);
}
process.stdout.write(useGlobalVips ? 'true' : 'false');
} else {
process.stdout.write('false');
}
};

View File

@ -2,10 +2,12 @@
### v0.17 - "*quill*"
Requires libvips v8.4.2
Requires libvips v8.4.2.
#### v0.17.0 - TBD
* Drop support for versions of Node prior to v4.
* Deprecate the following output format "option" functions:
quality, progressive, compressionLevel, withoutAdaptiveFiltering,
withoutChromaSubsampling, trellisQuantisation, trellisQuantization,

279
index.js
View File

@ -1,23 +1,22 @@
'use strict';
var path = require('path');
var util = require('util');
var stream = require('stream');
var events = require('events');
const path = require('path');
const util = require('util');
const stream = require('stream');
const events = require('events');
var semver = require('semver');
var color = require('color');
var BluebirdPromise = require('bluebird');
const semver = require('semver');
const color = require('color');
var sharp = require('./build/Release/sharp.node');
const sharp = require('./build/Release/sharp.node');
// Versioning
var versions = {
let versions = {
vips: sharp.libvipsVersion()
};
(function() {
(function () {
// Does libvips meet minimum requirement?
var libvipsVersionMin = require('./package.json').config.libvips;
const libvipsVersionMin = require('./package.json').config.libvips;
if (semver.lt(versions.vips, libvipsVersionMin)) {
throw new Error('Found libvips ' + versions.vips + ' but require at least ' + libvipsVersionMin);
}
@ -28,14 +27,14 @@ var versions = {
})();
// Limits
var maximum = {
const maximum = {
width: 0x3FFF,
height: 0x3FFF,
pixels: Math.pow(0x3FFF, 2)
};
// Constructor-factory
var Sharp = function(input, options) {
const Sharp = function (input, options) {
if (!(this instanceof Sharp)) {
return new Sharp(input, options);
}
@ -91,8 +90,8 @@ var Sharp = function(input, options) {
colourspace: 'srgb',
// overlay
overlayGravity: 0,
overlayXOffset : -1,
overlayYOffset : -1,
overlayXOffset: -1,
overlayYOffset: -1,
overlayTile: false,
overlayCutout: false,
// output
@ -116,7 +115,7 @@ var Sharp = function(input, options) {
tileSize: 256,
tileOverlap: 0,
// Function to notify of queue length changes
queueListener: function(queueLength) {
queueListener: function (queueLength) {
module.exports.queue.emit('change', queueLength);
}
};
@ -144,42 +143,42 @@ module.exports.versions = versions;
/*
Validation helpers
*/
var isDefined = function(val) {
const isDefined = function (val) {
return typeof val !== 'undefined' && val !== null;
};
var isObject = function(val) {
const isObject = function (val) {
return typeof val === 'object';
};
var isFunction = function(val) {
const isFunction = function (val) {
return typeof val === 'function';
};
var isBoolean = function(val) {
const isBoolean = function (val) {
return typeof val === 'boolean';
};
var isBuffer = function(val) {
const isBuffer = function (val) {
return typeof val === 'object' && val instanceof Buffer;
};
var isString = function(val) {
const isString = function (val) {
return typeof val === 'string' && val.length > 0;
};
var isNumber = function(val) {
const isNumber = function (val) {
return typeof val === 'number' && !Number.isNaN(val);
};
var isInteger = function(val) {
const isInteger = function (val) {
return isNumber(val) && val % 1 === 0;
};
var inRange = function(val, min, max) {
const inRange = function (val, min, max) {
return val >= min && val <= max;
};
var contains = function(val, list) {
const contains = function (val, list) {
return list.indexOf(val) !== -1;
};
/*
Create Object containing input and input-related options
*/
Sharp.prototype._createInputDescriptor = function(input, inputOptions, containerOptions) {
var inputDescriptor = {};
Sharp.prototype._createInputDescriptor = function (input, inputOptions, containerOptions) {
const inputDescriptor = {};
if (isString(input)) {
// filesystem
inputDescriptor.file = input;
@ -225,8 +224,7 @@ Sharp.prototype._createInputDescriptor = function(input, inputOptions, container
/*
Handle incoming chunk on Writable Stream
*/
Sharp.prototype._write = function(chunk, encoding, callback) {
/*jslint unused: false */
Sharp.prototype._write = function (chunk, encoding, callback) {
if (Array.isArray(this.options.input.buffer)) {
if (isBuffer(chunk)) {
this.options.input.buffer.push(chunk);
@ -242,12 +240,12 @@ Sharp.prototype._write = function(chunk, encoding, callback) {
/*
Flattens the array of chunks accumulated in input.buffer
*/
Sharp.prototype._flattenBufferIn = function() {
Sharp.prototype._flattenBufferIn = function () {
if (this._isStreamInput()) {
this.options.input.buffer = Buffer.concat(this.options.input.buffer);
}
};
Sharp.prototype._isStreamInput = function() {
Sharp.prototype._isStreamInput = function () {
return Array.isArray(this.options.input.buffer);
};
@ -274,7 +272,7 @@ module.exports.strategy = {
/*
What part of the image should be retained when cropping?
*/
Sharp.prototype.crop = function(crop) {
Sharp.prototype.crop = function (crop) {
this.options.canvas = 'crop';
if (!isDefined(crop)) {
// Default
@ -294,10 +292,10 @@ Sharp.prototype.crop = function(crop) {
return this;
};
Sharp.prototype.extract = function(options) {
var suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
Sharp.prototype.extract = function (options) {
const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
['left', 'top', 'width', 'height'].forEach(function (name) {
var value = options[name];
const value = options[name];
if (isInteger(value) && value >= 0) {
this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
} else {
@ -311,14 +309,15 @@ Sharp.prototype.extract = function(options) {
return this;
};
Sharp.prototype.extractChannel = function(channel) {
if (channel === 'red')
Sharp.prototype.extractChannel = function (channel) {
if (channel === 'red') {
channel = 0;
else if (channel === 'green')
} else if (channel === 'green') {
channel = 1;
else if (channel === 'blue')
} else if (channel === 'blue') {
channel = 2;
if(isInteger(channel) && inRange(channel,0,4)) {
}
if (isInteger(channel) && inRange(channel, 0, 4)) {
this.options.extractChannel = channel;
} else {
throw new Error('Cannot extract invalid channel ' + channel);
@ -331,24 +330,24 @@ Sharp.prototype.extractChannel = function(channel) {
Delegates to the 'Color' module, which can throw an Error
but is liberal in what it accepts, clamping values to sensible min/max.
*/
Sharp.prototype.background = function(rgba) {
var colour = color(rgba);
Sharp.prototype.background = function (rgba) {
const colour = color(rgba);
this.options.background = colour.rgbArray();
this.options.background.push(colour.alpha() * 255);
return this;
};
Sharp.prototype.embed = function() {
Sharp.prototype.embed = function () {
this.options.canvas = 'embed';
return this;
};
Sharp.prototype.max = function() {
Sharp.prototype.max = function () {
this.options.canvas = 'max';
return this;
};
Sharp.prototype.min = function() {
Sharp.prototype.min = function () {
this.options.canvas = 'min';
return this;
};
@ -357,17 +356,17 @@ Sharp.prototype.min = function() {
Ignoring the aspect ratio of the input, stretch the image to
the exact width and/or height provided via the resize method.
*/
Sharp.prototype.ignoreAspectRatio = function() {
Sharp.prototype.ignoreAspectRatio = function () {
this.options.canvas = 'ignore_aspect';
return this;
};
Sharp.prototype.flatten = function(flatten) {
Sharp.prototype.flatten = function (flatten) {
this.options.flatten = isBoolean(flatten) ? flatten : true;
return this;
};
Sharp.prototype.negate = function(negate) {
Sharp.prototype.negate = function (negate) {
this.options.negate = isBoolean(negate) ? negate : true;
return this;
};
@ -375,7 +374,7 @@ Sharp.prototype.negate = function(negate) {
/*
Bitwise boolean operations between images
*/
Sharp.prototype.boolean = function(operand, operator, options) {
Sharp.prototype.boolean = function (operand, operator, options) {
this.options.boolean = this._createInputDescriptor(operand, options);
if (isString(operator) && contains(operator, ['and', 'or', 'eor'])) {
this.options.booleanOp = operator;
@ -388,7 +387,7 @@ Sharp.prototype.boolean = function(operand, operator, options) {
/*
Overlay with another image, using an optional gravity
*/
Sharp.prototype.overlayWith = function(overlay, options) {
Sharp.prototype.overlayWith = function (overlay, options) {
this.options.overlay = this._createInputDescriptor(overlay, options, {
allowStream: false
});
@ -419,7 +418,7 @@ Sharp.prototype.overlayWith = function(overlay, options) {
}
}
if (isDefined(options.gravity)) {
if(isInteger(options.gravity) && inRange(options.gravity, 0, 8)) {
if (isInteger(options.gravity) && inRange(options.gravity, 0, 8)) {
this.options.overlayGravity = options.gravity;
} else if (isString(options.gravity) && isInteger(module.exports.gravity[options.gravity])) {
this.options.overlayGravity = module.exports.gravity[options.gravity];
@ -434,9 +433,9 @@ Sharp.prototype.overlayWith = function(overlay, options) {
/*
Add another color channel to the image
*/
Sharp.prototype.joinChannel = function(images, options) {
Sharp.prototype.joinChannel = function (images, options) {
if (Array.isArray(images)) {
images.forEach(function(image) {
images.forEach(function (image) {
this.options.joinChannelIn.push(this._createInputDescriptor(image, options));
}, this);
} else {
@ -449,7 +448,7 @@ Sharp.prototype.joinChannel = function(images, options) {
Rotate output image by 0, 90, 180 or 270 degrees
Auto-rotation based on the EXIF Orientation tag is represented by an angle of -1
*/
Sharp.prototype.rotate = function(angle) {
Sharp.prototype.rotate = function (angle) {
if (!isDefined(angle)) {
this.options.angle = -1;
} else if (isInteger(angle) && contains(angle, [0, 90, 180, 270])) {
@ -463,7 +462,7 @@ Sharp.prototype.rotate = function(angle) {
/*
Flip the image vertically, about the Y axis
*/
Sharp.prototype.flip = function(flip) {
Sharp.prototype.flip = function (flip) {
this.options.flip = isBoolean(flip) ? flip : true;
return this;
};
@ -471,7 +470,7 @@ Sharp.prototype.flip = function(flip) {
/*
Flop the image horizontally, about the X axis
*/
Sharp.prototype.flop = function(flop) {
Sharp.prototype.flop = function (flop) {
this.options.flop = isBoolean(flop) ? flop : true;
return this;
};
@ -481,7 +480,7 @@ Sharp.prototype.flop = function(flop) {
This is equivalent to GraphicsMagick's ">" geometry option:
"change the dimensions of the image only if its width or height exceeds the geometry specification"
*/
Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
Sharp.prototype.withoutEnlargement = function (withoutEnlargement) {
this.options.withoutEnlargement = isBoolean(withoutEnlargement) ? withoutEnlargement : true;
return this;
};
@ -491,7 +490,7 @@ Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
Call without a sigma to use a fast, mild blur.
Call with a sigma to use a slower, more accurate Gaussian blur.
*/
Sharp.prototype.blur = function(sigma) {
Sharp.prototype.blur = function (sigma) {
if (!isDefined(sigma)) {
// No arguments: default to mild blur
this.options.blurSigma = -1;
@ -510,18 +509,18 @@ Sharp.prototype.blur = function(sigma) {
/*
Convolve the image with a kernel.
*/
Sharp.prototype.convolve = function(kernel) {
Sharp.prototype.convolve = function (kernel) {
if (!isObject(kernel) || !Array.isArray(kernel.kernel) ||
!isInteger(kernel.width) || !isInteger(kernel.height) ||
!inRange(kernel.width, 3, 1001) || !inRange(kernel.height, 3, 1001) ||
kernel.height * kernel.width != kernel.kernel.length
kernel.height * kernel.width !== kernel.kernel.length
) {
// must pass in a kernel
throw new Error('Invalid convolution kernel');
}
// Default scale is sum of kernel values
if (!isInteger(kernel.scale)) {
kernel.scale = kernel.kernel.reduce(function(a, b) {
kernel.scale = kernel.kernel.reduce(function (a, b) {
return a + b;
}, 0);
}
@ -544,7 +543,7 @@ Sharp.prototype.convolve = function(kernel) {
flat - level of "flat" area sharpen, default 1
jagged - level of "jagged" area sharpen, default 2
*/
Sharp.prototype.sharpen = function(sigma, flat, jagged) {
Sharp.prototype.sharpen = function (sigma, flat, jagged) {
if (!isDefined(sigma)) {
// No arguments: default to mild sharpen
this.options.sharpenSigma = -1;
@ -576,7 +575,7 @@ Sharp.prototype.sharpen = function(sigma, flat, jagged) {
return this;
};
Sharp.prototype.threshold = function(threshold, options) {
Sharp.prototype.threshold = function (threshold, options) {
if (!isDefined(threshold)) {
this.options.threshold = 128;
} else if (isBoolean(threshold)) {
@ -599,7 +598,7 @@ Sharp.prototype.threshold = function(threshold, options) {
tolerance - if present, is a percentaged tolerance level between 0 and 100 to trim away similar color values
Defaulting to 10 when no tolerance is given.
*/
Sharp.prototype.trim = function(tolerance) {
Sharp.prototype.trim = function (tolerance) {
if (!isDefined(tolerance)) {
this.options.trimTolerance = 10;
} else if (isInteger(tolerance) && inRange(tolerance, 1, 99)) {
@ -614,7 +613,7 @@ Sharp.prototype.trim = function(tolerance) {
Darken image pre-resize (1/gamma) and brighten post-resize (gamma).
Improves brightness of resized image in non-linear colour spaces.
*/
Sharp.prototype.gamma = function(gamma) {
Sharp.prototype.gamma = function (gamma) {
if (!isDefined(gamma)) {
// Default gamma correction of 2.2 (sRGB)
this.options.gamma = 2.2;
@ -629,7 +628,7 @@ Sharp.prototype.gamma = function(gamma) {
/*
Enhance output image contrast by stretching its luminance to cover the full dynamic range
*/
Sharp.prototype.normalize = function(normalize) {
Sharp.prototype.normalize = function (normalize) {
this.options.normalize = isBoolean(normalize) ? normalize : true;
return this;
};
@ -638,7 +637,7 @@ Sharp.prototype.normalise = Sharp.prototype.normalize;
/*
Perform boolean/bitwise operation on image color channels - results in one channel image
*/
Sharp.prototype.bandbool = function(boolOp) {
Sharp.prototype.bandbool = function (boolOp) {
if (isString(boolOp) && contains(boolOp, ['and', 'or', 'eor'])) {
this.options.bandBoolOp = boolOp;
} else {
@ -650,7 +649,7 @@ Sharp.prototype.bandbool = function(boolOp) {
/*
Convert to greyscale
*/
Sharp.prototype.greyscale = function(greyscale) {
Sharp.prototype.greyscale = function (greyscale) {
this.options.greyscale = isBoolean(greyscale) ? greyscale : true;
return this;
};
@ -659,8 +658,8 @@ Sharp.prototype.grayscale = Sharp.prototype.greyscale;
/*
Set output colourspace
*/
Sharp.prototype.toColourspace = function(colourspace) {
if (!isString(colourspace) ) {
Sharp.prototype.toColourspace = function (colourspace) {
if (!isString(colourspace)) {
throw new Error('Invalid output colourspace ' + colourspace);
}
this.options.colourspace = colourspace;
@ -668,59 +667,59 @@ Sharp.prototype.toColourspace = function(colourspace) {
};
Sharp.prototype.toColorspace = Sharp.prototype.toColourspace;
Sharp.prototype.sequentialRead = function(sequentialRead) {
Sharp.prototype.sequentialRead = function (sequentialRead) {
this.options.sequentialRead = isBoolean(sequentialRead) ? sequentialRead : true;
return this;
};
// Deprecated output options
Sharp.prototype.quality = util.deprecate(function(quality) {
var formatOut = this.options.formatOut;
var options = { quality: quality };
Sharp.prototype.quality = util.deprecate(function (quality) {
const formatOut = this.options.formatOut;
const options = { quality: quality };
this.jpeg(options).webp(options).tiff(options);
this.options.formatOut = formatOut;
return this;
}, 'quality: use jpeg({ quality: ... }), webp({ quality: ... }) and/or tiff({ quality: ... }) instead');
Sharp.prototype.progressive = util.deprecate(function(progressive) {
var formatOut = this.options.formatOut;
var options = { progressive: (progressive !== false) };
Sharp.prototype.progressive = util.deprecate(function (progressive) {
const formatOut = this.options.formatOut;
const options = { progressive: (progressive !== false) };
this.jpeg(options).png(options);
this.options.formatOut = formatOut;
return this;
}, 'progressive: use jpeg({ progressive: ... }) and/or png({ progressive: ... }) instead');
Sharp.prototype.compressionLevel = util.deprecate(function(compressionLevel) {
var formatOut = this.options.formatOut;
Sharp.prototype.compressionLevel = util.deprecate(function (compressionLevel) {
const formatOut = this.options.formatOut;
this.png({ compressionLevel: compressionLevel });
this.options.formatOut = formatOut;
return this;
}, 'compressionLevel: use png({ compressionLevel: ... }) instead');
Sharp.prototype.withoutAdaptiveFiltering = util.deprecate(function(withoutAdaptiveFiltering) {
var formatOut = this.options.formatOut;
Sharp.prototype.withoutAdaptiveFiltering = util.deprecate(function (withoutAdaptiveFiltering) {
const formatOut = this.options.formatOut;
this.png({ adaptiveFiltering: (withoutAdaptiveFiltering === false) });
this.options.formatOut = formatOut;
return this;
}, 'withoutAdaptiveFiltering: use png({ adaptiveFiltering: ... }) instead');
Sharp.prototype.withoutChromaSubsampling = util.deprecate(function(withoutChromaSubsampling) {
var formatOut = this.options.formatOut;
Sharp.prototype.withoutChromaSubsampling = util.deprecate(function (withoutChromaSubsampling) {
const formatOut = this.options.formatOut;
this.jpeg({ chromaSubsampling: (withoutChromaSubsampling === false) ? '4:2:0' : '4:4:4' });
this.options.formatOut = formatOut;
return this;
}, 'withoutChromaSubsampling: use jpeg({ chromaSubsampling: "4:4:4" }) instead');
Sharp.prototype.trellisQuantisation = util.deprecate(function(trellisQuantisation) {
var formatOut = this.options.formatOut;
Sharp.prototype.trellisQuantisation = util.deprecate(function (trellisQuantisation) {
const formatOut = this.options.formatOut;
this.jpeg({ trellisQuantisation: (trellisQuantisation !== false) });
this.options.formatOut = formatOut;
return this;
}, 'trellisQuantisation: use jpeg({ trellisQuantisation: ... }) instead');
Sharp.prototype.trellisQuantization = Sharp.prototype.trellisQuantisation;
Sharp.prototype.overshootDeringing = util.deprecate(function(overshootDeringing) {
var formatOut = this.options.formatOut;
Sharp.prototype.overshootDeringing = util.deprecate(function (overshootDeringing) {
const formatOut = this.options.formatOut;
this.jpeg({ overshootDeringing: (overshootDeringing !== false) });
this.options.formatOut = formatOut;
return this;
}, 'overshootDeringing: use jpeg({ overshootDeringing: ... }) instead');
Sharp.prototype.optimiseScans = util.deprecate(function(optimiseScans) {
var formatOut = this.options.formatOut;
Sharp.prototype.optimiseScans = util.deprecate(function (optimiseScans) {
const formatOut = this.options.formatOut;
this.jpeg({ optimiseScans: (optimiseScans !== false) });
this.options.formatOut = formatOut;
return this;
@ -732,7 +731,7 @@ Sharp.prototype.optimizeScans = Sharp.prototype.optimiseScans;
Optionally provide an Object with attributes to update:
orientation: numeric value for EXIF Orientation tag
*/
Sharp.prototype.withMetadata = function(withMetadata) {
Sharp.prototype.withMetadata = function (withMetadata) {
this.options.withMetadata = isBoolean(withMetadata) ? withMetadata : true;
if (isObject(withMetadata)) {
if (isDefined(withMetadata.orientation)) {
@ -749,7 +748,7 @@ Sharp.prototype.withMetadata = function(withMetadata) {
/*
Tile-based deep zoom output options: size, overlap, layout
*/
Sharp.prototype.tile = function(tile) {
Sharp.prototype.tile = function (tile) {
if (isObject(tile)) {
// Size of square tiles, in pixels
if (isDefined(tile.size)) {
@ -793,7 +792,7 @@ Sharp.prototype.tile = function(tile) {
/*
Extend edges
*/
Sharp.prototype.extend = function(extend) {
Sharp.prototype.extend = function (extend) {
if (isInteger(extend) && extend > 0) {
this.options.extendTop = extend;
this.options.extendBottom = extend;
@ -854,7 +853,7 @@ module.exports.colorspace = module.exports.colourspace;
options.kernel is the kernel to use for reductions, default 'lanczos3'
options.interpolator is the interpolator to use for enlargements, default 'bicubic'
*/
Sharp.prototype.resize = function(width, height, options) {
Sharp.prototype.resize = function (width, height, options) {
if (isDefined(width)) {
if (isInteger(width) && inRange(width, 1, maximum.width)) {
this.options.width = width;
@ -899,11 +898,11 @@ Sharp.prototype.resize = function(width, height, options) {
Assumes the image dimensions contained in the file header can be trusted.
Alternatively can use boolean to disable or reset to default (maximum pixels)
*/
Sharp.prototype.limitInputPixels = function(limit) {
//if we pass in false we represent the integer as 0 to disable
if(limit === false) {
Sharp.prototype.limitInputPixels = function (limit) {
// if we pass in false we represent the integer as 0 to disable
if (limit === false) {
limit = 0;
} else if(limit === true) {
} else if (limit === true) {
limit = maximum.pixels;
}
if (typeof limit === 'number' && !Number.isNaN(limit) && limit % 1 === 0 && limit >= 0) {
@ -918,21 +917,21 @@ Sharp.prototype.limitInputPixels = function(limit) {
* Write output image data to a file
* @throws {Error} if an attempt has been made to force Buffer/Stream output type
*/
Sharp.prototype.toFile = function(fileOut, callback) {
Sharp.prototype.toFile = function (fileOut, callback) {
if (!fileOut || fileOut.length === 0) {
var errOutputInvalid = new Error('Invalid output');
const errOutputInvalid = new Error('Invalid output');
if (isFunction(callback)) {
callback(errOutputInvalid);
} else {
return BluebirdPromise.reject(errOutputInvalid);
return Promise.reject(errOutputInvalid);
}
} else {
if (this.options.input.file === fileOut) {
var errOutputIsInput = new Error('Cannot use same file for input and output');
const errOutputIsInput = new Error('Cannot use same file for input and output');
if (isFunction(callback)) {
callback(errOutputIsInput);
} else {
return BluebirdPromise.reject(errOutputIsInput);
return Promise.reject(errOutputIsInput);
}
} else {
this.options.fileOut = fileOut;
@ -947,7 +946,7 @@ Sharp.prototype.toFile = function(fileOut, callback) {
* @param {Function} [callback]
* @returns {Promise} when no callback is provided
*/
Sharp.prototype.toBuffer = function(callback) {
Sharp.prototype.toBuffer = function (callback) {
return this._pipeline(callback);
};
@ -960,7 +959,7 @@ Sharp.prototype.toBuffer = function(callback) {
* @param {Boolean} [options.force=true] - force output format, otherwise attempt to use input format
* @returns {Object} this
*/
Sharp.prototype._updateFormatOut = function(formatOut, options) {
Sharp.prototype._updateFormatOut = function (formatOut, options) {
this.options.formatOut = (isObject(options) && options.force === false) ? 'input' : formatOut;
return this;
};
@ -972,7 +971,7 @@ Sharp.prototype._updateFormatOut = function(formatOut, options) {
* @param {Boolean} val
* @returns {void}
*/
Sharp.prototype._setBooleanOption = function(key, val) {
Sharp.prototype._setBooleanOption = function (key, val) {
if (isBoolean(val)) {
this.options[key] = val;
} else {
@ -993,7 +992,7 @@ Sharp.prototype._setBooleanOption = function(key, val) {
* @returns {Object} this
* @throws {Error} Invalid options
*/
Sharp.prototype.jpeg = function(options) {
Sharp.prototype.jpeg = function (options) {
if (isObject(options)) {
if (isDefined(options.quality)) {
if (isInteger(options.quality) && inRange(options.quality, 1, 100)) {
@ -1040,7 +1039,7 @@ Sharp.prototype.jpeg = function(options) {
* @returns {Object} this
* @throws {Error} Invalid options
*/
Sharp.prototype.png = function(options) {
Sharp.prototype.png = function (options) {
if (isObject(options)) {
if (isDefined(options.progressive)) {
this._setBooleanOption('pngProgressive', options.progressive);
@ -1067,7 +1066,7 @@ Sharp.prototype.png = function(options) {
* @returns {Object} this
* @throws {Error} Invalid options
*/
Sharp.prototype.webp = function(options) {
Sharp.prototype.webp = function (options) {
if (isObject(options)) {
if (isDefined(options.quality)) {
if (isInteger(options.quality) && inRange(options.quality, 1, 100)) {
@ -1088,7 +1087,7 @@ Sharp.prototype.webp = function(options) {
* @returns {Object} this
* @throws {Error} Invalid options
*/
Sharp.prototype.tiff = function(options) {
Sharp.prototype.tiff = function (options) {
if (isObject(options)) {
if (isDefined(options.quality)) {
if (isInteger(options.quality) && inRange(options.quality, 1, 100)) {
@ -1105,7 +1104,7 @@ Sharp.prototype.tiff = function(options) {
* Force output to be raw, uncompressed uint8 pixel data.
* @returns {Object} this
*/
Sharp.prototype.raw = function() {
Sharp.prototype.raw = function () {
return this._updateFormatOut('raw');
};
@ -1116,7 +1115,7 @@ Sharp.prototype.raw = function() {
* @returns {Object} this
* @throws {Error} unsupported format or options
*/
Sharp.prototype.toFormat = function(format, options) {
Sharp.prototype.toFormat = function (format, options) {
if (isObject(format) && isString(format.id)) {
format = format.id;
}
@ -1129,7 +1128,7 @@ Sharp.prototype.toFormat = function(format, options) {
/*
Used by a Writable Stream to notify that it is ready for data
*/
Sharp.prototype._read = function() {
Sharp.prototype._read = function () {
if (!this.options.streamOut) {
this.options.streamOut = true;
this._pipeline();
@ -1140,13 +1139,13 @@ Sharp.prototype._read = function() {
Invoke the C++ image processing pipeline
Supports callback, stream and promise variants
*/
Sharp.prototype._pipeline = function(callback) {
var that = this;
Sharp.prototype._pipeline = function (callback) {
const that = this;
if (typeof callback === 'function') {
// output=file/buffer
if (this._isStreamInput()) {
// output=file/buffer, input=stream
this.on('finish', function() {
this.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, callback);
});
@ -1159,9 +1158,9 @@ Sharp.prototype._pipeline = function(callback) {
// output=stream
if (this._isStreamInput()) {
// output=stream, input=stream
this.on('finish', function() {
this.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, function(err, data, info) {
sharp.pipeline(that.options, function (err, data, info) {
if (err) {
that.emit('error', err);
} else {
@ -1173,7 +1172,7 @@ Sharp.prototype._pipeline = function(callback) {
});
} else {
// output=stream, input=file/buffer
sharp.pipeline(this.options, function(err, data, info) {
sharp.pipeline(this.options, function (err, data, info) {
if (err) {
that.emit('error', err);
} else {
@ -1188,10 +1187,10 @@ Sharp.prototype._pipeline = function(callback) {
// output=promise
if (this._isStreamInput()) {
// output=promise, input=stream
return new BluebirdPromise(function(resolve, reject) {
that.on('finish', function() {
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, function(err, data) {
sharp.pipeline(that.options, function (err, data) {
if (err) {
reject(err);
} else {
@ -1202,8 +1201,8 @@ Sharp.prototype._pipeline = function(callback) {
});
} else {
// output=promise, input=file/buffer
return new BluebirdPromise(function(resolve, reject) {
sharp.pipeline(that.options, function(err, data) {
return new Promise(function (resolve, reject) {
sharp.pipeline(that.options, function (err, data) {
if (err) {
reject(err);
} else {
@ -1219,11 +1218,11 @@ Sharp.prototype._pipeline = function(callback) {
Reads the image header and returns metadata
Supports callback, stream and promise variants
*/
Sharp.prototype.metadata = function(callback) {
var that = this;
Sharp.prototype.metadata = function (callback) {
const that = this;
if (typeof callback === 'function') {
if (this._isStreamInput()) {
this.on('finish', function() {
this.on('finish', function () {
that._flattenBufferIn();
sharp.metadata(that.options, callback);
});
@ -1233,10 +1232,10 @@ Sharp.prototype.metadata = function(callback) {
return this;
} else {
if (this._isStreamInput()) {
return new BluebirdPromise(function(resolve, reject) {
that.on('finish', function() {
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
sharp.metadata(that.options, function(err, metadata) {
sharp.metadata(that.options, function (err, metadata) {
if (err) {
reject(err);
} else {
@ -1246,8 +1245,8 @@ Sharp.prototype.metadata = function(callback) {
});
});
} else {
return new BluebirdPromise(function(resolve, reject) {
sharp.metadata(that.options, function(err, metadata) {
return new Promise(function (resolve, reject) {
sharp.metadata(that.options, function (err, metadata) {
if (err) {
reject(err);
} else {
@ -1263,13 +1262,13 @@ Sharp.prototype.metadata = function(callback) {
Clone new instance using existing options.
Cloned instances share the same input.
*/
Sharp.prototype.clone = function() {
var that = this;
Sharp.prototype.clone = function () {
const that = this;
// Clone existing options
var clone = new Sharp();
const clone = new Sharp();
util._extend(clone.options, this.options);
// Pass 'finish' event to clone for Stream-based input
this.on('finish', function() {
this.on('finish', function () {
// Clone inherits input data
that._flattenBufferIn();
clone.options.bufferIn = that.options.bufferIn;
@ -1281,7 +1280,7 @@ Sharp.prototype.clone = function() {
/**
Get and set cache memory, file and item limits
*/
module.exports.cache = function(options) {
module.exports.cache = function (options) {
if (isBoolean(options)) {
if (options) {
// Default cache settings of 50MB, 20 files, 100 items
@ -1301,21 +1300,21 @@ module.exports.cache(true);
/*
Get and set size of thread pool
*/
module.exports.concurrency = function(concurrency) {
module.exports.concurrency = function (concurrency) {
return sharp.concurrency(isInteger(concurrency) ? concurrency : null);
};
/*
Get internal counters
*/
module.exports.counters = function() {
module.exports.counters = function () {
return sharp.counters();
};
/*
Get and set use of SIMD vector unit instructions
*/
module.exports.simd = function(simd) {
module.exports.simd = function (simd) {
return sharp.simd(isBoolean(simd) ? simd : null);
};
// Switch off default

View File

@ -1,5 +1,6 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.17.0",
"author": "Lovell Fuller <npm@lovell.info>",
"contributors": [
@ -29,14 +30,11 @@
"Matt Hirsch <mhirsch@media.mit.edu>",
"Matthias Thoemmes <thoemmes@gmail.com>"
],
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"scripts": {
"clean": "rm -rf node_modules/ build/ include/ lib/ coverage/ test/fixtures/output.*",
"test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=30000 ./test/unit/*.js",
"test-win": "node ./node_modules/mocha/bin/mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test": "semistandard && cross-env VIPS_WARNING=0 nyc --reporter=lcov --branches=96 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-leak": "./test/leak/leak.sh",
"test-packaging": "./packaging/test-linux-x64.sh",
"test-clean": "rm -rf coverage/ test/fixtures/output.* && npm install && npm test"
"test-packaging": "./packaging/test-linux-x64.sh"
},
"main": "index.js",
"repository": {
@ -59,24 +57,24 @@
"vips"
],
"dependencies": {
"bluebird": "^3.4.6",
"caw": "^2.0.0",
"color": "^0.11.3",
"got": "^6.5.0",
"nan": "^2.4.0",
"semver": "^5.3.0",
"request": "^2.75.0",
"tar": "^2.2.1"
},
"devDependencies": {
"async": "^2.1.2",
"bufferutil": "^1.2.1",
"coveralls": "^2.11.14",
"cross-env": "^3.1.3",
"exif-reader": "^1.0.1",
"icc": "^0.0.2",
"istanbul": "^0.4.5",
"mocha": "^3.1.2",
"mocha-jshint": "^2.3.1",
"node-cpplint": "^0.4.0",
"nyc": "^8.3.2",
"rimraf": "^2.5.4",
"semistandard": "^9.1.0",
"unzip": "^0.1.11"
},
"license": "Apache-2.0",
@ -84,6 +82,14 @@
"libvips": "8.4.2"
},
"engines": {
"node": ">=0.10"
"node": ">=4"
},
"semistandard": {
"env": [
"mocha"
],
"ignore": [
"test"
]
}
}

View File

@ -1,34 +1,32 @@
'use strict';
var path = require('path');
var assert = require('assert');
var sharp = require('../../index');
var maxColourDistance = require('../../build/Release/sharp')._maxColourDistance;
const path = require('path');
const sharp = require('../../index');
const maxColourDistance = require('../../build/Release/sharp')._maxColourDistance;
// Helpers
var getPath = function(filename) {
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
var fingerprint = function(image, callback) {
const fingerprint = function (image, callback) {
sharp(image)
.greyscale()
.normalise()
.resize(9, 8)
.ignoreAspectRatio()
.raw()
.toBuffer(function(err, data) {
.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];
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');
}
}
@ -109,14 +107,14 @@ module.exports = {
path: getPath,
// Path for expected output images
expected: function(filename) {
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) {
assertSimilar: function (expectedImage, actualImage, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
@ -138,12 +136,12 @@ module.exports = {
throw new TypeError('`callback` must be a function');
}
fingerprint(expectedImage, function(err, expectedFingerprint) {
fingerprint(expectedImage, function (err, expectedFingerprint) {
if (err) return callback(err);
fingerprint(actualImage, function(err, actualFingerprint) {
fingerprint(actualImage, function (err, actualFingerprint) {
if (err) return callback(err);
var distance = 0;
for (var i = 0; i < 64; i++) {
let distance = 0;
for (let i = 0; i < 64; i++) {
if (expectedFingerprint[i] !== actualFingerprint[i]) {
distance++;
}
@ -158,7 +156,7 @@ module.exports = {
});
},
assertMaxColourDistance: function(actualImagePath, expectedImagePath, acceptedDistance) {
assertMaxColourDistance: function (actualImagePath, expectedImagePath, acceptedDistance) {
if (typeof actualImagePath !== 'string') {
throw new TypeError('`actualImagePath` must be a string; got ' + actualImagePath);
}
@ -169,7 +167,7 @@ module.exports = {
// Default threshold
acceptedDistance = 1;
}
var distance = maxColourDistance(actualImagePath, expectedImagePath);
const distance = maxColourDistance(actualImagePath, expectedImagePath);
if (distance > acceptedDistance) {
throw new Error('Expected maximum absolute distance of ' + acceptedDistance + ', actual ' + distance);
}

View File

@ -1,19 +1,18 @@
'use strict';
var assert = require('assert');
var fs = require('fs');
var sharp = require('../../index');
var fixtures = require('../fixtures');
var BluebirdPromise = require('bluebird');
const assert = require('assert');
const fs = require('fs');
describe('Image channel insertion', function() {
const sharp = require('../../index');
const fixtures = require('../fixtures');
it('Grayscale to RGB, buffer', function(done) {
describe('Image channel insertion', function () {
it('Grayscale to RGB, buffer', function (done) {
sharp(fixtures.inputPng) // gray -> red
.resize(320, 240)
.joinChannel(fixtures.inputPngTestJoinChannel) // new green channel
.joinChannel(fixtures.inputPngStripesH) // new blue channel
.toBuffer(function(err, data, info) {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
@ -22,12 +21,12 @@ describe('Image channel insertion', function() {
});
});
it('Grayscale to RGB, file', function(done) {
it('Grayscale to RGB, file', function (done) {
sharp(fixtures.inputPng) // gray -> red
.resize(320, 240)
.joinChannel(fs.readFileSync(fixtures.inputPngTestJoinChannel)) // new green channel
.joinChannel(fs.readFileSync(fixtures.inputPngStripesH)) // new blue channel
.toBuffer(function(err, data, info) {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
@ -36,14 +35,14 @@ describe('Image channel insertion', function() {
});
});
it('Grayscale to RGBA, buffer', function(done) {
it('Grayscale to RGBA, buffer', function (done) {
sharp(fixtures.inputPng) // gray -> red
.resize(320, 240)
.joinChannel([fixtures.inputPngTestJoinChannel,
fixtures.inputPngStripesH,
fixtures.inputPngStripesV]) // new green + blue + alpha channel
.toColourspace(sharp.colourspace.srgb)
.toBuffer(function(err, data, info) {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
@ -52,14 +51,14 @@ describe('Image channel insertion', function() {
});
});
it('Grayscale to RGBA, file', function(done) {
it('Grayscale to RGBA, file', function (done) {
sharp(fixtures.inputPng) // gray -> red
.resize(320, 240)
.joinChannel([fs.readFileSync(fixtures.inputPngTestJoinChannel), // new green channel
fs.readFileSync(fixtures.inputPngStripesH), // new blue channel
fs.readFileSync(fixtures.inputPngStripesV)]) // new alpha channel
.toColourspace('srgb')
.toBuffer(function(err, data, info) {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
@ -68,7 +67,7 @@ describe('Image channel insertion', function() {
});
});
it('Grayscale to CMYK, buffers', function(done) {
it('Grayscale to CMYK, buffers', function (done) {
sharp(fixtures.inputPng) // gray -> magenta
.resize(320, 240)
.joinChannel([fs.readFileSync(fixtures.inputPngTestJoinChannel), // new cyan channel
@ -76,7 +75,7 @@ describe('Image channel insertion', function() {
fs.readFileSync(fixtures.inputPngStripesV)]) // new black channel
.toColorspace('cmyk')
.toFormat('jpeg')
.toBuffer(function(err, data, info) {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
@ -85,12 +84,12 @@ describe('Image channel insertion', function() {
});
});
it('Join raw buffers to RGB', function(done) {
BluebirdPromise.all([
it('Join raw buffers to RGB', function (done) {
Promise.all([
sharp(fixtures.inputPngTestJoinChannel).toColourspace('b-w').raw().toBuffer(),
sharp(fixtures.inputPngStripesH).toColourspace('b-w').raw().toBuffer()
])
.then(function(buffers) {
.then(function (buffers) {
sharp(fixtures.inputPng)
.resize(320, 240)
.joinChannel(buffers,
@ -99,7 +98,7 @@ describe('Image channel insertion', function() {
height: 240,
channels: 1
}})
.toBuffer(function(err, data, info) {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
@ -107,19 +106,19 @@ describe('Image channel insertion', function() {
fixtures.assertSimilar(fixtures.expected('joinChannel-rgb.jpg'), data, done);
});
})
.catch(function(err) {
.catch(function (err) {
throw err;
});
});
it('Grayscale to RGBA, files, two arrays', function(done) {
it('Grayscale to RGBA, files, two arrays', function (done) {
sharp(fixtures.inputPng) // gray -> red
.resize(320, 240)
.joinChannel([fs.readFileSync(fixtures.inputPngTestJoinChannel)]) // new green channel
.joinChannel([fs.readFileSync(fixtures.inputPngStripesH), // new blue channel
fs.readFileSync(fixtures.inputPngStripesV)]) // new alpha channel
.toColourspace('srgb')
.toBuffer(function(err, data, info) {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
@ -128,24 +127,23 @@ describe('Image channel insertion', function() {
});
});
it('Invalid raw buffer description', function() {
assert.throws(function() {
sharp().joinChannel(fs.readFileSync(fixtures.inputPng),{raw:{}});
it('Invalid raw buffer description', function () {
assert.throws(function () {
sharp().joinChannel(fs.readFileSync(fixtures.inputPng), {raw: {}});
});
});
it('Invalid input', function() {
assert.throws(function() {
it('Invalid input', function () {
assert.throws(function () {
sharp(fixtures.inputJpg)
.joinChannel(1);
});
});
it('No arguments', function() {
assert.throws(function() {
it('No arguments', function () {
assert.throws(function () {
sharp(fixtures.inputJpg)
.joinChannel();
});
});
});

View File

@ -1 +0,0 @@
require('mocha-jshint')();

View File

@ -1,35 +1,35 @@
'use strict';
var fs = require('fs');
var path = require('path');
var assert = require('assert');
const fs = require('fs');
const path = require('path');
const assert = require('assert');
var async = require('async');
var rimraf = require('rimraf');
var unzip = require('unzip');
const eachLimit = require('async/eachLimit');
const rimraf = require('rimraf');
const unzip = require('unzip');
var sharp = require('../../index');
var fixtures = require('../fixtures');
const sharp = require('../../index');
const fixtures = require('../fixtures');
// Verifies all tiles in a given dz output directory are <= size
var assertDeepZoomTiles = function(directory, expectedSize, expectedLevels, done) {
const assertDeepZoomTiles = function (directory, expectedSize, expectedLevels, done) {
// Get levels
var levels = fs.readdirSync(directory);
const levels = fs.readdirSync(directory);
assert.strictEqual(expectedLevels, levels.length);
// Get tiles
var tiles = [];
levels.forEach(function(level) {
const tiles = [];
levels.forEach(function (level) {
// Verify level directory name
assert.strictEqual(true, /^[0-9]+$/.test(level));
fs.readdirSync(path.join(directory, level)).forEach(function(tile) {
fs.readdirSync(path.join(directory, level)).forEach(function (tile) {
// Verify tile file name
assert.strictEqual(true, /^[0-9]+_[0-9]+\.jpeg$/.test(tile));
tiles.push(path.join(directory, level, tile));
});
});
// Verify each tile is <= expectedSize
async.eachSeries(tiles, function(tile, done) {
sharp(tile).metadata(function(err, metadata) {
eachLimit(tiles, 8, function (tile, done) {
sharp(tile).metadata(function (err, metadata) {
if (err) {
done(err);
} else {
@ -46,11 +46,10 @@ var assertDeepZoomTiles = function(directory, expectedSize, expectedLevels, done
}, done);
};
describe('Tile', function() {
it('Valid size values pass', function() {
[1, 8192].forEach(function(size) {
assert.doesNotThrow(function() {
describe('Tile', function () {
it('Valid size values pass', function () {
[1, 8192].forEach(function (size) {
assert.doesNotThrow(function () {
sharp().tile({
size: size
});
@ -58,9 +57,9 @@ describe('Tile', function() {
});
});
it('Invalid size values fail', function() {
['zoinks', 1.1, -1, 0, 8193].forEach(function(size) {
assert.throws(function() {
it('Invalid size values fail', function () {
['zoinks', 1.1, -1, 0, 8193].forEach(function (size) {
assert.throws(function () {
sharp().tile({
size: size
});
@ -68,9 +67,9 @@ describe('Tile', function() {
});
});
it('Valid overlap values pass', function() {
[0, 8192].forEach(function(overlap) {
assert.doesNotThrow(function() {
it('Valid overlap values pass', function () {
[0, 8192].forEach(function (overlap) {
assert.doesNotThrow(function () {
sharp().tile({
size: 8192,
overlap: overlap
@ -79,9 +78,9 @@ describe('Tile', function() {
});
});
it('Invalid overlap values fail', function() {
['zoinks', 1.1, -1, 8193].forEach(function(overlap) {
assert.throws(function() {
it('Invalid overlap values fail', function () {
['zoinks', 1.1, -1, 8193].forEach(function (overlap) {
assert.throws(function () {
sharp().tile({
overlap: overlap
});
@ -89,9 +88,9 @@ describe('Tile', function() {
});
});
it('Valid container values pass', function() {
['fs', 'zip'].forEach(function(container) {
assert.doesNotThrow(function() {
it('Valid container values pass', function () {
['fs', 'zip'].forEach(function (container) {
assert.doesNotThrow(function () {
sharp().tile({
container: container
});
@ -99,9 +98,9 @@ describe('Tile', function() {
});
});
it('Invalid container values fail', function() {
['zoinks', 1].forEach(function(container) {
assert.throws(function() {
it('Invalid container values fail', function () {
['zoinks', 1].forEach(function (container) {
assert.throws(function () {
sharp().tile({
container: container
});
@ -109,9 +108,9 @@ describe('Tile', function() {
});
});
it('Valid layout values pass', function() {
['dz', 'google', 'zoomify'].forEach(function(layout) {
assert.doesNotThrow(function() {
it('Valid layout values pass', function () {
['dz', 'google', 'zoomify'].forEach(function (layout) {
assert.doesNotThrow(function () {
sharp().tile({
layout: layout
});
@ -119,9 +118,9 @@ describe('Tile', function() {
});
});
it('Invalid layout values fail', function() {
['zoinks', 1].forEach(function(layout) {
assert.throws(function() {
it('Invalid layout values fail', function () {
['zoinks', 1].forEach(function (layout) {
assert.throws(function () {
sharp().tile({
layout: layout
});
@ -129,25 +128,24 @@ describe('Tile', function() {
});
});
it('Prevent larger overlap than default size', function() {
assert.throws(function() {
it('Prevent larger overlap than default size', function () {
assert.throws(function () {
sharp().tile({overlap: 257});
});
});
it('Prevent larger overlap than provided size', function() {
assert.throws(function() {
it('Prevent larger overlap than provided size', function () {
assert.throws(function () {
sharp().tile({size: 512, overlap: 513});
});
});
if (sharp.format.dz.output.file) {
it('Deep Zoom layout', function(done) {
var directory = fixtures.path('output.dzi_files');
rimraf(directory, function() {
it('Deep Zoom layout', function (done) {
const directory = fixtures.path('output.dzi_files');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.toFile(fixtures.path('output.dzi'), function(err, info) {
.toFile(fixtures.path('output.dzi'), function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assertDeepZoomTiles(directory, 256, 13, done);
@ -155,15 +153,15 @@ describe('Tile', function() {
});
});
it('Deep Zoom layout with custom size+overlap', function(done) {
var directory = fixtures.path('output.512.dzi_files');
rimraf(directory, function() {
it('Deep Zoom layout with custom size+overlap', function (done) {
const directory = fixtures.path('output.512.dzi_files');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
size: 512,
overlap: 16
})
.toFile(fixtures.path('output.512.dzi'), function(err, info) {
.toFile(fixtures.path('output.512.dzi'), function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assertDeepZoomTiles(directory, 512 + 2 * 16, 13, done);
@ -171,17 +169,17 @@ describe('Tile', function() {
});
});
it('Zoomify layout', function(done) {
var directory = fixtures.path('output.zoomify.dzi');
rimraf(directory, function() {
it('Zoomify layout', function (done) {
const directory = fixtures.path('output.zoomify.dzi');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
layout: 'zoomify'
})
.toFile(fixtures.path('output.zoomify.dzi'), function(err, info) {
.toFile(fixtures.path('output.zoomify.dzi'), function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
fs.stat(path.join(directory, 'ImageProperties.xml'), function(err, stat) {
fs.stat(path.join(directory, 'ImageProperties.xml'), function (err, stat) {
if (err) throw err;
assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0);
@ -191,17 +189,17 @@ describe('Tile', function() {
});
});
it('Google layout', function(done) {
var directory = fixtures.path('output.google.dzi');
rimraf(directory, function() {
it('Google layout', function (done) {
const directory = fixtures.path('output.google.dzi');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
layout: 'google'
})
.toFile(directory, function(err, info) {
.toFile(directory, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
fs.stat(path.join(directory, '0', '0', '0.jpg'), function(err, stat) {
fs.stat(path.join(directory, '0', '0', '0.jpg'), function (err, stat) {
if (err) throw err;
assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0);
@ -211,23 +209,23 @@ describe('Tile', function() {
});
});
it('Write to ZIP container using file extension', function(done) {
var container = fixtures.path('output.dz.container.zip');
var extractTo = fixtures.path('output.dz.container');
var directory = path.join(extractTo, 'output.dz.container_files');
rimraf(directory, function() {
it('Write to ZIP container using file extension', function (done) {
const container = fixtures.path('output.dz.container.zip');
const extractTo = fixtures.path('output.dz.container');
const directory = path.join(extractTo, 'output.dz.container_files');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.toFile(container, function(err, info) {
.toFile(container, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
fs.stat(container, function(err, stat) {
fs.stat(container, function (err, stat) {
if (err) throw err;
assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0);
fs.createReadStream(container)
.pipe(unzip.Extract({path: path.dirname(extractTo)}))
.on('error', function(err) { throw err; })
.on('close', function() {
.on('error', function (err) { throw err; })
.on('close', function () {
assertDeepZoomTiles(directory, 256, 13, done);
});
});
@ -235,34 +233,32 @@ describe('Tile', function() {
});
});
it('Write to ZIP container using container tile option', function(done) {
var container = fixtures.path('output.dz.containeropt.zip');
var extractTo = fixtures.path('output.dz.containeropt');
var directory = path.join(extractTo, 'output.dz.containeropt_files');
rimraf(directory, function() {
it('Write to ZIP container using container tile option', function (done) {
const container = fixtures.path('output.dz.containeropt.zip');
const extractTo = fixtures.path('output.dz.containeropt');
const directory = path.join(extractTo, 'output.dz.containeropt_files');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
container: 'zip'
})
.toFile(container, function(err, info) {
.toFile(container, function (err, info) {
// Vips overrides .dzi extension to .zip used by container var below
if (err) throw err;
assert.strictEqual('dz', info.format);
fs.stat(container, function(err, stat) {
fs.stat(container, function (err, stat) {
if (err) throw err;
assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0);
fs.createReadStream(container)
.pipe(unzip.Extract({path: path.dirname(extractTo)}))
.on('error', function(err) { throw err; })
.on('close', function() {
.on('error', function (err) { throw err; })
.on('close', function () {
assertDeepZoomTiles(directory, 256, 13, done);
});
});
});
});
});
}
});