sharp 0.27.0 → 0.28.1
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.
- package/README.md +15 -14
- package/binding.gyp +3 -5
- package/install/dll-copy.js +4 -4
- package/install/libvips.js +67 -31
- package/lib/channel.js +25 -7
- package/lib/colour.js +7 -0
- package/lib/constructor.js +48 -13
- package/lib/input.js +53 -13
- package/lib/is.js +10 -0
- package/lib/libvips.js +25 -3
- package/lib/operation.js +9 -4
- package/lib/output.js +142 -47
- package/lib/resize.js +42 -15
- package/lib/utility.js +17 -10
- package/package.json +13 -13
- package/src/common.cc +86 -43
- package/src/common.h +12 -4
- package/src/metadata.cc +12 -0
- package/src/metadata.h +3 -0
- package/src/operations.cc +1 -1
- package/src/pipeline.cc +54 -14
- package/src/pipeline.h +8 -3
- package/src/sharp.cc +1 -1
- package/src/utilities.cc +16 -0
- package/src/utilities.h +1 -0
package/README.md
CHANGED
|
@@ -19,6 +19,14 @@ rotation, extraction, compositing and gamma correction are available.
|
|
|
19
19
|
Most modern macOS, Windows and Linux systems running Node.js v10+
|
|
20
20
|
do not require any additional install or runtime dependencies.
|
|
21
21
|
|
|
22
|
+
## Documentation
|
|
23
|
+
|
|
24
|
+
Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
|
|
25
|
+
[installation instructions](https://sharp.pixelplumbing.com/install),
|
|
26
|
+
[API documentation](https://sharp.pixelplumbing.com/api-constructor),
|
|
27
|
+
[benchmark tests](https://sharp.pixelplumbing.com/performance) and
|
|
28
|
+
[changelog](https://sharp.pixelplumbing.com/changelog).
|
|
29
|
+
|
|
22
30
|
## Examples
|
|
23
31
|
|
|
24
32
|
```sh
|
|
@@ -43,6 +51,7 @@ sharp(inputBuffer)
|
|
|
43
51
|
sharp('input.jpg')
|
|
44
52
|
.rotate()
|
|
45
53
|
.resize(200)
|
|
54
|
+
.jpeg({ mozjpeg: true })
|
|
46
55
|
.toBuffer()
|
|
47
56
|
.then( data => { ... })
|
|
48
57
|
.catch( err => { ... });
|
|
@@ -84,25 +93,17 @@ readableStream
|
|
|
84
93
|
.pipe(writableStream);
|
|
85
94
|
```
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
[](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
|
|
89
|
-
|
|
90
|
-
### Documentation
|
|
91
|
-
|
|
92
|
-
Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
|
|
93
|
-
[installation instructions](https://sharp.pixelplumbing.com/install),
|
|
94
|
-
[API documentation](https://sharp.pixelplumbing.com/api-constructor),
|
|
95
|
-
[benchmark tests](https://sharp.pixelplumbing.com/performance) and
|
|
96
|
-
[changelog](https://sharp.pixelplumbing.com/changelog).
|
|
97
|
-
|
|
98
|
-
### Contributing
|
|
96
|
+
## Contributing
|
|
99
97
|
|
|
100
98
|
A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md)
|
|
101
99
|
covers reporting bugs, requesting features and submitting code changes.
|
|
102
100
|
|
|
103
|
-
|
|
101
|
+
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
|
102
|
+
[](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
|
|
103
|
+
|
|
104
|
+
## Licensing
|
|
104
105
|
|
|
105
|
-
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
|
|
106
|
+
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Lovell Fuller and contributors.
|
|
106
107
|
|
|
107
108
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
108
109
|
you may not use this file except in compliance with the License.
|
package/binding.gyp
CHANGED
|
@@ -140,8 +140,7 @@
|
|
|
140
140
|
'link_settings': {
|
|
141
141
|
'library_dirs': ['<(sharp_vendor_dir)/lib'],
|
|
142
142
|
'libraries': [
|
|
143
|
-
'libvips-cpp.42.dylib'
|
|
144
|
-
'libvips.42.dylib'
|
|
143
|
+
'libvips-cpp.42.dylib'
|
|
145
144
|
]
|
|
146
145
|
},
|
|
147
146
|
'xcode_settings': {
|
|
@@ -153,13 +152,12 @@
|
|
|
153
152
|
}],
|
|
154
153
|
['OS == "linux"', {
|
|
155
154
|
'defines': [
|
|
156
|
-
'_GLIBCXX_USE_CXX11_ABI=
|
|
155
|
+
'_GLIBCXX_USE_CXX11_ABI=1'
|
|
157
156
|
],
|
|
158
157
|
'link_settings': {
|
|
159
158
|
'library_dirs': ['<(sharp_vendor_dir)/lib'],
|
|
160
159
|
'libraries': [
|
|
161
|
-
'-l:libvips-cpp.so.42'
|
|
162
|
-
'-l:libvips.so.42'
|
|
160
|
+
'-l:libvips-cpp.so.42'
|
|
163
161
|
],
|
|
164
162
|
'ldflags': [
|
|
165
163
|
# Ensure runtime linking is relative to sharp.node
|
package/install/dll-copy.js
CHANGED
|
@@ -4,7 +4,6 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
6
|
const libvips = require('../lib/libvips');
|
|
7
|
-
const npmLog = require('npmlog');
|
|
8
7
|
|
|
9
8
|
const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
|
|
10
9
|
|
|
@@ -12,13 +11,13 @@ const platform = process.env.npm_config_platform || process.platform;
|
|
|
12
11
|
if (platform === 'win32') {
|
|
13
12
|
const buildDir = path.join(__dirname, '..', 'build');
|
|
14
13
|
const buildReleaseDir = path.join(buildDir, 'Release');
|
|
15
|
-
|
|
14
|
+
libvips.log(`Creating ${buildReleaseDir}`);
|
|
16
15
|
try {
|
|
17
16
|
libvips.mkdirSync(buildDir);
|
|
18
17
|
libvips.mkdirSync(buildReleaseDir);
|
|
19
18
|
} catch (err) {}
|
|
20
19
|
const vendorLibDir = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, 'lib');
|
|
21
|
-
|
|
20
|
+
libvips.log(`Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
|
|
22
21
|
try {
|
|
23
22
|
fs
|
|
24
23
|
.readdirSync(vendorLibDir)
|
|
@@ -32,6 +31,7 @@ if (platform === 'win32') {
|
|
|
32
31
|
);
|
|
33
32
|
});
|
|
34
33
|
} catch (err) {
|
|
35
|
-
|
|
34
|
+
libvips.log(err);
|
|
35
|
+
process.exit(1);
|
|
36
36
|
}
|
|
37
37
|
}
|
package/install/libvips.js
CHANGED
|
@@ -7,7 +7,6 @@ const stream = require('stream');
|
|
|
7
7
|
const zlib = require('zlib');
|
|
8
8
|
|
|
9
9
|
const detectLibc = require('detect-libc');
|
|
10
|
-
const npmLog = require('npmlog');
|
|
11
10
|
const semver = require('semver');
|
|
12
11
|
const simpleGet = require('simple-get');
|
|
13
12
|
const tarFs = require('tar-fs');
|
|
@@ -22,34 +21,50 @@ const minimumGlibcVersionByArch = {
|
|
|
22
21
|
x64: '2.17'
|
|
23
22
|
};
|
|
24
23
|
|
|
24
|
+
const hasSharpPrebuild = [
|
|
25
|
+
'darwin-x64',
|
|
26
|
+
'linux-arm64',
|
|
27
|
+
'linux-x64',
|
|
28
|
+
'linuxmusl-x64',
|
|
29
|
+
'linuxmusl-arm64',
|
|
30
|
+
'win32-ia32',
|
|
31
|
+
'win32-x64'
|
|
32
|
+
];
|
|
33
|
+
|
|
25
34
|
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
|
26
35
|
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
|
27
36
|
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
|
28
37
|
const supportsBrotli = ('BrotliDecompress' in zlib);
|
|
29
38
|
|
|
30
39
|
const fail = function (err) {
|
|
31
|
-
|
|
40
|
+
libvips.log(err);
|
|
32
41
|
if (err.code === 'EACCES') {
|
|
33
|
-
|
|
42
|
+
libvips.log('Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
|
|
34
43
|
}
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
libvips.log('Attempting to build from source via node-gyp but this may fail due to the above error');
|
|
45
|
+
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
|
|
37
46
|
process.exit(1);
|
|
38
47
|
};
|
|
39
48
|
|
|
40
|
-
const extractTarball = function (tarPath) {
|
|
49
|
+
const extractTarball = function (tarPath, platformAndArch) {
|
|
41
50
|
const vendorPath = path.join(__dirname, '..', 'vendor');
|
|
42
51
|
libvips.mkdirSync(vendorPath);
|
|
43
52
|
const versionedVendorPath = path.join(vendorPath, minimumLibvipsVersion);
|
|
44
53
|
libvips.mkdirSync(versionedVendorPath);
|
|
54
|
+
|
|
55
|
+
const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;
|
|
56
|
+
const ignore = function (name) {
|
|
57
|
+
return ignoreVendorInclude && name.includes('include/');
|
|
58
|
+
};
|
|
59
|
+
|
|
45
60
|
stream.pipeline(
|
|
46
61
|
fs.createReadStream(tarPath),
|
|
47
62
|
supportsBrotli ? new zlib.BrotliDecompress() : new zlib.Gunzip(),
|
|
48
|
-
tarFs.extract(versionedVendorPath),
|
|
63
|
+
tarFs.extract(versionedVendorPath, { ignore }),
|
|
49
64
|
function (err) {
|
|
50
65
|
if (err) {
|
|
51
66
|
if (/unexpected end of file/.test(err.message)) {
|
|
52
|
-
|
|
67
|
+
fail(new Error(`Please delete ${tarPath} as it is not a valid tarball`));
|
|
53
68
|
}
|
|
54
69
|
fail(err);
|
|
55
70
|
}
|
|
@@ -62,11 +77,11 @@ try {
|
|
|
62
77
|
|
|
63
78
|
if (useGlobalLibvips) {
|
|
64
79
|
const globalLibvipsVersion = libvips.globalLibvipsVersion();
|
|
65
|
-
|
|
66
|
-
|
|
80
|
+
libvips.log(`Detected globally-installed libvips v${globalLibvipsVersion}`);
|
|
81
|
+
libvips.log('Building from source via node-gyp');
|
|
67
82
|
process.exit(1);
|
|
68
83
|
} else if (libvips.hasVendoredLibvips()) {
|
|
69
|
-
|
|
84
|
+
libvips.log(`Using existing vendored libvips v${minimumLibvipsVersion}`);
|
|
70
85
|
} else {
|
|
71
86
|
// Is this arch/platform supported?
|
|
72
87
|
const arch = process.env.npm_config_arch || process.arch;
|
|
@@ -74,6 +89,9 @@ try {
|
|
|
74
89
|
if (arch === 'ia32' && !platformAndArch.startsWith('win32')) {
|
|
75
90
|
throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
76
91
|
}
|
|
92
|
+
if (platformAndArch === 'darwin-arm64') {
|
|
93
|
+
throw new Error("Please run 'brew install vips' to install libvips on Apple M1 (ARM64) systems");
|
|
94
|
+
}
|
|
77
95
|
if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
|
|
78
96
|
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
79
97
|
}
|
|
@@ -82,6 +100,11 @@ try {
|
|
|
82
100
|
throw new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
83
101
|
}
|
|
84
102
|
}
|
|
103
|
+
if (detectLibc.family === detectLibc.MUSL && detectLibc.version) {
|
|
104
|
+
if (semver.lt(detectLibc.version, '1.1.24')) {
|
|
105
|
+
throw new Error(`Use with musl ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
85
108
|
|
|
86
109
|
const supportedNodeVersion = process.env.npm_package_engines_node || require('../package.json').engines.node;
|
|
87
110
|
if (!semver.satisfies(process.versions.node, supportedNodeVersion)) {
|
|
@@ -94,13 +117,11 @@ try {
|
|
|
94
117
|
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.' + extension;
|
|
95
118
|
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
|
|
96
119
|
if (fs.existsSync(tarPathCache)) {
|
|
97
|
-
|
|
98
|
-
extractTarball(tarPathCache);
|
|
120
|
+
libvips.log(`Using cached ${tarPathCache}`);
|
|
121
|
+
extractTarball(tarPathCache, platformAndArch);
|
|
99
122
|
} else {
|
|
100
|
-
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
|
|
101
|
-
const tmpFile = fs.createWriteStream(tarPathTemp);
|
|
102
123
|
const url = distBaseUrl + tarFilename;
|
|
103
|
-
|
|
124
|
+
libvips.log(`Downloading ${url}`);
|
|
104
125
|
simpleGet({ url: url, agent: agent() }, function (err, response) {
|
|
105
126
|
if (err) {
|
|
106
127
|
fail(err);
|
|
@@ -109,24 +130,39 @@ try {
|
|
|
109
130
|
} else if (response.statusCode !== 200) {
|
|
110
131
|
fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
|
|
111
132
|
} else {
|
|
133
|
+
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
|
|
134
|
+
const tmpFileStream = fs.createWriteStream(tarPathTemp);
|
|
112
135
|
response
|
|
113
|
-
.on('error',
|
|
114
|
-
|
|
136
|
+
.on('error', function (err) {
|
|
137
|
+
tmpFileStream.destroy(err);
|
|
138
|
+
})
|
|
139
|
+
.on('close', function () {
|
|
140
|
+
if (!response.complete) {
|
|
141
|
+
tmpFileStream.destroy(new Error('Download incomplete (connection was terminated)'));
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
.pipe(tmpFileStream);
|
|
145
|
+
tmpFileStream
|
|
146
|
+
.on('error', function (err) {
|
|
147
|
+
// Clean up temporary file
|
|
148
|
+
try {
|
|
149
|
+
fs.unlinkSync(tarPathTemp);
|
|
150
|
+
} catch (e) {}
|
|
151
|
+
fail(err);
|
|
152
|
+
})
|
|
153
|
+
.on('close', function () {
|
|
154
|
+
try {
|
|
155
|
+
// Attempt to rename
|
|
156
|
+
fs.renameSync(tarPathTemp, tarPathCache);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
// Fall back to copy and unlink
|
|
159
|
+
fs.copyFileSync(tarPathTemp, tarPathCache);
|
|
160
|
+
fs.unlinkSync(tarPathTemp);
|
|
161
|
+
}
|
|
162
|
+
extractTarball(tarPathCache, platformAndArch);
|
|
163
|
+
});
|
|
115
164
|
}
|
|
116
165
|
});
|
|
117
|
-
tmpFile
|
|
118
|
-
.on('error', fail)
|
|
119
|
-
.on('close', function () {
|
|
120
|
-
try {
|
|
121
|
-
// Attempt to rename
|
|
122
|
-
fs.renameSync(tarPathTemp, tarPathCache);
|
|
123
|
-
} catch (err) {
|
|
124
|
-
// Fall back to copy and unlink
|
|
125
|
-
fs.copyFileSync(tarPathTemp, tarPathCache);
|
|
126
|
-
fs.unlinkSync(tarPathTemp);
|
|
127
|
-
}
|
|
128
|
-
extractTarball(tarPathCache);
|
|
129
|
-
});
|
|
130
166
|
}
|
|
131
167
|
}
|
|
132
168
|
} catch (err) {
|
package/lib/channel.js
CHANGED
|
@@ -30,21 +30,39 @@ function removeAlpha () {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* Ensure
|
|
33
|
+
* Ensure the output image has an alpha transparency channel.
|
|
34
|
+
* If missing, the added alpha channel will have the specified
|
|
35
|
+
* transparency level, defaulting to fully-opaque (1).
|
|
36
|
+
* This is a no-op if the image already has an alpha channel.
|
|
34
37
|
*
|
|
35
38
|
* @since 0.21.2
|
|
36
39
|
*
|
|
37
40
|
* @example
|
|
38
|
-
*
|
|
41
|
+
* // rgba.png will be a 4 channel image with a fully-opaque alpha channel
|
|
42
|
+
* await sharp('rgb.jpg')
|
|
39
43
|
* .ensureAlpha()
|
|
40
|
-
* .toFile('rgba.png'
|
|
41
|
-
*
|
|
42
|
-
*
|
|
44
|
+
* .toFile('rgba.png')
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // rgba is a 4 channel image with a fully-transparent alpha channel
|
|
48
|
+
* const rgba = await sharp(rgb)
|
|
49
|
+
* .ensureAlpha(0)
|
|
50
|
+
* .toBuffer();
|
|
43
51
|
*
|
|
52
|
+
* @param {number} [alpha=1] - alpha transparency level (0=fully-transparent, 1=fully-opaque)
|
|
44
53
|
* @returns {Sharp}
|
|
54
|
+
* @throws {Error} Invalid alpha transparency level
|
|
45
55
|
*/
|
|
46
|
-
function ensureAlpha () {
|
|
47
|
-
|
|
56
|
+
function ensureAlpha (alpha) {
|
|
57
|
+
if (is.defined(alpha)) {
|
|
58
|
+
if (is.number(alpha) && is.inRange(alpha, 0, 1)) {
|
|
59
|
+
this.options.ensureAlpha = alpha;
|
|
60
|
+
} else {
|
|
61
|
+
throw is.invalidParameterError('alpha', 'number between 0 and 1', alpha);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
this.options.ensureAlpha = 1;
|
|
65
|
+
}
|
|
48
66
|
return this;
|
|
49
67
|
}
|
|
50
68
|
|
package/lib/colour.js
CHANGED
|
@@ -57,6 +57,13 @@ function grayscale (grayscale) {
|
|
|
57
57
|
/**
|
|
58
58
|
* Set the output colourspace.
|
|
59
59
|
* By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // Output 16 bits per pixel RGB
|
|
63
|
+
* await sharp(input)
|
|
64
|
+
* .toColourspace('rgb16')
|
|
65
|
+
* .toFile('16-bpp.png')
|
|
66
|
+
*
|
|
60
67
|
* @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)
|
|
61
68
|
* @returns {Sharp}
|
|
62
69
|
* @throws {Error} Invalid parameters
|
package/lib/constructor.js
CHANGED
|
@@ -18,12 +18,10 @@ try {
|
|
|
18
18
|
help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
|
|
19
19
|
} else if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
|
|
20
20
|
help.push('- Run "brew update && brew upgrade vips"');
|
|
21
|
-
} else if (/Cannot find module/.test(err.message)) {
|
|
22
|
-
help.push('- Run "npm rebuild --verbose sharp" and look for errors');
|
|
23
21
|
} else {
|
|
24
22
|
help.push(
|
|
25
23
|
'- Remove the "node_modules/sharp" directory then run',
|
|
26
|
-
' "npm install --ignore-scripts=false --verbose" and look for errors'
|
|
24
|
+
' "npm install --ignore-scripts=false --verbose sharp" and look for errors'
|
|
27
25
|
);
|
|
28
26
|
}
|
|
29
27
|
help.push(
|
|
@@ -90,8 +88,37 @@ const debuglog = util.debuglog('sharp');
|
|
|
90
88
|
* // Convert an animated GIF to an animated WebP
|
|
91
89
|
* await sharp('in.gif', { animated: true }).toFile('out.webp');
|
|
92
90
|
*
|
|
93
|
-
* @
|
|
94
|
-
*
|
|
91
|
+
* @example
|
|
92
|
+
* // Read a raw array of pixels and save it to a png
|
|
93
|
+
* const input = Uint8Array.from([255, 255, 255, 0, 0, 0]); // or Uint8ClampedArray
|
|
94
|
+
* const image = sharp(input, {
|
|
95
|
+
* // because the input does not contain its dimensions or how many channels it has
|
|
96
|
+
* // we need to specify it in the constructor options
|
|
97
|
+
* raw: {
|
|
98
|
+
* width: 2,
|
|
99
|
+
* height: 1,
|
|
100
|
+
* channels: 3
|
|
101
|
+
* }
|
|
102
|
+
* });
|
|
103
|
+
* await image.toFile('my-two-pixels.png');
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* // Generate RGB Gaussian noise
|
|
107
|
+
* await sharp({
|
|
108
|
+
* create: {
|
|
109
|
+
* width: 300,
|
|
110
|
+
* height: 200,
|
|
111
|
+
* channels: 3,
|
|
112
|
+
* noise: {
|
|
113
|
+
* type: 'gaussian',
|
|
114
|
+
* mean: 128,
|
|
115
|
+
* sigma: 30
|
|
116
|
+
* }
|
|
117
|
+
* }
|
|
118
|
+
* }).toFile('noise.png');
|
|
119
|
+
*
|
|
120
|
+
* @param {(Buffer|Uint8Array|Uint8ClampedArray|string)} [input] - if present, can be
|
|
121
|
+
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
|
95
122
|
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
|
96
123
|
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
|
97
124
|
* @param {Object} [options] - if present, is an Object with optional attributes.
|
|
@@ -105,17 +132,22 @@ const debuglog = util.debuglog('sharp');
|
|
|
105
132
|
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
|
|
106
133
|
* @param {number} [options.pages=1] - number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages.
|
|
107
134
|
* @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based.
|
|
135
|
+
* @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
|
|
108
136
|
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
|
109
137
|
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
|
|
110
138
|
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
|
111
|
-
* @param {number} [options.raw.width]
|
|
112
|
-
* @param {number} [options.raw.height]
|
|
113
|
-
* @param {number} [options.raw.channels] - 1
|
|
139
|
+
* @param {number} [options.raw.width] - integral number of pixels wide.
|
|
140
|
+
* @param {number} [options.raw.height] - integral number of pixels high.
|
|
141
|
+
* @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
|
|
114
142
|
* @param {Object} [options.create] - describes a new image to be created.
|
|
115
|
-
* @param {number} [options.create.width]
|
|
116
|
-
* @param {number} [options.create.height]
|
|
117
|
-
* @param {number} [options.create.channels] - 3
|
|
143
|
+
* @param {number} [options.create.width] - integral number of pixels wide.
|
|
144
|
+
* @param {number} [options.create.height] - integral number of pixels high.
|
|
145
|
+
* @param {number} [options.create.channels] - integral number of channels, either 3 (RGB) or 4 (RGBA).
|
|
118
146
|
* @param {string|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
|
147
|
+
* @param {Object} [options.create.noise] - describes a noise to be created.
|
|
148
|
+
* @param {string} [options.create.noise.type] - type of generated noise, currently only `gaussian` is supported.
|
|
149
|
+
* @param {number} [options.create.noise.mean] - mean of pixels in generated noise.
|
|
150
|
+
* @param {number} [options.create.noise.sigma] - standard deviation of pixels in generated noise.
|
|
119
151
|
* @returns {Sharp}
|
|
120
152
|
* @throws {Error} Invalid parameters
|
|
121
153
|
*/
|
|
@@ -190,7 +222,7 @@ const Sharp = function (input, options) {
|
|
|
190
222
|
joinChannelIn: [],
|
|
191
223
|
extractChannel: -1,
|
|
192
224
|
removeAlpha: false,
|
|
193
|
-
ensureAlpha:
|
|
225
|
+
ensureAlpha: -1,
|
|
194
226
|
colourspace: 'srgb',
|
|
195
227
|
composite: [],
|
|
196
228
|
// output
|
|
@@ -200,6 +232,7 @@ const Sharp = function (input, options) {
|
|
|
200
232
|
withMetadata: false,
|
|
201
233
|
withMetadataOrientation: -1,
|
|
202
234
|
withMetadataIcc: '',
|
|
235
|
+
withMetadataStrs: {},
|
|
203
236
|
resolveWithObject: false,
|
|
204
237
|
// output format
|
|
205
238
|
jpegQuality: 80,
|
|
@@ -211,7 +244,7 @@ const Sharp = function (input, options) {
|
|
|
211
244
|
jpegOptimiseCoding: true,
|
|
212
245
|
jpegQuantisationTable: 0,
|
|
213
246
|
pngProgressive: false,
|
|
214
|
-
pngCompressionLevel:
|
|
247
|
+
pngCompressionLevel: 6,
|
|
215
248
|
pngAdaptiveFiltering: false,
|
|
216
249
|
pngPalette: false,
|
|
217
250
|
pngQuality: 100,
|
|
@@ -237,6 +270,7 @@ const Sharp = function (input, options) {
|
|
|
237
270
|
heifLossless: false,
|
|
238
271
|
heifCompression: 'av1',
|
|
239
272
|
heifSpeed: 5,
|
|
273
|
+
heifChromaSubsampling: '4:2:0',
|
|
240
274
|
tileSize: 256,
|
|
241
275
|
tileOverlap: 0,
|
|
242
276
|
tileContainer: 'fs',
|
|
@@ -247,6 +281,7 @@ const Sharp = function (input, options) {
|
|
|
247
281
|
tileSkipBlanks: -1,
|
|
248
282
|
tileBackground: [255, 255, 255, 255],
|
|
249
283
|
tileCentre: false,
|
|
284
|
+
tileId: 'https://example.com/iiif',
|
|
250
285
|
linearA: 1,
|
|
251
286
|
linearB: 0,
|
|
252
287
|
// Function to notify of libvips warnings
|
package/lib/input.js
CHANGED
|
@@ -9,9 +9,9 @@ const sharp = require('../build/Release/sharp.node');
|
|
|
9
9
|
* @private
|
|
10
10
|
*/
|
|
11
11
|
function _inputOptionsFromObject (obj) {
|
|
12
|
-
const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages } = obj;
|
|
13
|
-
return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages].some(is.defined)
|
|
14
|
-
? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages }
|
|
12
|
+
const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd } = obj;
|
|
13
|
+
return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined)
|
|
14
|
+
? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd }
|
|
15
15
|
: undefined;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -31,6 +31,9 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
31
31
|
} else if (is.buffer(input)) {
|
|
32
32
|
// Buffer
|
|
33
33
|
inputDescriptor.buffer = input;
|
|
34
|
+
} else if (is.uint8Array(input)) {
|
|
35
|
+
// Uint8Array or Uint8ClampedArray
|
|
36
|
+
inputDescriptor.buffer = Buffer.from(input.buffer);
|
|
34
37
|
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
|
35
38
|
// Plain Object descriptor, e.g. create
|
|
36
39
|
inputOptions = input;
|
|
@@ -128,28 +131,64 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
128
131
|
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
|
|
129
132
|
}
|
|
130
133
|
}
|
|
134
|
+
// Sub Image File Directory (TIFF)
|
|
135
|
+
if (is.defined(inputOptions.subifd)) {
|
|
136
|
+
if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
|
|
137
|
+
inputDescriptor.subifd = inputOptions.subifd;
|
|
138
|
+
} else {
|
|
139
|
+
throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
131
142
|
// Create new image
|
|
132
143
|
if (is.defined(inputOptions.create)) {
|
|
133
144
|
if (
|
|
134
145
|
is.object(inputOptions.create) &&
|
|
135
146
|
is.integer(inputOptions.create.width) && inputOptions.create.width > 0 &&
|
|
136
147
|
is.integer(inputOptions.create.height) && inputOptions.create.height > 0 &&
|
|
137
|
-
is.integer(inputOptions.create.channels)
|
|
138
|
-
is.defined(inputOptions.create.background)
|
|
148
|
+
is.integer(inputOptions.create.channels)
|
|
139
149
|
) {
|
|
140
150
|
inputDescriptor.createWidth = inputOptions.create.width;
|
|
141
151
|
inputDescriptor.createHeight = inputOptions.create.height;
|
|
142
152
|
inputDescriptor.createChannels = inputOptions.create.channels;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
153
|
+
// Noise
|
|
154
|
+
if (is.defined(inputOptions.create.noise)) {
|
|
155
|
+
if (!is.object(inputOptions.create.noise)) {
|
|
156
|
+
throw new Error('Expected noise to be an object');
|
|
157
|
+
}
|
|
158
|
+
if (!is.inArray(inputOptions.create.noise.type, ['gaussian'])) {
|
|
159
|
+
throw new Error('Only gaussian noise is supported at the moment');
|
|
160
|
+
}
|
|
161
|
+
if (!is.inRange(inputOptions.create.channels, 1, 4)) {
|
|
162
|
+
throw is.invalidParameterError('create.channels', 'number between 1 and 4', inputOptions.create.channels);
|
|
163
|
+
}
|
|
164
|
+
inputDescriptor.createNoiseType = inputOptions.create.noise.type;
|
|
165
|
+
if (is.number(inputOptions.create.noise.mean) && is.inRange(inputOptions.create.noise.mean, 0, 10000)) {
|
|
166
|
+
inputDescriptor.createNoiseMean = inputOptions.create.noise.mean;
|
|
167
|
+
} else {
|
|
168
|
+
throw is.invalidParameterError('create.noise.mean', 'number between 0 and 10000', inputOptions.create.noise.mean);
|
|
169
|
+
}
|
|
170
|
+
if (is.number(inputOptions.create.noise.sigma) && is.inRange(inputOptions.create.noise.sigma, 0, 10000)) {
|
|
171
|
+
inputDescriptor.createNoiseSigma = inputOptions.create.noise.sigma;
|
|
172
|
+
} else {
|
|
173
|
+
throw is.invalidParameterError('create.noise.sigma', 'number between 0 and 10000', inputOptions.create.noise.sigma);
|
|
174
|
+
}
|
|
175
|
+
} else if (is.defined(inputOptions.create.background)) {
|
|
176
|
+
if (!is.inRange(inputOptions.create.channels, 3, 4)) {
|
|
177
|
+
throw is.invalidParameterError('create.channels', 'number between 3 and 4', inputOptions.create.channels);
|
|
178
|
+
}
|
|
179
|
+
const background = color(inputOptions.create.background);
|
|
180
|
+
inputDescriptor.createBackground = [
|
|
181
|
+
background.red(),
|
|
182
|
+
background.green(),
|
|
183
|
+
background.blue(),
|
|
184
|
+
Math.round(background.alpha() * 255)
|
|
185
|
+
];
|
|
186
|
+
} else {
|
|
187
|
+
throw new Error('Expected valid noise or background to create a new input image');
|
|
188
|
+
}
|
|
150
189
|
delete inputDescriptor.buffer;
|
|
151
190
|
} else {
|
|
152
|
-
throw new Error('Expected width, height
|
|
191
|
+
throw new Error('Expected valid width, height and channels to create a new input image');
|
|
153
192
|
}
|
|
154
193
|
}
|
|
155
194
|
} else if (is.defined(inputOptions)) {
|
|
@@ -224,6 +263,7 @@ function _isStreamInput () {
|
|
|
224
263
|
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
|
225
264
|
* - `pagePrimary`: Number of the primary page in a HEIF image
|
|
226
265
|
* - `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
|
|
266
|
+
* - `subifds`: Number of Sub Image File Directories in an OME-TIFF image
|
|
227
267
|
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
|
228
268
|
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
|
229
269
|
* - `orientation`: Number value of the EXIF Orientation header, if present
|
package/lib/is.js
CHANGED
|
@@ -48,6 +48,15 @@ const buffer = function (val) {
|
|
|
48
48
|
return val instanceof Buffer;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Is this value a Uint8Array or Uint8ClampedArray object?
|
|
53
|
+
* @private
|
|
54
|
+
*/
|
|
55
|
+
const uint8Array = function (val) {
|
|
56
|
+
// allow both since Uint8ClampedArray simply clamps the values between 0-255
|
|
57
|
+
return val instanceof Uint8Array || val instanceof Uint8ClampedArray;
|
|
58
|
+
};
|
|
59
|
+
|
|
51
60
|
/**
|
|
52
61
|
* Is this value a non-empty string?
|
|
53
62
|
* @private
|
|
@@ -110,6 +119,7 @@ module.exports = {
|
|
|
110
119
|
fn: fn,
|
|
111
120
|
bool: bool,
|
|
112
121
|
buffer: buffer,
|
|
122
|
+
uint8Array: uint8Array,
|
|
113
123
|
string: string,
|
|
114
124
|
number: number,
|
|
115
125
|
integer: integer,
|
package/lib/libvips.js
CHANGED
|
@@ -37,10 +37,28 @@ const cachePath = function () {
|
|
|
37
37
|
return libvipsCachePath;
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
const log = function (item) {
|
|
41
|
+
if (item instanceof Error) {
|
|
42
|
+
console.error(`sharp: Installation error: ${item.message}`);
|
|
43
|
+
} else {
|
|
44
|
+
console.log(`sharp: ${item}`);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const isRosetta = function () {
|
|
49
|
+
/* istanbul ignore next */
|
|
50
|
+
if (process.platform === 'darwin' && process.arch === 'x64') {
|
|
51
|
+
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
|
|
52
|
+
return (translated || '').trim() === 'sysctl.proc_translated: 1';
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
};
|
|
56
|
+
|
|
40
57
|
const globalLibvipsVersion = function () {
|
|
41
58
|
if (process.platform !== 'win32') {
|
|
42
|
-
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout
|
|
43
|
-
|
|
59
|
+
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
|
|
60
|
+
/* istanbul ignore next */
|
|
61
|
+
return (globalLibvipsVersion || '').trim();
|
|
44
62
|
} else {
|
|
45
63
|
return '';
|
|
46
64
|
}
|
|
@@ -81,7 +99,10 @@ const useGlobalLibvips = function () {
|
|
|
81
99
|
if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
|
|
82
100
|
return false;
|
|
83
101
|
}
|
|
84
|
-
|
|
102
|
+
/* istanbul ignore next */
|
|
103
|
+
if (isRosetta()) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
85
106
|
const globalVipsVersion = globalLibvipsVersion();
|
|
86
107
|
return !!globalVipsVersion && /* istanbul ignore next */
|
|
87
108
|
semver.gte(globalVipsVersion, minimumLibvipsVersion);
|
|
@@ -91,6 +112,7 @@ module.exports = {
|
|
|
91
112
|
minimumLibvipsVersion,
|
|
92
113
|
minimumLibvipsVersionLabelled,
|
|
93
114
|
cachePath,
|
|
115
|
+
log,
|
|
94
116
|
globalLibvipsVersion,
|
|
95
117
|
hasVendoredLibvips,
|
|
96
118
|
pkgConfigPath,
|