sharp 0.20.5 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,19 +3,17 @@
3
3
  ### Test environment
4
4
 
5
5
  * AWS EC2 eu-west-1 [c5.large](https://aws.amazon.com/ec2/instance-types/c5/) (2x Xeon Platinum 8124M CPU @ 3.00GHz)
6
- * Ubuntu 17.10 (hvm:ebs-ssd, 20180102, ami-0741d47e)
7
- * Node.js v8.9.4
6
+ * Ubuntu 18.04 (hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180912 ami-00035f41c82244dab)
7
+ * Node.js v10.11.0
8
8
 
9
9
  ### The contenders
10
10
 
11
- * [jimp](https://www.npmjs.com/package/jimp) v0.2.28 - Image processing in pure JavaScript. Bilinear interpolation only.
12
- * [pajk-lwip](https://www.npmjs.com/package/pajk-lwip) v0.2.0 (fork) - Wrapper around CImg that compiles dependencies from source.
13
- * [mapnik](https://www.npmjs.org/package/mapnik) v3.6.2 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
11
+ * [jimp](https://www.npmjs.com/package/jimp) v0.5.3 - Image processing in pure JavaScript. Provides bicubic interpolation.
12
+ * [mapnik](https://www.npmjs.org/package/mapnik) v4.0.1 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
14
13
  * [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) v1.9.3 - Wrapper around libmagick++, supports Buffers only.
15
14
  * [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
16
15
  * [gm](https://www.npmjs.com/package/gm) v1.23.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
17
- * [images](https://www.npmjs.com/package/images) v3.0.1 - Compiles dependencies from source. Provides bicubic interpolation.
18
- * sharp v0.19.0 / libvips v8.6.1 - Caching within libvips disabled to ensure a fair comparison.
16
+ * sharp v0.21.0 / libvips v8.7.0 - Caching within libvips disabled to ensure a fair comparison.
19
17
 
20
18
  ### The task
21
19
 
@@ -27,19 +25,14 @@ then compress to JPEG at a "quality" setting of 80.
27
25
 
28
26
  | Module | Input | Output | Ops/sec | Speed-up |
29
27
  | :----------------- | :----- | :----- | ------: | -------: |
30
- | jimp (bilinear) | buffer | buffer | 1.14 | 1.0 |
31
- | lwip | buffer | buffer | 1.86 | 1.6 |
32
- | mapnik | buffer | buffer | 3.34 | 2.9 |
33
- | imagemagick-native | buffer | buffer | 4.13 | 3.6 |
34
- | gm | buffer | buffer | 4.21 | 3.7 |
35
- | gm | file | file | 4.27 | 3.7 |
36
- | imagemagick | file | file | 4.67 | 4.1 |
37
- | images (bicubic) | file | file | 6.22 | 5.5 |
38
- | sharp | stream | stream | 24.43 | 21.4 |
39
- | sharp | file | file | 25.97 | 22.7 |
40
- | sharp | file | buffer | 26.00 | 22.8 |
41
- | sharp | buffer | file | 26.33 | 23.0 |
42
- | sharp | buffer | buffer | 26.43 | 23.1 |
28
+ | jimp | buffer | buffer | 0.71 | 1.0 |
29
+ | mapnik | buffer | buffer | 3.32 | 4.7 |
30
+ | gm | buffer | buffer | 3.97 | 5.6 |
31
+ | imagemagick-native | buffer | buffer | 4.06 | 5.7 |
32
+ | imagemagick | file | file | 4.24 | 6.0 |
33
+ | sharp | stream | stream | 25.30 | 35.6 |
34
+ | sharp | file | file | 26.17 | 36.9 |
35
+ | sharp | buffer | buffer | 26.45 | 37.3 |
43
36
 
44
37
  Greater libvips performance can be expected with caching enabled (default)
45
38
  and using 8+ core machines, especially those with larger L1/L2 CPU caches.
@@ -57,7 +50,7 @@ brew install mapnik
57
50
  ```
58
51
 
59
52
  ```sh
60
- sudo apt-get install imagemagick libmagick++-dev graphicsmagick mapnik-dev
53
+ sudo apt-get install imagemagick libmagick++-dev graphicsmagick libmapnik-dev
61
54
  ```
62
55
 
63
56
  ```sh
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
6
  const copyFileSync = require('fs-copy-file-sync');
7
+ const libvips = require('../lib/libvips');
7
8
  const npmLog = require('npmlog');
8
9
 
9
10
  if (process.platform === 'win32') {
@@ -11,8 +12,8 @@ if (process.platform === 'win32') {
11
12
  const buildReleaseDir = path.join(buildDir, 'Release');
12
13
  npmLog.info('sharp', `Creating ${buildReleaseDir}`);
13
14
  try {
14
- fs.mkdirSync(buildDir);
15
- fs.mkdirSync(buildReleaseDir);
15
+ libvips.mkdirSync(buildDir);
16
+ libvips.mkdirSync(buildReleaseDir);
16
17
  } catch (err) {}
17
18
  const vendorLibDir = path.join(__dirname, '..', 'vendor', 'lib');
18
19
  npmLog.info('sharp', `Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
@@ -9,6 +9,7 @@ const npmLog = require('npmlog');
9
9
  const semver = require('semver');
10
10
  const simpleGet = require('simple-get');
11
11
  const tar = require('tar');
12
+ const copyFileSync = require('fs-copy-file-sync');
12
13
 
13
14
  const agent = require('../lib/agent');
14
15
  const libvips = require('../lib/libvips');
@@ -17,6 +18,24 @@ const platform = require('../lib/platform');
17
18
  const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
18
19
  const distBaseUrl = process.env.SHARP_DIST_BASE_URL || `https://github.com/lovell/sharp-libvips/releases/download/v${minimumLibvipsVersion}/`;
19
20
 
21
+ const fail = function (err) {
22
+ npmLog.error('sharp', err.message);
23
+ npmLog.error('sharp', 'Please see http://sharp.pixelplumbing.com/page/install');
24
+ process.exit(1);
25
+ };
26
+
27
+ const extractTarball = function (tarPath) {
28
+ const vendorPath = path.join(__dirname, '..', 'vendor');
29
+ libvips.mkdirSync(vendorPath);
30
+ tar
31
+ .extract({
32
+ file: tarPath,
33
+ cwd: vendorPath,
34
+ strict: true
35
+ })
36
+ .catch(fail);
37
+ };
38
+
20
39
  try {
21
40
  const useGlobalLibvips = libvips.useGlobalLibvips();
22
41
  if (useGlobalLibvips) {
@@ -29,54 +48,54 @@ try {
29
48
  } else {
30
49
  // Is this arch/platform supported?
31
50
  const arch = process.env.npm_config_arch || process.arch;
32
- if (platform() === 'win32-ia32') {
51
+ const platformAndArch = platform();
52
+ if (platformAndArch === 'win32-ia32') {
33
53
  throw new Error('Windows x86 (32-bit) node.exe is not supported');
34
54
  }
35
55
  if (arch === 'ia32') {
36
- throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}\n`);
56
+ throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
37
57
  }
38
- if (detectLibc.isNonGlibcLinux) {
39
- throw new Error(`Use with ${detectLibc.family} libc requires manual installation of libvips >= ${minimumLibvipsVersion}`);
58
+ if (platformAndArch === 'freebsd-x64') {
59
+ throw new Error(`FreeBSD systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
40
60
  }
41
61
  if (detectLibc.family === detectLibc.GLIBC && detectLibc.version && semver.lt(`${detectLibc.version}.0`, '2.13.0')) {
42
62
  throw new Error(`Use with glibc version ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
43
63
  }
44
64
  // Download to per-process temporary file
45
- const tarFilename = ['libvips', minimumLibvipsVersion, platform()].join('-') + '.tar.gz';
46
- const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
47
- const tmpFile = fs.createWriteStream(tarPathTemp);
48
- const url = distBaseUrl + tarFilename;
49
- npmLog.info('sharp', `Downloading ${url}`);
50
- simpleGet({ url: url, agent: agent() }, function (err, response) {
51
- if (err) {
52
- throw err;
53
- }
54
- if (response.statusCode !== 200) {
55
- throw new Error(`Status ${response.statusCode}`);
56
- }
57
- response.pipe(tmpFile);
58
- });
59
- tmpFile.on('close', function () {
60
- const vendorPath = path.join(__dirname, '..', 'vendor');
61
- fs.mkdirSync(vendorPath);
62
- tar
63
- .extract({
64
- file: tarPathTemp,
65
- cwd: vendorPath,
66
- strict: true
67
- })
68
- .then(function () {
65
+ const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.gz';
66
+ const tarPathCache = path.join(libvips.cachePath(), tarFilename);
67
+ if (fs.existsSync(tarPathCache)) {
68
+ npmLog.info('sharp', `Using cached ${tarPathCache}`);
69
+ extractTarball(tarPathCache);
70
+ } else {
71
+ const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
72
+ const tmpFile = fs.createWriteStream(tarPathTemp);
73
+ const url = distBaseUrl + tarFilename;
74
+ npmLog.info('sharp', `Downloading ${url}`);
75
+ simpleGet({ url: url, agent: agent() }, function (err, response) {
76
+ if (err) {
77
+ throw err;
78
+ }
79
+ if (response.statusCode !== 200) {
80
+ throw new Error(`Status ${response.statusCode}`);
81
+ }
82
+ response.pipe(tmpFile);
83
+ });
84
+ tmpFile
85
+ .on('error', fail)
86
+ .on('close', function () {
69
87
  try {
88
+ // Attempt to rename
89
+ fs.renameSync(tarPathTemp, tarPathCache);
90
+ } catch (err) {
91
+ // Fall back to copy and unlink
92
+ copyFileSync(tarPathTemp, tarPathCache);
70
93
  fs.unlinkSync(tarPathTemp);
71
- } catch (err) {}
72
- })
73
- .catch(function (err) {
74
- throw err;
94
+ }
95
+ extractTarball(tarPathCache);
75
96
  });
76
- });
97
+ }
77
98
  }
78
99
  } catch (err) {
79
- npmLog.error('sharp', err.message);
80
- npmLog.error('sharp', 'Please see http://sharp.pixelplumbing.com/page/install');
81
- process.exit(1);
100
+ fail(err);
82
101
  }
package/lib/channel.js CHANGED
@@ -12,6 +12,23 @@ const bool = {
12
12
  eor: 'eor'
13
13
  };
14
14
 
15
+ /**
16
+ * Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
17
+ *
18
+ * @example
19
+ * sharp('rgba.png')
20
+ * .removeAlpha()
21
+ * .toFile('rgb.png', function(err, info) {
22
+ * // rgb.png is a 3 channel image without an alpha channel
23
+ * });
24
+ *
25
+ * @returns {Sharp}
26
+ */
27
+ function removeAlpha () {
28
+ this.options.removeAlpha = true;
29
+ return this;
30
+ }
31
+
15
32
  /**
16
33
  * Extract a single channel from a multi-channel image.
17
34
  *
@@ -102,6 +119,7 @@ function bandbool (boolOp) {
102
119
  module.exports = function (Sharp) {
103
120
  // Public instance functions
104
121
  [
122
+ removeAlpha,
105
123
  extractChannel,
106
124
  joinChannel,
107
125
  bandbool
package/lib/colour.js CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const deprecate = require('util').deprecate;
4
+
3
5
  const color = require('color');
4
6
  const is = require('./is');
5
7
 
@@ -16,25 +18,20 @@ const colourspace = {
16
18
  };
17
19
 
18
20
  /**
19
- * Set the background for the `embed`, `flatten` and `extend` operations.
20
- * The default background is `{r: 0, g: 0, b: 0, alpha: 1}`, black without transparency.
21
- *
22
- * Delegates to the _color_ module, which can throw an Error
23
- * but is liberal in what it accepts, clipping values to sensible min/max.
24
- * The alpha value is a float between `0` (transparent) and `1` (opaque).
25
- *
26
- * @param {String|Object} rgba - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
27
- * @returns {Sharp}
28
- * @throws {Error} Invalid parameter
21
+ * @deprecated
22
+ * @private
29
23
  */
30
24
  function background (rgba) {
31
25
  const colour = color(rgba);
32
- this.options.background = [
26
+ const background = [
33
27
  colour.red(),
34
28
  colour.green(),
35
29
  colour.blue(),
36
30
  Math.round(colour.alpha() * 255)
37
31
  ];
32
+ this.options.resizeBackground = background;
33
+ this.options.extendBackground = background;
34
+ this.options.flattenBackground = background.slice(0, 3);
38
35
  return this;
39
36
  }
40
37
 
@@ -80,7 +77,7 @@ function grayscale (grayscale) {
80
77
  /**
81
78
  * Set the output colourspace.
82
79
  * By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
83
- * @param {String} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
80
+ * @param {String} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
84
81
  * @returns {Sharp}
85
82
  * @throws {Error} Invalid parameters
86
83
  */
@@ -102,23 +99,45 @@ function toColorspace (colorspace) {
102
99
  return this.toColourspace(colorspace);
103
100
  }
104
101
 
102
+ /**
103
+ * Update a colour attribute of the this.options Object.
104
+ * @private
105
+ * @param {String} key
106
+ * @param {String|Object} val
107
+ * @throws {Error} Invalid key
108
+ */
109
+ function _setColourOption (key, val) {
110
+ if (is.object(val) || is.string(val)) {
111
+ const colour = color(val);
112
+ this.options[key] = [
113
+ colour.red(),
114
+ colour.green(),
115
+ colour.blue(),
116
+ Math.round(colour.alpha() * 255)
117
+ ];
118
+ }
119
+ }
120
+
105
121
  /**
106
122
  * Decorate the Sharp prototype with colour-related functions.
107
123
  * @private
108
124
  */
109
125
  module.exports = function (Sharp) {
110
- // Public instance functions
111
126
  [
112
- background,
127
+ // Public
113
128
  tint,
114
129
  greyscale,
115
130
  grayscale,
116
131
  toColourspace,
117
- toColorspace
132
+ toColorspace,
133
+ // Private
134
+ _setColourOption
118
135
  ].forEach(function (f) {
119
136
  Sharp.prototype[f.name] = f;
120
137
  });
121
138
  // Class attributes
122
139
  Sharp.colourspace = colourspace;
123
140
  Sharp.colorspace = colourspace;
141
+ // Deprecated
142
+ Sharp.prototype.background = deprecate(background, 'background(background) is deprecated, use resize({ background }), extend({ background }) or flatten({ background }) instead');
124
143
  };
package/lib/composite.js CHANGED
@@ -19,8 +19,7 @@ const is = require('./is');
19
19
  * .overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
20
20
  * .sharpen()
21
21
  * .withMetadata()
22
- * .quality(90)
23
- * .webp()
22
+ * .webp( { quality: 90 } )
24
23
  * .toBuffer()
25
24
  * .then(function(outputBuffer) {
26
25
  * // outputBuffer contains upside down, 300px wide, alpha channel flattened
@@ -35,7 +34,7 @@ const is = require('./is');
35
34
  * @param {Number} [options.left] - the pixel offset from the left edge.
36
35
  * @param {Boolean} [options.tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
37
36
  * @param {Boolean} [options.cutout=false] - set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another.
38
- * @param {Number} [options.density=72] - integral number representing the DPI for vector overlay image.
37
+ * @param {Number} [options.density=72] - number representing the DPI for vector overlay image.
39
38
  * @param {Object} [options.raw] - describes overlay when using raw pixel data.
40
39
  * @param {Number} [options.raw.width]
41
40
  * @param {Number} [options.raw.height]
@@ -4,42 +4,10 @@ const path = require('path');
4
4
  const util = require('util');
5
5
  const stream = require('stream');
6
6
  const events = require('events');
7
- const semver = require('semver');
8
7
  const is = require('./is');
9
- const platform = require('./platform');
10
- const sharp = require('../build/Release/sharp.node');
11
8
 
12
- // Vendor platform
13
- (function () {
14
- let vendorPlatformId;
15
- try {
16
- vendorPlatformId = require('../vendor/platform.json');
17
- } catch (err) {
18
- return;
19
- }
20
- const currentPlatformId = platform();
21
- /* istanbul ignore if */
22
- if (currentPlatformId !== vendorPlatformId) {
23
- throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm rebuild'.`);
24
- }
25
- })();
26
-
27
- // Versioning
28
- let versions = {
29
- vips: sharp.libvipsVersion()
30
- };
31
- (function () {
32
- // Does libvips meet minimum requirement?
33
- const libvipsVersionMin = require('../package.json').config.libvips;
34
- /* istanbul ignore if */
35
- if (semver.lt(versions.vips, libvipsVersionMin)) {
36
- throw new Error('Found libvips ' + versions.vips + ' but require at least ' + libvipsVersionMin);
37
- }
38
- // Include versions of dependencies, if present
39
- try {
40
- versions = require('../vendor/versions.json');
41
- } catch (err) {}
42
- })();
9
+ require('./libvips').hasVendoredLibvips();
10
+ const sharp = require('../build/Release/sharp.node');
43
11
 
44
12
  // Use NODE_DEBUG=sharp to enable libvips warnings
45
13
  const debuglog = util.debuglog('sharp');
@@ -81,7 +49,7 @@ const debuglog = util.debuglog('sharp');
81
49
  * width: 300,
82
50
  * height: 200,
83
51
  * channels: 4,
84
- * background: { r: 255, g: 0, b: 0, alpha: 128 }
52
+ * background: { r: 255, g: 0, b: 0, alpha: 0.5 }
85
53
  * }
86
54
  * })
87
55
  * .png()
@@ -96,7 +64,7 @@ const debuglog = util.debuglog('sharp');
96
64
  * @param {Boolean} [options.failOnError=false] - by default apply a "best effort"
97
65
  * to decode images, even if the data is corrupt or invalid. Set this flag to true
98
66
  * if you'd rather halt processing and raise an error when loading invalid images.
99
- * @param {Number} [options.density=72] - integral number representing the DPI for vector images.
67
+ * @param {Number} [options.density=72] - number representing the DPI for vector images.
100
68
  * @param {Number} [options.page=0] - page number to extract for multi-page input (GIF, TIFF)
101
69
  * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
102
70
  * @param {Number} [options.raw.width]
@@ -136,10 +104,12 @@ const Sharp = function (input, options) {
136
104
  width: -1,
137
105
  height: -1,
138
106
  canvas: 'crop',
139
- crop: 0,
140
- embed: 0,
107
+ position: 0,
108
+ resizeBackground: [0, 0, 0, 255],
141
109
  useExifOrientation: false,
142
110
  angle: 0,
111
+ rotationAngle: 0,
112
+ rotationBackground: [0, 0, 0, 255],
143
113
  rotateBeforePreExtract: false,
144
114
  flip: false,
145
115
  flop: false,
@@ -147,14 +117,15 @@ const Sharp = function (input, options) {
147
117
  extendBottom: 0,
148
118
  extendLeft: 0,
149
119
  extendRight: 0,
120
+ extendBackground: [0, 0, 0, 255],
150
121
  withoutEnlargement: false,
151
122
  kernel: 'lanczos3',
152
123
  fastShrinkOnLoad: true,
153
124
  // operations
154
- background: [0, 0, 0, 255],
155
125
  tintA: 128,
156
126
  tintB: 128,
157
127
  flatten: false,
128
+ flattenBackground: [0, 0, 0],
158
129
  negate: false,
159
130
  medianSize: 0,
160
131
  blurSigma: 0,
@@ -163,7 +134,7 @@ const Sharp = function (input, options) {
163
134
  sharpenJagged: 2,
164
135
  threshold: 0,
165
136
  thresholdGrayscale: true,
166
- trimTolerance: 0,
137
+ trimThreshold: 0,
167
138
  gamma: 0,
168
139
  greyscale: false,
169
140
  normalise: 0,
@@ -171,6 +142,7 @@ const Sharp = function (input, options) {
171
142
  booleanFileIn: '',
172
143
  joinChannelIn: [],
173
144
  extractChannel: -1,
145
+ removeAlpha: false,
174
146
  colourspace: 'srgb',
175
147
  // overlay
176
148
  overlayGravity: 0,
@@ -193,6 +165,7 @@ const Sharp = function (input, options) {
193
165
  jpegOvershootDeringing: false,
194
166
  jpegOptimiseScans: false,
195
167
  jpegOptimiseCoding: true,
168
+ jpegQuantisationTable: 0,
196
169
  pngProgressive: false,
197
170
  pngCompressionLevel: 9,
198
171
  pngAdaptiveFiltering: false,
@@ -249,7 +222,12 @@ Sharp.format = sharp.format();
249
222
  * @example
250
223
  * console.log(sharp.versions);
251
224
  */
252
- Sharp.versions = versions;
225
+ Sharp.versions = {
226
+ vips: sharp.libvipsVersion()
227
+ };
228
+ try {
229
+ Sharp.versions = require('../vendor/versions.json');
230
+ } catch (err) {}
253
231
 
254
232
  /**
255
233
  * Export constructor.
package/lib/input.js CHANGED
@@ -36,7 +36,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
36
36
  }
37
37
  // Density
38
38
  if (is.defined(inputOptions.density)) {
39
- if (is.integer(inputOptions.density) && is.inRange(inputOptions.density, 1, 2400)) {
39
+ if (is.inRange(inputOptions.density, 1, 2400)) {
40
40
  inputDescriptor.density = inputOptions.density;
41
41
  } else {
42
42
  throw new Error('Invalid density (1 to 2400) ' + inputOptions.density);
@@ -177,12 +177,15 @@ function clone () {
177
177
  * A Promises/A+ promise is returned when `callback` is not provided.
178
178
  *
179
179
  * - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
180
+ * - `size`: Total size of image in bytes, for Stream and Buffer input only
180
181
  * - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
181
182
  * - `height`: Number of pixels high (EXIF orientation is not taken into consideration)
182
- * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636)
183
+ * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636)
183
184
  * - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
184
- * - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
185
+ * - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
185
186
  * - `density`: Number of pixels per inch (DPI), if present
187
+ * - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
188
+ * - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
186
189
  * - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
187
190
  * - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
188
191
  * - `orientation`: Number value of the EXIF Orientation header, if present
@@ -264,6 +267,7 @@ function metadata (callback) {
264
267
  * - `maxX` (x-coordinate of one of the pixel where the maximum lies)
265
268
  * - `maxY` (y-coordinate of one of the pixel where the maximum lies)
266
269
  * - `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
270
+ * - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
267
271
  *
268
272
  * @example
269
273
  * const image = sharp(inputJpg);
package/lib/libvips.js CHANGED
@@ -1,17 +1,38 @@
1
1
  'use strict';
2
2
 
3
+ const fs = require('fs');
4
+ const os = require('os');
3
5
  const path = require('path');
4
6
  const spawnSync = require('child_process').spawnSync;
5
7
  const semver = require('semver');
6
8
  const platform = require('./platform');
7
9
 
8
- const minimumLibvipsVersion = process.env.npm_package_config_libvips || require('../package.json').config.libvips;
10
+ const env = process.env;
11
+ const minimumLibvipsVersion = env.npm_package_config_libvips || require('../package.json').config.libvips;
9
12
 
10
13
  const spawnSyncOptions = {
11
14
  encoding: 'utf8',
12
15
  shell: true
13
16
  };
14
17
 
18
+ const mkdirSync = function (dirPath) {
19
+ try {
20
+ fs.mkdirSync(dirPath);
21
+ } catch (err) {
22
+ if (err.code !== 'EEXIST') {
23
+ throw err;
24
+ }
25
+ }
26
+ };
27
+
28
+ const cachePath = function () {
29
+ const npmCachePath = env.npm_config_cache || (env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
30
+ mkdirSync(npmCachePath);
31
+ const libvipsCachePath = path.join(npmCachePath, '_libvips');
32
+ mkdirSync(libvipsCachePath);
33
+ return libvipsCachePath;
34
+ };
35
+
15
36
  const globalLibvipsVersion = function () {
16
37
  if (process.platform !== 'win32') {
17
38
  const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout || '';
@@ -23,21 +44,30 @@ const globalLibvipsVersion = function () {
23
44
 
24
45
  const hasVendoredLibvips = function () {
25
46
  const currentPlatformId = platform();
47
+ const vendorPath = path.join(__dirname, '..', 'vendor');
48
+ let vendorVersionId;
49
+ let vendorPlatformId;
26
50
  try {
27
- const vendorPlatformId = require(path.join(__dirname, '..', 'vendor', 'platform.json'));
51
+ vendorVersionId = require(path.join(vendorPath, 'versions.json')).vips;
52
+ vendorPlatformId = require(path.join(vendorPath, 'platform.json'));
53
+ } catch (err) {}
54
+ if (vendorVersionId && vendorVersionId !== minimumLibvipsVersion) {
55
+ throw new Error(`Found vendored libvips v${vendorVersionId} but require v${minimumLibvipsVersion}. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
56
+ }
57
+ if (vendorPlatformId) {
28
58
  if (currentPlatformId === vendorPlatformId) {
29
59
  return true;
30
60
  } else {
31
61
  throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
32
62
  }
33
- } catch (err) {}
63
+ }
34
64
  return false;
35
65
  };
36
66
 
37
67
  const pkgConfigPath = function () {
38
68
  if (process.platform !== 'win32') {
39
69
  const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
40
- return [brewPkgConfigPath.trim(), process.env.PKG_CONFIG_PATH, '/usr/local/lib/pkgconfig', '/usr/lib/pkgconfig']
70
+ return [brewPkgConfigPath.trim(), env.PKG_CONFIG_PATH, '/usr/local/lib/pkgconfig', '/usr/lib/pkgconfig']
41
71
  .filter(function (p) { return !!p; })
42
72
  .join(':');
43
73
  } else {
@@ -46,7 +76,7 @@ const pkgConfigPath = function () {
46
76
  };
47
77
 
48
78
  const useGlobalLibvips = function () {
49
- if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
79
+ if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
50
80
  return false;
51
81
  }
52
82
 
@@ -56,8 +86,10 @@ const useGlobalLibvips = function () {
56
86
 
57
87
  module.exports = {
58
88
  minimumLibvipsVersion: minimumLibvipsVersion,
89
+ cachePath: cachePath,
59
90
  globalLibvipsVersion: globalLibvipsVersion,
60
91
  hasVendoredLibvips: hasVendoredLibvips,
61
92
  pkgConfigPath: pkgConfigPath,
62
- useGlobalLibvips: useGlobalLibvips
93
+ useGlobalLibvips: useGlobalLibvips,
94
+ mkdirSync: mkdirSync
63
95
  };