sharp 0.27.1 → 0.28.2
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 +14 -13
- package/binding.gyp +3 -5
- package/install/dll-copy.js +4 -4
- package/install/libvips.js +85 -38
- package/lib/agent.js +1 -1
- package/lib/channel.js +25 -7
- package/lib/constructor.js +10 -6
- package/lib/input.js +19 -3
- package/lib/libvips.js +27 -4
- package/lib/operation.js +9 -4
- package/lib/output.js +129 -43
- package/lib/resize.js +42 -15
- package/lib/utility.js +17 -10
- package/package.json +14 -13
- package/src/common.cc +25 -11
- package/src/common.h +8 -3
- package/src/metadata.cc +6 -0
- package/src/metadata.h +2 -0
- package/src/operations.cc +1 -1
- package/src/pipeline.cc +42 -9
- package/src/pipeline.h +8 -3
- package/src/sharp.cc +1 -0
- 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,23 +93,15 @@ 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
106
|
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Lovell Fuller and contributors.
|
|
106
107
|
|
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,8 +7,8 @@ const stream = require('stream');
|
|
|
7
7
|
const zlib = require('zlib');
|
|
8
8
|
|
|
9
9
|
const detectLibc = require('detect-libc');
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const semverLessThan = require('semver/functions/lt');
|
|
11
|
+
const semverSatisfies = require('semver/functions/satisfies');
|
|
12
12
|
const simpleGet = require('simple-get');
|
|
13
13
|
const tarFs = require('tar-fs');
|
|
14
14
|
|
|
@@ -22,34 +22,59 @@ const minimumGlibcVersionByArch = {
|
|
|
22
22
|
x64: '2.17'
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
const hasSharpPrebuild = [
|
|
26
|
+
'darwin-x64',
|
|
27
|
+
'linux-arm64',
|
|
28
|
+
'linux-x64',
|
|
29
|
+
'linuxmusl-x64',
|
|
30
|
+
'linuxmusl-arm64',
|
|
31
|
+
'win32-ia32',
|
|
32
|
+
'win32-x64'
|
|
33
|
+
];
|
|
34
|
+
|
|
25
35
|
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
|
26
36
|
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
|
27
37
|
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
|
28
38
|
const supportsBrotli = ('BrotliDecompress' in zlib);
|
|
39
|
+
const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
|
|
29
40
|
|
|
30
41
|
const fail = function (err) {
|
|
31
|
-
|
|
42
|
+
libvips.log(err);
|
|
32
43
|
if (err.code === 'EACCES') {
|
|
33
|
-
|
|
44
|
+
libvips.log('Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
|
|
34
45
|
}
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
libvips.log('Attempting to build from source via node-gyp but this may fail due to the above error');
|
|
47
|
+
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
|
|
37
48
|
process.exit(1);
|
|
38
49
|
};
|
|
39
50
|
|
|
40
|
-
const
|
|
51
|
+
const handleError = function (err) {
|
|
52
|
+
if (installationForced) {
|
|
53
|
+
libvips.log(`Installation warning: ${err.message}`);
|
|
54
|
+
} else {
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const extractTarball = function (tarPath, platformAndArch) {
|
|
41
60
|
const vendorPath = path.join(__dirname, '..', 'vendor');
|
|
42
61
|
libvips.mkdirSync(vendorPath);
|
|
43
62
|
const versionedVendorPath = path.join(vendorPath, minimumLibvipsVersion);
|
|
44
63
|
libvips.mkdirSync(versionedVendorPath);
|
|
64
|
+
|
|
65
|
+
const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;
|
|
66
|
+
const ignore = function (name) {
|
|
67
|
+
return ignoreVendorInclude && name.includes('include/');
|
|
68
|
+
};
|
|
69
|
+
|
|
45
70
|
stream.pipeline(
|
|
46
71
|
fs.createReadStream(tarPath),
|
|
47
72
|
supportsBrotli ? new zlib.BrotliDecompress() : new zlib.Gunzip(),
|
|
48
|
-
tarFs.extract(versionedVendorPath),
|
|
73
|
+
tarFs.extract(versionedVendorPath, { ignore }),
|
|
49
74
|
function (err) {
|
|
50
75
|
if (err) {
|
|
51
76
|
if (/unexpected end of file/.test(err.message)) {
|
|
52
|
-
|
|
77
|
+
fail(new Error(`Please delete ${tarPath} as it is not a valid tarball`));
|
|
53
78
|
}
|
|
54
79
|
fail(err);
|
|
55
80
|
}
|
|
@@ -62,11 +87,11 @@ try {
|
|
|
62
87
|
|
|
63
88
|
if (useGlobalLibvips) {
|
|
64
89
|
const globalLibvipsVersion = libvips.globalLibvipsVersion();
|
|
65
|
-
|
|
66
|
-
|
|
90
|
+
libvips.log(`Detected globally-installed libvips v${globalLibvipsVersion}`);
|
|
91
|
+
libvips.log('Building from source via node-gyp');
|
|
67
92
|
process.exit(1);
|
|
68
93
|
} else if (libvips.hasVendoredLibvips()) {
|
|
69
|
-
|
|
94
|
+
libvips.log(`Using existing vendored libvips v${minimumLibvipsVersion}`);
|
|
70
95
|
} else {
|
|
71
96
|
// Is this arch/platform supported?
|
|
72
97
|
const arch = process.env.npm_config_arch || process.arch;
|
|
@@ -74,18 +99,27 @@ try {
|
|
|
74
99
|
if (arch === 'ia32' && !platformAndArch.startsWith('win32')) {
|
|
75
100
|
throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
76
101
|
}
|
|
102
|
+
if (platformAndArch === 'darwin-arm64') {
|
|
103
|
+
throw new Error("Please run 'brew install vips' to install libvips on Apple M1 (ARM64) systems");
|
|
104
|
+
}
|
|
77
105
|
if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
|
|
78
106
|
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
79
107
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
108
|
+
// Linux libc version check
|
|
109
|
+
if (detectLibc.family === detectLibc.GLIBC && detectLibc.version && minimumGlibcVersionByArch[arch]) {
|
|
110
|
+
if (semverLessThan(`${detectLibc.version}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
|
|
111
|
+
handleError(new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
|
83
112
|
}
|
|
84
113
|
}
|
|
85
|
-
|
|
114
|
+
if (detectLibc.family === detectLibc.MUSL && detectLibc.version) {
|
|
115
|
+
if (semverLessThan(detectLibc.version, '1.1.24')) {
|
|
116
|
+
handleError(new Error(`Use with musl ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Node.js minimum version check
|
|
86
120
|
const supportedNodeVersion = process.env.npm_package_engines_node || require('../package.json').engines.node;
|
|
87
|
-
if (!
|
|
88
|
-
|
|
121
|
+
if (!semverSatisfies(process.versions.node, supportedNodeVersion)) {
|
|
122
|
+
handleError(new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`));
|
|
89
123
|
}
|
|
90
124
|
|
|
91
125
|
const extension = supportsBrotli ? 'br' : 'gz';
|
|
@@ -94,13 +128,11 @@ try {
|
|
|
94
128
|
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.' + extension;
|
|
95
129
|
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
|
|
96
130
|
if (fs.existsSync(tarPathCache)) {
|
|
97
|
-
|
|
98
|
-
extractTarball(tarPathCache);
|
|
131
|
+
libvips.log(`Using cached ${tarPathCache}`);
|
|
132
|
+
extractTarball(tarPathCache, platformAndArch);
|
|
99
133
|
} else {
|
|
100
|
-
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
|
|
101
|
-
const tmpFile = fs.createWriteStream(tarPathTemp);
|
|
102
134
|
const url = distBaseUrl + tarFilename;
|
|
103
|
-
|
|
135
|
+
libvips.log(`Downloading ${url}`);
|
|
104
136
|
simpleGet({ url: url, agent: agent() }, function (err, response) {
|
|
105
137
|
if (err) {
|
|
106
138
|
fail(err);
|
|
@@ -109,24 +141,39 @@ try {
|
|
|
109
141
|
} else if (response.statusCode !== 200) {
|
|
110
142
|
fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
|
|
111
143
|
} else {
|
|
144
|
+
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
|
|
145
|
+
const tmpFileStream = fs.createWriteStream(tarPathTemp);
|
|
112
146
|
response
|
|
113
|
-
.on('error',
|
|
114
|
-
|
|
147
|
+
.on('error', function (err) {
|
|
148
|
+
tmpFileStream.destroy(err);
|
|
149
|
+
})
|
|
150
|
+
.on('close', function () {
|
|
151
|
+
if (!response.complete) {
|
|
152
|
+
tmpFileStream.destroy(new Error('Download incomplete (connection was terminated)'));
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
.pipe(tmpFileStream);
|
|
156
|
+
tmpFileStream
|
|
157
|
+
.on('error', function (err) {
|
|
158
|
+
// Clean up temporary file
|
|
159
|
+
try {
|
|
160
|
+
fs.unlinkSync(tarPathTemp);
|
|
161
|
+
} catch (e) {}
|
|
162
|
+
fail(err);
|
|
163
|
+
})
|
|
164
|
+
.on('close', function () {
|
|
165
|
+
try {
|
|
166
|
+
// Attempt to rename
|
|
167
|
+
fs.renameSync(tarPathTemp, tarPathCache);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
// Fall back to copy and unlink
|
|
170
|
+
fs.copyFileSync(tarPathTemp, tarPathCache);
|
|
171
|
+
fs.unlinkSync(tarPathTemp);
|
|
172
|
+
}
|
|
173
|
+
extractTarball(tarPathCache, platformAndArch);
|
|
174
|
+
});
|
|
115
175
|
}
|
|
116
176
|
});
|
|
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
177
|
}
|
|
131
178
|
}
|
|
132
179
|
} catch (err) {
|
package/lib/agent.js
CHANGED
|
@@ -25,7 +25,7 @@ module.exports = function () {
|
|
|
25
25
|
? tunnelAgent.httpsOverHttps
|
|
26
26
|
: tunnelAgent.httpsOverHttp;
|
|
27
27
|
const proxyAuth = proxy.username && proxy.password
|
|
28
|
-
? `${proxy.username}:${proxy.password}`
|
|
28
|
+
? `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`
|
|
29
29
|
: null;
|
|
30
30
|
return tunnel({
|
|
31
31
|
proxy: {
|
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/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(
|
|
@@ -117,7 +115,7 @@ const debuglog = util.debuglog('sharp');
|
|
|
117
115
|
* sigma: 30
|
|
118
116
|
* }
|
|
119
117
|
* }
|
|
120
|
-
* }.toFile('noise.png');
|
|
118
|
+
* }).toFile('noise.png');
|
|
121
119
|
*
|
|
122
120
|
* @param {(Buffer|Uint8Array|Uint8ClampedArray|string)} [input] - if present, can be
|
|
123
121
|
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
|
@@ -134,12 +132,15 @@ const debuglog = util.debuglog('sharp');
|
|
|
134
132
|
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
|
|
135
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.
|
|
136
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.
|
|
137
136
|
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
|
138
137
|
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
|
|
139
138
|
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
|
140
139
|
* @param {number} [options.raw.width] - integral number of pixels wide.
|
|
141
140
|
* @param {number} [options.raw.height] - integral number of pixels high.
|
|
142
141
|
* @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
|
|
142
|
+
* @param {boolean} [options.raw.premultiplied] - specifies that the raw input has already been premultiplied, set to `true`
|
|
143
|
+
* to avoid sharp premultiplying the image. (optional, default `false`)
|
|
143
144
|
* @param {Object} [options.create] - describes a new image to be created.
|
|
144
145
|
* @param {number} [options.create.width] - integral number of pixels wide.
|
|
145
146
|
* @param {number} [options.create.height] - integral number of pixels high.
|
|
@@ -223,7 +224,7 @@ const Sharp = function (input, options) {
|
|
|
223
224
|
joinChannelIn: [],
|
|
224
225
|
extractChannel: -1,
|
|
225
226
|
removeAlpha: false,
|
|
226
|
-
ensureAlpha:
|
|
227
|
+
ensureAlpha: -1,
|
|
227
228
|
colourspace: 'srgb',
|
|
228
229
|
composite: [],
|
|
229
230
|
// output
|
|
@@ -232,7 +233,9 @@ const Sharp = function (input, options) {
|
|
|
232
233
|
streamOut: false,
|
|
233
234
|
withMetadata: false,
|
|
234
235
|
withMetadataOrientation: -1,
|
|
236
|
+
withMetadataDensity: 0,
|
|
235
237
|
withMetadataIcc: '',
|
|
238
|
+
withMetadataStrs: {},
|
|
236
239
|
resolveWithObject: false,
|
|
237
240
|
// output format
|
|
238
241
|
jpegQuality: 80,
|
|
@@ -244,7 +247,7 @@ const Sharp = function (input, options) {
|
|
|
244
247
|
jpegOptimiseCoding: true,
|
|
245
248
|
jpegQuantisationTable: 0,
|
|
246
249
|
pngProgressive: false,
|
|
247
|
-
pngCompressionLevel:
|
|
250
|
+
pngCompressionLevel: 6,
|
|
248
251
|
pngAdaptiveFiltering: false,
|
|
249
252
|
pngPalette: false,
|
|
250
253
|
pngQuality: 100,
|
|
@@ -281,6 +284,7 @@ const Sharp = function (input, options) {
|
|
|
281
284
|
tileSkipBlanks: -1,
|
|
282
285
|
tileBackground: [255, 255, 255, 255],
|
|
283
286
|
tileCentre: false,
|
|
287
|
+
tileId: 'https://example.com/iiif',
|
|
284
288
|
linearA: 1,
|
|
285
289
|
linearB: 0,
|
|
286
290
|
// 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
|
|
|
@@ -30,9 +30,15 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
30
30
|
inputDescriptor.file = input;
|
|
31
31
|
} else if (is.buffer(input)) {
|
|
32
32
|
// Buffer
|
|
33
|
+
if (input.length === 0) {
|
|
34
|
+
throw Error('Input Buffer is empty');
|
|
35
|
+
}
|
|
33
36
|
inputDescriptor.buffer = input;
|
|
34
37
|
} else if (is.uint8Array(input)) {
|
|
35
38
|
// Uint8Array or Uint8ClampedArray
|
|
39
|
+
if (input.length === 0) {
|
|
40
|
+
throw Error('Input Bit Array is empty');
|
|
41
|
+
}
|
|
36
42
|
inputDescriptor.buffer = Buffer.from(input.buffer);
|
|
37
43
|
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
|
38
44
|
// Plain Object descriptor, e.g. create
|
|
@@ -97,6 +103,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
97
103
|
inputDescriptor.rawWidth = inputOptions.raw.width;
|
|
98
104
|
inputDescriptor.rawHeight = inputOptions.raw.height;
|
|
99
105
|
inputDescriptor.rawChannels = inputOptions.raw.channels;
|
|
106
|
+
inputDescriptor.rawPremultiplied = !!inputOptions.raw.premultiplied;
|
|
100
107
|
} else {
|
|
101
108
|
throw new Error('Expected width, height and channels for raw pixel input');
|
|
102
109
|
}
|
|
@@ -131,6 +138,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
131
138
|
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
|
|
132
139
|
}
|
|
133
140
|
}
|
|
141
|
+
// Sub Image File Directory (TIFF)
|
|
142
|
+
if (is.defined(inputOptions.subifd)) {
|
|
143
|
+
if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
|
|
144
|
+
inputDescriptor.subifd = inputOptions.subifd;
|
|
145
|
+
} else {
|
|
146
|
+
throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
134
149
|
// Create new image
|
|
135
150
|
if (is.defined(inputOptions.create)) {
|
|
136
151
|
if (
|
|
@@ -255,6 +270,7 @@ function _isStreamInput () {
|
|
|
255
270
|
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
|
256
271
|
* - `pagePrimary`: Number of the primary page in a HEIF image
|
|
257
272
|
* - `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
|
|
273
|
+
* - `subifds`: Number of Sub Image File Directories in an OME-TIFF image
|
|
258
274
|
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
|
259
275
|
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
|
260
276
|
* - `orientation`: Number value of the EXIF Orientation header, if present
|
package/lib/libvips.js
CHANGED
|
@@ -4,13 +4,15 @@ const fs = require('fs');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const spawnSync = require('child_process').spawnSync;
|
|
7
|
-
const
|
|
7
|
+
const semverCoerce = require('semver/functions/coerce');
|
|
8
|
+
const semverGreaterThanOrEqualTo = require('semver/functions/gte');
|
|
9
|
+
|
|
8
10
|
const platform = require('./platform');
|
|
9
11
|
|
|
10
12
|
const env = process.env;
|
|
11
13
|
const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */
|
|
12
14
|
require('../package.json').config.libvips;
|
|
13
|
-
const minimumLibvipsVersion =
|
|
15
|
+
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
|
|
14
16
|
|
|
15
17
|
const spawnSyncOptions = {
|
|
16
18
|
encoding: 'utf8',
|
|
@@ -37,6 +39,23 @@ const cachePath = function () {
|
|
|
37
39
|
return libvipsCachePath;
|
|
38
40
|
};
|
|
39
41
|
|
|
42
|
+
const log = function (item) {
|
|
43
|
+
if (item instanceof Error) {
|
|
44
|
+
console.error(`sharp: Installation error: ${item.message}`);
|
|
45
|
+
} else {
|
|
46
|
+
console.log(`sharp: ${item}`);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const isRosetta = function () {
|
|
51
|
+
/* istanbul ignore next */
|
|
52
|
+
if (process.platform === 'darwin' && process.arch === 'x64') {
|
|
53
|
+
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
|
|
54
|
+
return (translated || '').trim() === 'sysctl.proc_translated: 1';
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
|
|
40
59
|
const globalLibvipsVersion = function () {
|
|
41
60
|
if (process.platform !== 'win32') {
|
|
42
61
|
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
|
|
@@ -82,16 +101,20 @@ const useGlobalLibvips = function () {
|
|
|
82
101
|
if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
|
|
83
102
|
return false;
|
|
84
103
|
}
|
|
85
|
-
|
|
104
|
+
/* istanbul ignore next */
|
|
105
|
+
if (isRosetta()) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
86
108
|
const globalVipsVersion = globalLibvipsVersion();
|
|
87
109
|
return !!globalVipsVersion && /* istanbul ignore next */
|
|
88
|
-
|
|
110
|
+
semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
|
|
89
111
|
};
|
|
90
112
|
|
|
91
113
|
module.exports = {
|
|
92
114
|
minimumLibvipsVersion,
|
|
93
115
|
minimumLibvipsVersionLabelled,
|
|
94
116
|
cachePath,
|
|
117
|
+
log,
|
|
95
118
|
globalLibvipsVersion,
|
|
96
119
|
hasVendoredLibvips,
|
|
97
120
|
pkgConfigPath,
|
package/lib/operation.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { flatten: flattenArray } = require('array-flatten');
|
|
4
3
|
const color = require('color');
|
|
5
4
|
const is = require('./is');
|
|
6
5
|
|
|
@@ -127,7 +126,7 @@ function flop (flop) {
|
|
|
127
126
|
* @throws {Error} Invalid parameters
|
|
128
127
|
*/
|
|
129
128
|
function affine (matrix, options) {
|
|
130
|
-
const flatMatrix =
|
|
129
|
+
const flatMatrix = [].concat(...matrix);
|
|
131
130
|
if (flatMatrix.length === 4 && flatMatrix.every(is.number)) {
|
|
132
131
|
this.options.affineMatrix = flatMatrix;
|
|
133
132
|
} else {
|
|
@@ -269,7 +268,13 @@ function blur (sigma) {
|
|
|
269
268
|
}
|
|
270
269
|
|
|
271
270
|
/**
|
|
272
|
-
* Merge alpha transparency channel, if any, with a background.
|
|
271
|
+
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* await sharp(rgbaInput)
|
|
275
|
+
* .flatten({background: '#F0A703' })
|
|
276
|
+
* .toBuffer();
|
|
277
|
+
*
|
|
273
278
|
* @param {Object} [options]
|
|
274
279
|
* @param {string|Object} [options.background={r: 0, g: 0, b: 0}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black.
|
|
275
280
|
* @returns {Sharp}
|
|
@@ -397,7 +402,7 @@ function convolve (kernel) {
|
|
|
397
402
|
}
|
|
398
403
|
|
|
399
404
|
/**
|
|
400
|
-
* Any pixel value
|
|
405
|
+
* Any pixel value greater than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
|
|
401
406
|
* @param {number} [threshold=128] - a value in the range 0-255 representing the level at which the threshold will be applied.
|
|
402
407
|
* @param {Object} [options]
|
|
403
408
|
* @param {Boolean} [options.greyscale=true] - convert to single channel greyscale.
|