sharp 0.29.0 → 0.30.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.
- package/README.md +2 -2
- package/binding.gyp +2 -0
- package/install/libvips.js +39 -8
- package/lib/constructor.js +20 -5
- package/lib/input.js +21 -4
- package/lib/libvips.js +16 -2
- package/lib/operation.js +31 -3
- package/lib/output.js +250 -73
- package/lib/platform.js +1 -1
- package/lib/resize.js +19 -0
- package/lib/sharp.js +11 -3
- package/lib/utility.js +22 -3
- package/package.json +30 -13
- package/src/common.cc +156 -29
- package/src/common.h +49 -7
- package/src/libvips/cplusplus/VImage.cpp +34 -16
- package/src/libvips/cplusplus/vips-operators.cpp +29 -1
- package/src/metadata.cc +6 -0
- package/src/metadata.h +1 -0
- package/src/operations.cc +98 -3
- package/src/operations.h +15 -2
- package/src/pipeline.cc +357 -268
- package/src/pipeline.h +31 -19
- package/src/sharp.cc +16 -0
- package/src/utilities.cc +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
The typical use case for this high speed Node.js module
|
|
6
6
|
is to convert large images in common formats to
|
|
7
|
-
smaller, web-friendly JPEG, PNG, WebP and AVIF images of varying dimensions.
|
|
7
|
+
smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions.
|
|
8
8
|
|
|
9
9
|
Resizing an image is typically 4x-5x faster than using the
|
|
10
10
|
quickest ImageMagick and GraphicsMagick settings
|
|
@@ -103,7 +103,7 @@ covers reporting bugs, requesting features and submitting code changes.
|
|
|
103
103
|
|
|
104
104
|
## Licensing
|
|
105
105
|
|
|
106
|
-
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Lovell Fuller and contributors.
|
|
106
|
+
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Lovell Fuller and contributors.
|
|
107
107
|
|
|
108
108
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
109
109
|
you may not use this file except in compliance with the License.
|
package/binding.gyp
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
'VCCLCompilerTool': {
|
|
40
40
|
'ExceptionHandling': 1,
|
|
41
41
|
'Optimization': 1,
|
|
42
|
+
'RuntimeLibrary': '2', # /MD
|
|
42
43
|
'WholeProgramOptimization': 'true'
|
|
43
44
|
},
|
|
44
45
|
'VCLibrarianTool': {
|
|
@@ -205,6 +206,7 @@
|
|
|
205
206
|
'VCCLCompilerTool': {
|
|
206
207
|
'ExceptionHandling': 1,
|
|
207
208
|
'Optimization': 1,
|
|
209
|
+
'RuntimeLibrary': '2', # /MD
|
|
208
210
|
'WholeProgramOptimization': 'true'
|
|
209
211
|
},
|
|
210
212
|
'VCLibrarianTool': {
|
package/install/libvips.js
CHANGED
|
@@ -5,6 +5,7 @@ const os = require('os');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const stream = require('stream');
|
|
7
7
|
const zlib = require('zlib');
|
|
8
|
+
const { createHash } = require('crypto');
|
|
8
9
|
|
|
9
10
|
const detectLibc = require('detect-libc');
|
|
10
11
|
const semverLessThan = require('semver/functions/lt');
|
|
@@ -18,7 +19,7 @@ const platform = require('../lib/platform');
|
|
|
18
19
|
|
|
19
20
|
const minimumGlibcVersionByArch = {
|
|
20
21
|
arm: '2.28',
|
|
21
|
-
arm64: '2.
|
|
22
|
+
arm64: '2.17',
|
|
22
23
|
x64: '2.17'
|
|
23
24
|
};
|
|
24
25
|
|
|
@@ -55,6 +56,33 @@ const handleError = function (err) {
|
|
|
55
56
|
}
|
|
56
57
|
};
|
|
57
58
|
|
|
59
|
+
const verifyIntegrity = function (platformAndArch) {
|
|
60
|
+
const expected = libvips.integrity(platformAndArch);
|
|
61
|
+
if (installationForced || !expected) {
|
|
62
|
+
libvips.log(`Integrity check skipped for ${platformAndArch}`);
|
|
63
|
+
return new stream.PassThrough();
|
|
64
|
+
}
|
|
65
|
+
const hash = createHash('sha512');
|
|
66
|
+
return new stream.Transform({
|
|
67
|
+
transform: function (chunk, _encoding, done) {
|
|
68
|
+
hash.update(chunk);
|
|
69
|
+
done(null, chunk);
|
|
70
|
+
},
|
|
71
|
+
flush: function (done) {
|
|
72
|
+
const digest = `sha512-${hash.digest('base64')}`;
|
|
73
|
+
if (expected !== digest) {
|
|
74
|
+
libvips.removeVendoredLibvips();
|
|
75
|
+
libvips.log(`Integrity expected: ${expected}`);
|
|
76
|
+
libvips.log(`Integrity received: ${digest}`);
|
|
77
|
+
done(new Error(`Integrity check failed for ${platformAndArch}`));
|
|
78
|
+
} else {
|
|
79
|
+
libvips.log(`Integrity check passed for ${platformAndArch}`);
|
|
80
|
+
done();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
58
86
|
const extractTarball = function (tarPath, platformAndArch) {
|
|
59
87
|
const versionedVendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch);
|
|
60
88
|
libvips.mkdirSync(versionedVendorPath);
|
|
@@ -66,6 +94,7 @@ const extractTarball = function (tarPath, platformAndArch) {
|
|
|
66
94
|
|
|
67
95
|
stream.pipeline(
|
|
68
96
|
fs.createReadStream(tarPath),
|
|
97
|
+
verifyIntegrity(platformAndArch),
|
|
69
98
|
new zlib.BrotliDecompress(),
|
|
70
99
|
tarFs.extract(versionedVendorPath, { ignore }),
|
|
71
100
|
function (err) {
|
|
@@ -103,14 +132,16 @@ try {
|
|
|
103
132
|
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
104
133
|
}
|
|
105
134
|
// Linux libc version check
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
135
|
+
const libcFamily = detectLibc.familySync();
|
|
136
|
+
const libcVersion = detectLibc.versionSync();
|
|
137
|
+
if (libcFamily === detectLibc.GLIBC && libcVersion && minimumGlibcVersionByArch[arch]) {
|
|
138
|
+
if (semverLessThan(`${libcVersion}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
|
|
139
|
+
handleError(new Error(`Use with glibc ${libcVersion} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
|
109
140
|
}
|
|
110
141
|
}
|
|
111
|
-
if (
|
|
112
|
-
if (semverLessThan(
|
|
113
|
-
handleError(new Error(`Use with musl ${
|
|
142
|
+
if (libcFamily === detectLibc.MUSL && libcVersion) {
|
|
143
|
+
if (semverLessThan(libcVersion, '1.1.24')) {
|
|
144
|
+
handleError(new Error(`Use with musl ${libcVersion} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
|
114
145
|
}
|
|
115
146
|
}
|
|
116
147
|
// Node.js minimum version check
|
|
@@ -120,7 +151,7 @@ try {
|
|
|
120
151
|
}
|
|
121
152
|
|
|
122
153
|
// Download to per-process temporary file
|
|
123
|
-
const tarFilename = ['libvips',
|
|
154
|
+
const tarFilename = ['libvips', minimumLibvipsVersionLabelled, platformAndArch].join('-') + '.tar.br';
|
|
124
155
|
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
|
|
125
156
|
if (fs.existsSync(tarPathCache)) {
|
|
126
157
|
libvips.log(`Using cached ${tarPathCache}`);
|
package/lib/constructor.js
CHANGED
|
@@ -13,7 +13,7 @@ const debuglog = util.debuglog('sharp');
|
|
|
13
13
|
/**
|
|
14
14
|
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
|
|
15
15
|
*
|
|
16
|
-
* JPEG, PNG, WebP, AVIF or TIFF format image data can be streamed out from this object.
|
|
16
|
+
* JPEG, PNG, WebP, GIF, AVIF or TIFF format image data can be streamed out from this object.
|
|
17
17
|
* When using Stream based output, derived attributes are available from the `info` event.
|
|
18
18
|
*
|
|
19
19
|
* Non-critical problems encountered during processing are emitted as `warning` events.
|
|
@@ -103,6 +103,7 @@ const debuglog = util.debuglog('sharp');
|
|
|
103
103
|
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
|
|
104
104
|
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
|
105
105
|
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
|
106
|
+
* @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG).
|
|
106
107
|
* @param {boolean} [options.sequentialRead=false] - Set this to `true` to use sequential rather than random access where possible.
|
|
107
108
|
* This can reduce memory usage and might improve performance on some systems.
|
|
108
109
|
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
|
|
@@ -165,6 +166,7 @@ const Sharp = function (input, options) {
|
|
|
165
166
|
extendRight: 0,
|
|
166
167
|
extendBackground: [0, 0, 0, 255],
|
|
167
168
|
withoutEnlargement: false,
|
|
169
|
+
withoutReduction: false,
|
|
168
170
|
affineMatrix: [],
|
|
169
171
|
affineBackground: [0, 0, 0, 255],
|
|
170
172
|
affineIdx: 0,
|
|
@@ -199,6 +201,7 @@ const Sharp = function (input, options) {
|
|
|
199
201
|
brightness: 1,
|
|
200
202
|
saturation: 1,
|
|
201
203
|
hue: 0,
|
|
204
|
+
lightness: 0,
|
|
202
205
|
booleanBufferIn: null,
|
|
203
206
|
booleanFileIn: '',
|
|
204
207
|
joinChannelIn: [],
|
|
@@ -232,14 +235,23 @@ const Sharp = function (input, options) {
|
|
|
232
235
|
pngAdaptiveFiltering: false,
|
|
233
236
|
pngPalette: false,
|
|
234
237
|
pngQuality: 100,
|
|
235
|
-
|
|
238
|
+
pngEffort: 7,
|
|
239
|
+
pngBitdepth: 8,
|
|
236
240
|
pngDither: 1,
|
|
241
|
+
jp2Quality: 80,
|
|
242
|
+
jp2TileHeight: 512,
|
|
243
|
+
jp2TileWidth: 512,
|
|
244
|
+
jp2Lossless: false,
|
|
245
|
+
jp2ChromaSubsampling: '4:4:4',
|
|
237
246
|
webpQuality: 80,
|
|
238
247
|
webpAlphaQuality: 100,
|
|
239
248
|
webpLossless: false,
|
|
240
249
|
webpNearLossless: false,
|
|
241
250
|
webpSmartSubsample: false,
|
|
242
|
-
|
|
251
|
+
webpEffort: 4,
|
|
252
|
+
gifBitdepth: 8,
|
|
253
|
+
gifEffort: 7,
|
|
254
|
+
gifDither: 1,
|
|
243
255
|
tiffQuality: 80,
|
|
244
256
|
tiffCompression: 'jpeg',
|
|
245
257
|
tiffPredictor: 'horizontal',
|
|
@@ -250,10 +262,11 @@ const Sharp = function (input, options) {
|
|
|
250
262
|
tiffTileWidth: 256,
|
|
251
263
|
tiffXres: 1.0,
|
|
252
264
|
tiffYres: 1.0,
|
|
265
|
+
tiffResolutionUnit: 'inch',
|
|
253
266
|
heifQuality: 50,
|
|
254
267
|
heifLossless: false,
|
|
255
268
|
heifCompression: 'av1',
|
|
256
|
-
|
|
269
|
+
heifEffort: 4,
|
|
257
270
|
heifChromaSubsampling: '4:4:4',
|
|
258
271
|
rawDepth: 'uchar',
|
|
259
272
|
tileSize: 256,
|
|
@@ -267,6 +280,7 @@ const Sharp = function (input, options) {
|
|
|
267
280
|
tileBackground: [255, 255, 255, 255],
|
|
268
281
|
tileCentre: false,
|
|
269
282
|
tileId: 'https://example.com/iiif',
|
|
283
|
+
timeoutSeconds: 0,
|
|
270
284
|
linearA: 1,
|
|
271
285
|
linearB: 0,
|
|
272
286
|
// Function to notify of libvips warnings
|
|
@@ -282,7 +296,8 @@ const Sharp = function (input, options) {
|
|
|
282
296
|
this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
|
|
283
297
|
return this;
|
|
284
298
|
};
|
|
285
|
-
|
|
299
|
+
Object.setPrototypeOf(Sharp.prototype, stream.Duplex.prototype);
|
|
300
|
+
Object.setPrototypeOf(Sharp, stream.Duplex);
|
|
286
301
|
|
|
287
302
|
/**
|
|
288
303
|
* Take a "snapshot" of the Sharp instance, returning a new instance.
|
package/lib/input.js
CHANGED
|
@@ -9,9 +9,9 @@ const sharp = require('./sharp');
|
|
|
9
9
|
* @private
|
|
10
10
|
*/
|
|
11
11
|
function _inputOptionsFromObject (obj) {
|
|
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 }
|
|
12
|
+
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd } = obj;
|
|
13
|
+
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined)
|
|
14
|
+
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd }
|
|
15
15
|
: undefined;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -23,6 +23,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
23
23
|
const inputDescriptor = {
|
|
24
24
|
failOnError: true,
|
|
25
25
|
limitInputPixels: Math.pow(0x3FFF, 2),
|
|
26
|
+
unlimited: false,
|
|
26
27
|
sequentialRead: false
|
|
27
28
|
};
|
|
28
29
|
if (is.string(input)) {
|
|
@@ -83,6 +84,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
83
84
|
throw is.invalidParameterError('limitInputPixels', 'integer >= 0', inputOptions.limitInputPixels);
|
|
84
85
|
}
|
|
85
86
|
}
|
|
87
|
+
// unlimited
|
|
88
|
+
if (is.defined(inputOptions.unlimited)) {
|
|
89
|
+
if (is.bool(inputOptions.unlimited)) {
|
|
90
|
+
inputDescriptor.unlimited = inputOptions.unlimited;
|
|
91
|
+
} else {
|
|
92
|
+
throw is.invalidParameterError('unlimited', 'boolean', inputOptions.unlimited);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
86
95
|
// sequentialRead
|
|
87
96
|
if (is.defined(inputOptions.sequentialRead)) {
|
|
88
97
|
if (is.bool(inputOptions.sequentialRead)) {
|
|
@@ -281,7 +290,11 @@ function _isStreamInput () {
|
|
|
281
290
|
}
|
|
282
291
|
|
|
283
292
|
/**
|
|
284
|
-
* Fast access to (uncached) image metadata without decoding any compressed
|
|
293
|
+
* Fast access to (uncached) image metadata without decoding any compressed pixel data.
|
|
294
|
+
*
|
|
295
|
+
* This is taken from the header of the input image.
|
|
296
|
+
* It does not include operations, such as resize, to be applied to the output image.
|
|
297
|
+
*
|
|
285
298
|
* A `Promise` is returned when `callback` is not provided.
|
|
286
299
|
*
|
|
287
300
|
* - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
|
|
@@ -303,6 +316,7 @@ function _isStreamInput () {
|
|
|
303
316
|
* - `subifds`: Number of Sub Image File Directories in an OME-TIFF image
|
|
304
317
|
* - `background`: Default background colour, if present, for PNG (bKGD) and GIF images, either an RGB Object or a single greyscale value
|
|
305
318
|
* - `compression`: The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC)
|
|
319
|
+
* - `resolutionUnit`: The unit of resolution (density), either `inch` or `cm`, if present
|
|
306
320
|
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
|
307
321
|
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
|
308
322
|
* - `orientation`: Number value of the EXIF Orientation header, if present
|
|
@@ -313,6 +327,9 @@ function _isStreamInput () {
|
|
|
313
327
|
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
|
314
328
|
*
|
|
315
329
|
* @example
|
|
330
|
+
* const metadata = await sharp(input).metadata();
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
316
333
|
* const image = sharp(inputJpg);
|
|
317
334
|
* image
|
|
318
335
|
* .metadata()
|
package/lib/libvips.js
CHANGED
|
@@ -8,10 +8,11 @@ const semverCoerce = require('semver/functions/coerce');
|
|
|
8
8
|
const semverGreaterThanOrEqualTo = require('semver/functions/gte');
|
|
9
9
|
|
|
10
10
|
const platform = require('./platform');
|
|
11
|
+
const { config } = require('../package.json');
|
|
11
12
|
|
|
12
13
|
const env = process.env;
|
|
13
14
|
const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */
|
|
14
|
-
|
|
15
|
+
config.libvips;
|
|
15
16
|
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
|
|
16
17
|
|
|
17
18
|
const spawnSyncOptions = {
|
|
@@ -19,6 +20,8 @@ const spawnSyncOptions = {
|
|
|
19
20
|
shell: true
|
|
20
21
|
};
|
|
21
22
|
|
|
23
|
+
const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
|
|
24
|
+
|
|
22
25
|
const mkdirSync = function (dirPath) {
|
|
23
26
|
try {
|
|
24
27
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
@@ -39,6 +42,10 @@ const cachePath = function () {
|
|
|
39
42
|
return libvipsCachePath;
|
|
40
43
|
};
|
|
41
44
|
|
|
45
|
+
const integrity = function (platformAndArch) {
|
|
46
|
+
return env[`npm_package_config_integrity_${platformAndArch.replace('-', '_')}`] || config.integrity[platformAndArch];
|
|
47
|
+
};
|
|
48
|
+
|
|
42
49
|
const log = function (item) {
|
|
43
50
|
if (item instanceof Error) {
|
|
44
51
|
console.error(`sharp: Installation error: ${item.message}`);
|
|
@@ -67,10 +74,15 @@ const globalLibvipsVersion = function () {
|
|
|
67
74
|
};
|
|
68
75
|
|
|
69
76
|
const hasVendoredLibvips = function () {
|
|
70
|
-
const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
|
|
71
77
|
return fs.existsSync(vendorPath);
|
|
72
78
|
};
|
|
73
79
|
|
|
80
|
+
/* istanbul ignore next */
|
|
81
|
+
const removeVendoredLibvips = function () {
|
|
82
|
+
const rm = fs.rmSync ? fs.rmSync : fs.rmdirSync;
|
|
83
|
+
rm(vendorPath, { recursive: true, maxRetries: 3, force: true });
|
|
84
|
+
};
|
|
85
|
+
|
|
74
86
|
const pkgConfigPath = function () {
|
|
75
87
|
if (process.platform !== 'win32') {
|
|
76
88
|
const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
|
|
@@ -99,9 +111,11 @@ module.exports = {
|
|
|
99
111
|
minimumLibvipsVersion,
|
|
100
112
|
minimumLibvipsVersionLabelled,
|
|
101
113
|
cachePath,
|
|
114
|
+
integrity,
|
|
102
115
|
log,
|
|
103
116
|
globalLibvipsVersion,
|
|
104
117
|
hasVendoredLibvips,
|
|
118
|
+
removeVendoredLibvips,
|
|
105
119
|
pkgConfigPath,
|
|
106
120
|
useGlobalLibvips,
|
|
107
121
|
mkdirSync
|
package/lib/operation.js
CHANGED
|
@@ -245,8 +245,21 @@ function median (size) {
|
|
|
245
245
|
|
|
246
246
|
/**
|
|
247
247
|
* Blur the image.
|
|
248
|
-
*
|
|
248
|
+
*
|
|
249
|
+
* When used without parameters, performs a fast 3x3 box blur (equivalent to a box linear filter).
|
|
250
|
+
*
|
|
249
251
|
* When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* const boxBlurred = await sharp(input)
|
|
255
|
+
* .blur()
|
|
256
|
+
* .toBuffer();
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* const gaussianBlurred = await sharp(input)
|
|
260
|
+
* .blur(5)
|
|
261
|
+
* .toBuffer();
|
|
262
|
+
*
|
|
250
263
|
* @param {number} [sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
|
251
264
|
* @returns {Sharp}
|
|
252
265
|
* @throws {Error} Invalid parameters
|
|
@@ -570,14 +583,16 @@ function recomb (inputMatrix) {
|
|
|
570
583
|
}
|
|
571
584
|
|
|
572
585
|
/**
|
|
573
|
-
* Transforms the image using brightness, saturation
|
|
586
|
+
* Transforms the image using brightness, saturation, hue rotation, and lightness.
|
|
587
|
+
* Brightness and lightness both operate on luminance, with the difference being that
|
|
588
|
+
* brightness is multiplicative whereas lightness is additive.
|
|
574
589
|
*
|
|
575
590
|
* @since 0.22.1
|
|
576
591
|
*
|
|
577
592
|
* @example
|
|
578
593
|
* sharp(input)
|
|
579
594
|
* .modulate({
|
|
580
|
-
* brightness: 2 // increase
|
|
595
|
+
* brightness: 2 // increase brightness by a factor of 2
|
|
581
596
|
* });
|
|
582
597
|
*
|
|
583
598
|
* sharp(input)
|
|
@@ -585,6 +600,11 @@ function recomb (inputMatrix) {
|
|
|
585
600
|
* hue: 180 // hue-rotate by 180 degrees
|
|
586
601
|
* });
|
|
587
602
|
*
|
|
603
|
+
* sharp(input)
|
|
604
|
+
* .modulate({
|
|
605
|
+
* lightness: 50 // increase lightness by +50
|
|
606
|
+
* });
|
|
607
|
+
*
|
|
588
608
|
* // decreate brightness and saturation while also hue-rotating by 90 degrees
|
|
589
609
|
* sharp(input)
|
|
590
610
|
* .modulate({
|
|
@@ -597,6 +617,7 @@ function recomb (inputMatrix) {
|
|
|
597
617
|
* @param {number} [options.brightness] Brightness multiplier
|
|
598
618
|
* @param {number} [options.saturation] Saturation multiplier
|
|
599
619
|
* @param {number} [options.hue] Degrees for hue rotation
|
|
620
|
+
* @param {number} [options.lightness] Lightness addend
|
|
600
621
|
* @returns {Sharp}
|
|
601
622
|
*/
|
|
602
623
|
function modulate (options) {
|
|
@@ -624,6 +645,13 @@ function modulate (options) {
|
|
|
624
645
|
throw is.invalidParameterError('hue', 'number', options.hue);
|
|
625
646
|
}
|
|
626
647
|
}
|
|
648
|
+
if ('lightness' in options) {
|
|
649
|
+
if (is.number(options.lightness)) {
|
|
650
|
+
this.options.lightness = options.lightness;
|
|
651
|
+
} else {
|
|
652
|
+
throw is.invalidParameterError('lightness', 'number', options.lightness);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
627
655
|
return this;
|
|
628
656
|
}
|
|
629
657
|
|