sharp 0.30.2 → 0.30.5
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/install/libvips.js +9 -1
- package/lib/colour.js +9 -0
- package/lib/composite.js +18 -2
- package/lib/constructor.js +7 -7
- package/lib/input.js +33 -14
- package/lib/libvips.js +10 -2
- package/lib/operation.js +147 -28
- package/lib/output.js +6 -1
- package/lib/platform.js +5 -3
- package/lib/sharp.js +2 -2
- package/lib/utility.js +17 -4
- package/package.json +9 -7
- package/src/common.cc +8 -11
- package/src/common.h +2 -2
- package/src/operations.cc +10 -3
- package/src/operations.h +2 -1
- package/src/pipeline.cc +27 -15
- package/src/pipeline.h +10 -4
package/install/libvips.js
CHANGED
|
@@ -35,6 +35,7 @@ const hasSharpPrebuild = [
|
|
|
35
35
|
];
|
|
36
36
|
|
|
37
37
|
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
|
38
|
+
const localLibvipsDir = process.env.npm_config_sharp_libvips_local_prebuilds || '';
|
|
38
39
|
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
|
39
40
|
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
|
40
41
|
const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
|
|
@@ -42,7 +43,9 @@ const installationForced = !!(process.env.npm_config_sharp_install_force || proc
|
|
|
42
43
|
const fail = function (err) {
|
|
43
44
|
libvips.log(err);
|
|
44
45
|
if (err.code === 'EACCES') {
|
|
45
|
-
libvips.log('Are you trying to install as a root or sudo user?
|
|
46
|
+
libvips.log('Are you trying to install as a root or sudo user?');
|
|
47
|
+
libvips.log('- For npm <= v6, try again with the "--unsafe-perm" flag');
|
|
48
|
+
libvips.log('- For npm >= v8, the user must own the directory "npm install" is run in');
|
|
46
49
|
}
|
|
47
50
|
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
|
|
48
51
|
process.exit(1);
|
|
@@ -156,6 +159,11 @@ try {
|
|
|
156
159
|
if (fs.existsSync(tarPathCache)) {
|
|
157
160
|
libvips.log(`Using cached ${tarPathCache}`);
|
|
158
161
|
extractTarball(tarPathCache, platformAndArch);
|
|
162
|
+
} else if (localLibvipsDir) {
|
|
163
|
+
// If localLibvipsDir is given try to use binaries from local directory
|
|
164
|
+
const tarPathLocal = path.join(path.resolve(localLibvipsDir), `v${minimumLibvipsVersionLabelled}`, tarFilename);
|
|
165
|
+
libvips.log(`Using local libvips from ${tarPathLocal}`);
|
|
166
|
+
extractTarball(tarPathLocal, platformAndArch);
|
|
159
167
|
} else {
|
|
160
168
|
const url = distBaseUrl + tarFilename;
|
|
161
169
|
libvips.log(`Downloading ${url}`);
|
package/lib/colour.js
CHANGED
|
@@ -19,6 +19,11 @@ const colourspace = {
|
|
|
19
19
|
* Tint the image using the provided chroma while preserving the image luminance.
|
|
20
20
|
* An alpha channel may be present and will be unchanged by the operation.
|
|
21
21
|
*
|
|
22
|
+
* @example
|
|
23
|
+
* const output = await sharp(input)
|
|
24
|
+
* .tint({ r: 255, g: 240, b: 16 })
|
|
25
|
+
* .toBuffer();
|
|
26
|
+
*
|
|
22
27
|
* @param {string|Object} rgb - parsed by the [color](https://www.npmjs.org/package/color) module to extract chroma values.
|
|
23
28
|
* @returns {Sharp}
|
|
24
29
|
* @throws {Error} Invalid parameter
|
|
@@ -37,6 +42,10 @@ function tint (rgb) {
|
|
|
37
42
|
* This may be overridden by other sharp operations such as `toColourspace('b-w')`,
|
|
38
43
|
* which will produce an output image containing one color channel.
|
|
39
44
|
* An alpha channel may be present, and will be unchanged by the operation.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const output = await sharp(input).greyscale().toBuffer();
|
|
48
|
+
*
|
|
40
49
|
* @param {Boolean} [greyscale=true]
|
|
41
50
|
* @returns {Sharp}
|
|
42
51
|
*/
|
package/lib/composite.js
CHANGED
|
@@ -50,12 +50,27 @@ const blend = {
|
|
|
50
50
|
* `hard-light`, `soft-light`, `difference`, `exclusion`.
|
|
51
51
|
*
|
|
52
52
|
* More information about blend modes can be found at
|
|
53
|
-
* https://libvips.
|
|
53
|
+
* https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
|
|
54
54
|
* and https://www.cairographics.org/operators/
|
|
55
55
|
*
|
|
56
56
|
* @since 0.22.0
|
|
57
57
|
*
|
|
58
58
|
* @example
|
|
59
|
+
* await sharp(background)
|
|
60
|
+
* .composite([
|
|
61
|
+
* { input: layer1, gravity: 'northwest' },
|
|
62
|
+
* { input: layer2, gravity: 'southeast' },
|
|
63
|
+
* ])
|
|
64
|
+
* .toFile('combined.png');
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* const output = await sharp('input.gif', { animated: true })
|
|
68
|
+
* .composite([
|
|
69
|
+
* { input: 'overlay.png', tile: true, blend: 'saturate' }
|
|
70
|
+
* ])
|
|
71
|
+
* .toBuffer();
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
59
74
|
* sharp('input.png')
|
|
60
75
|
* .rotate(180)
|
|
61
76
|
* .resize(300)
|
|
@@ -89,7 +104,8 @@ const blend = {
|
|
|
89
104
|
* @param {Number} [images[].raw.width]
|
|
90
105
|
* @param {Number} [images[].raw.height]
|
|
91
106
|
* @param {Number} [images[].raw.channels]
|
|
92
|
-
* @param {boolean} [images[].
|
|
107
|
+
* @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
|
|
108
|
+
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor#parameters|constructor parameters}
|
|
93
109
|
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
|
|
94
110
|
* @returns {Sharp}
|
|
95
111
|
* @throws {Error} Invalid parameters
|
package/lib/constructor.js
CHANGED
|
@@ -98,8 +98,7 @@ const debuglog = util.debuglog('sharp');
|
|
|
98
98
|
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
|
99
99
|
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
|
100
100
|
* @param {Object} [options] - if present, is an Object with optional attributes.
|
|
101
|
-
* @param {
|
|
102
|
-
* Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid.
|
|
101
|
+
* @param {string} [options.failOn='warning'] - level of sensitivity to invalid images, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels.
|
|
103
102
|
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
|
|
104
103
|
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
|
105
104
|
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
|
@@ -186,8 +185,11 @@ const Sharp = function (input, options) {
|
|
|
186
185
|
medianSize: 0,
|
|
187
186
|
blurSigma: 0,
|
|
188
187
|
sharpenSigma: 0,
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
sharpenM1: 1,
|
|
189
|
+
sharpenM2: 2,
|
|
190
|
+
sharpenX1: 2,
|
|
191
|
+
sharpenY2: 10,
|
|
192
|
+
sharpenY3: 20,
|
|
191
193
|
threshold: 0,
|
|
192
194
|
thresholdGrayscale: true,
|
|
193
195
|
trimThreshold: 0,
|
|
@@ -317,9 +319,7 @@ Object.setPrototypeOf(Sharp, stream.Duplex);
|
|
|
317
319
|
* // Using Promises to know when the pipeline is complete
|
|
318
320
|
* const fs = require("fs");
|
|
319
321
|
* const got = require("got");
|
|
320
|
-
* const sharpStream = sharp({
|
|
321
|
-
* failOnError: false
|
|
322
|
-
* });
|
|
322
|
+
* const sharpStream = sharp({ failOn: 'none' });
|
|
323
323
|
*
|
|
324
324
|
* const promises = [];
|
|
325
325
|
*
|
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, 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 }
|
|
12
|
+
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd } = obj;
|
|
13
|
+
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd].some(is.defined)
|
|
14
|
+
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd }
|
|
15
15
|
: undefined;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -21,7 +21,7 @@ function _inputOptionsFromObject (obj) {
|
|
|
21
21
|
*/
|
|
22
22
|
function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
23
23
|
const inputDescriptor = {
|
|
24
|
-
|
|
24
|
+
failOn: 'warning',
|
|
25
25
|
limitInputPixels: Math.pow(0x3FFF, 2),
|
|
26
26
|
unlimited: false,
|
|
27
27
|
sequentialRead: false
|
|
@@ -39,7 +39,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
39
39
|
if (input.length === 0) {
|
|
40
40
|
throw Error('Input Bit Array is empty');
|
|
41
41
|
}
|
|
42
|
-
inputDescriptor.buffer = Buffer.from(input.buffer);
|
|
42
|
+
inputDescriptor.buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
|
43
43
|
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
|
44
44
|
// Plain Object descriptor, e.g. create
|
|
45
45
|
inputOptions = input;
|
|
@@ -56,14 +56,22 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
56
56
|
}`);
|
|
57
57
|
}
|
|
58
58
|
if (is.object(inputOptions)) {
|
|
59
|
-
//
|
|
59
|
+
// Deprecated: failOnError
|
|
60
60
|
if (is.defined(inputOptions.failOnError)) {
|
|
61
61
|
if (is.bool(inputOptions.failOnError)) {
|
|
62
|
-
inputDescriptor.
|
|
62
|
+
inputDescriptor.failOn = inputOptions.failOnError ? 'warning' : 'none';
|
|
63
63
|
} else {
|
|
64
64
|
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
// failOn
|
|
68
|
+
if (is.defined(inputOptions.failOn)) {
|
|
69
|
+
if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {
|
|
70
|
+
inputDescriptor.failOn = inputOptions.failOn;
|
|
71
|
+
} else {
|
|
72
|
+
throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
67
75
|
// Density
|
|
68
76
|
if (is.defined(inputOptions.density)) {
|
|
69
77
|
if (is.inRange(inputOptions.density, 1, 100000)) {
|
|
@@ -299,11 +307,11 @@ function _isStreamInput () {
|
|
|
299
307
|
*
|
|
300
308
|
* - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
|
|
301
309
|
* - `size`: Total size of image in bytes, for Stream and Buffer input only
|
|
302
|
-
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
|
|
303
|
-
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration)
|
|
304
|
-
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://libvips.
|
|
310
|
+
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
|
|
311
|
+
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
|
|
312
|
+
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
|
|
305
313
|
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
|
306
|
-
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.
|
|
314
|
+
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
|
|
307
315
|
* - `density`: Number of pixels per inch (DPI), if present
|
|
308
316
|
* - `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
|
|
309
317
|
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
|
@@ -343,6 +351,17 @@ function _isStreamInput () {
|
|
|
343
351
|
* // data contains a WebP image half the width and height of the original JPEG
|
|
344
352
|
* });
|
|
345
353
|
*
|
|
354
|
+
* @example
|
|
355
|
+
* // Based on EXIF rotation metadata, get the right-side-up width and height:
|
|
356
|
+
*
|
|
357
|
+
* const size = getNormalSize(await sharp(input).metadata());
|
|
358
|
+
*
|
|
359
|
+
* function getNormalSize({ width, height, orientation }) {
|
|
360
|
+
* return orientation || 0 >= 5
|
|
361
|
+
* ? { width: height, height: width }
|
|
362
|
+
* : { width, height };
|
|
363
|
+
* }
|
|
364
|
+
*
|
|
346
365
|
* @param {Function} [callback] - called with the arguments `(err, metadata)`
|
|
347
366
|
* @returns {Promise<Object>|Sharp}
|
|
348
367
|
*/
|
|
@@ -401,9 +420,9 @@ function metadata (callback) {
|
|
|
401
420
|
* - `maxX` (x-coordinate of one of the pixel where the maximum lies)
|
|
402
421
|
* - `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
|
403
422
|
* - `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
|
|
404
|
-
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any
|
|
405
|
-
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any
|
|
406
|
-
* - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram
|
|
423
|
+
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any.
|
|
424
|
+
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any.
|
|
425
|
+
* - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram.
|
|
407
426
|
*
|
|
408
427
|
* **Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
|
|
409
428
|
* written to a buffer in order to run `stats` on the result (see third example).
|
package/lib/libvips.js
CHANGED
|
@@ -65,7 +65,12 @@ const isRosetta = function () {
|
|
|
65
65
|
|
|
66
66
|
const globalLibvipsVersion = function () {
|
|
67
67
|
if (process.platform !== 'win32') {
|
|
68
|
-
const globalLibvipsVersion = spawnSync(
|
|
68
|
+
const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
|
|
69
|
+
...spawnSyncOptions,
|
|
70
|
+
env: {
|
|
71
|
+
PKG_CONFIG_PATH: pkgConfigPath()
|
|
72
|
+
}
|
|
73
|
+
}).stdout;
|
|
69
74
|
/* istanbul ignore next */
|
|
70
75
|
return (globalLibvipsVersion || '').trim();
|
|
71
76
|
} else {
|
|
@@ -85,7 +90,10 @@ const removeVendoredLibvips = function () {
|
|
|
85
90
|
|
|
86
91
|
const pkgConfigPath = function () {
|
|
87
92
|
if (process.platform !== 'win32') {
|
|
88
|
-
const brewPkgConfigPath = spawnSync(
|
|
93
|
+
const brewPkgConfigPath = spawnSync(
|
|
94
|
+
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
|
|
95
|
+
spawnSyncOptions
|
|
96
|
+
).stdout || '';
|
|
89
97
|
return [
|
|
90
98
|
brewPkgConfigPath.trim(),
|
|
91
99
|
env.PKG_CONFIG_PATH,
|
package/lib/operation.js
CHANGED
|
@@ -63,6 +63,10 @@ function rotate (angle, options) {
|
|
|
63
63
|
/**
|
|
64
64
|
* Flip the image about the vertical Y axis. This always occurs after rotation, if any.
|
|
65
65
|
* The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* const output = await sharp(input).flip().toBuffer();
|
|
69
|
+
*
|
|
66
70
|
* @param {Boolean} [flip=true]
|
|
67
71
|
* @returns {Sharp}
|
|
68
72
|
*/
|
|
@@ -74,6 +78,10 @@ function flip (flip) {
|
|
|
74
78
|
/**
|
|
75
79
|
* Flop the image about the horizontal X axis. This always occurs after rotation, if any.
|
|
76
80
|
* The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const output = await sharp(input).flop().toBuffer();
|
|
84
|
+
*
|
|
77
85
|
* @param {Boolean} [flop=true]
|
|
78
86
|
* @returns {Sharp}
|
|
79
87
|
*/
|
|
@@ -185,40 +193,107 @@ function affine (matrix, options) {
|
|
|
185
193
|
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
|
|
186
194
|
* Separate control over the level of sharpening in "flat" and "jagged" areas is available.
|
|
187
195
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* @
|
|
196
|
+
* See {@link https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen|libvips sharpen} operation.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* const data = await sharp(input).sharpen().toBuffer();
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* const data = await sharp(input).sharpen({ sigma: 2 }).toBuffer();
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* const data = await sharp(input)
|
|
206
|
+
* .sharpen({
|
|
207
|
+
* sigma: 2,
|
|
208
|
+
* m1: 0
|
|
209
|
+
* m2: 3,
|
|
210
|
+
* x1: 3,
|
|
211
|
+
* y2: 15,
|
|
212
|
+
* y3: 15,
|
|
213
|
+
* })
|
|
214
|
+
* .toBuffer();
|
|
215
|
+
*
|
|
216
|
+
* @param {Object|number} [options] - if present, is an Object with attributes or (deprecated) a number for `options.sigma`.
|
|
217
|
+
* @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
|
218
|
+
* @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas.
|
|
219
|
+
* @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas.
|
|
220
|
+
* @param {number} [options.x1=2.0] - threshold between "flat" and "jagged"
|
|
221
|
+
* @param {number} [options.y2=10.0] - maximum amount of brightening.
|
|
222
|
+
* @param {number} [options.y3=20.0] - maximum amount of darkening.
|
|
223
|
+
* @param {number} [flat] - (deprecated) see `options.m1`.
|
|
224
|
+
* @param {number} [jagged] - (deprecated) see `options.m2`.
|
|
191
225
|
* @returns {Sharp}
|
|
192
226
|
* @throws {Error} Invalid parameters
|
|
193
227
|
*/
|
|
194
|
-
function sharpen (
|
|
195
|
-
if (!is.defined(
|
|
228
|
+
function sharpen (options, flat, jagged) {
|
|
229
|
+
if (!is.defined(options)) {
|
|
196
230
|
// No arguments: default to mild sharpen
|
|
197
231
|
this.options.sharpenSigma = -1;
|
|
198
|
-
} else if (is.bool(
|
|
199
|
-
//
|
|
200
|
-
this.options.sharpenSigma =
|
|
201
|
-
} else if (is.number(
|
|
202
|
-
//
|
|
203
|
-
this.options.sharpenSigma =
|
|
204
|
-
//
|
|
232
|
+
} else if (is.bool(options)) {
|
|
233
|
+
// Deprecated boolean argument: apply mild sharpen?
|
|
234
|
+
this.options.sharpenSigma = options ? -1 : 0;
|
|
235
|
+
} else if (is.number(options) && is.inRange(options, 0.01, 10000)) {
|
|
236
|
+
// Deprecated numeric argument: specific sigma
|
|
237
|
+
this.options.sharpenSigma = options;
|
|
238
|
+
// Deprecated control over flat areas
|
|
205
239
|
if (is.defined(flat)) {
|
|
206
240
|
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
|
|
207
|
-
this.options.
|
|
241
|
+
this.options.sharpenM1 = flat;
|
|
208
242
|
} else {
|
|
209
243
|
throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
|
|
210
244
|
}
|
|
211
245
|
}
|
|
212
|
-
//
|
|
246
|
+
// Deprecated control over jagged areas
|
|
213
247
|
if (is.defined(jagged)) {
|
|
214
248
|
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
|
|
215
|
-
this.options.
|
|
249
|
+
this.options.sharpenM2 = jagged;
|
|
216
250
|
} else {
|
|
217
251
|
throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
|
|
218
252
|
}
|
|
219
253
|
}
|
|
254
|
+
} else if (is.plainObject(options)) {
|
|
255
|
+
if (is.number(options.sigma) && is.inRange(options.sigma, 0.01, 10000)) {
|
|
256
|
+
this.options.sharpenSigma = options.sigma;
|
|
257
|
+
} else {
|
|
258
|
+
throw is.invalidParameterError('options.sigma', 'number between 0.01 and 10000', options.sigma);
|
|
259
|
+
}
|
|
260
|
+
if (is.defined(options.m1)) {
|
|
261
|
+
if (is.number(options.m1) && is.inRange(options.m1, 0, 10000)) {
|
|
262
|
+
this.options.sharpenM1 = options.m1;
|
|
263
|
+
} else {
|
|
264
|
+
throw is.invalidParameterError('options.m1', 'number between 0 and 10000', options.m1);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (is.defined(options.m2)) {
|
|
268
|
+
if (is.number(options.m2) && is.inRange(options.m2, 0, 10000)) {
|
|
269
|
+
this.options.sharpenM2 = options.m2;
|
|
270
|
+
} else {
|
|
271
|
+
throw is.invalidParameterError('options.m2', 'number between 0 and 10000', options.m2);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (is.defined(options.x1)) {
|
|
275
|
+
if (is.number(options.x1) && is.inRange(options.x1, 0, 10000)) {
|
|
276
|
+
this.options.sharpenX1 = options.x1;
|
|
277
|
+
} else {
|
|
278
|
+
throw is.invalidParameterError('options.x1', 'number between 0 and 10000', options.x1);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (is.defined(options.y2)) {
|
|
282
|
+
if (is.number(options.y2) && is.inRange(options.y2, 0, 10000)) {
|
|
283
|
+
this.options.sharpenY2 = options.y2;
|
|
284
|
+
} else {
|
|
285
|
+
throw is.invalidParameterError('options.y2', 'number between 0 and 10000', options.y2);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (is.defined(options.y3)) {
|
|
289
|
+
if (is.number(options.y3) && is.inRange(options.y3, 0, 10000)) {
|
|
290
|
+
this.options.sharpenY3 = options.y3;
|
|
291
|
+
} else {
|
|
292
|
+
throw is.invalidParameterError('options.y3', 'number between 0 and 10000', options.y3);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
220
295
|
} else {
|
|
221
|
-
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000',
|
|
296
|
+
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', options);
|
|
222
297
|
}
|
|
223
298
|
return this;
|
|
224
299
|
}
|
|
@@ -226,6 +301,13 @@ function sharpen (sigma, flat, jagged) {
|
|
|
226
301
|
/**
|
|
227
302
|
* Apply median filter.
|
|
228
303
|
* When used without parameters the default window is 3x3.
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* const output = await sharp(input).median().toBuffer();
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* const output = await sharp(input).median(5).toBuffer();
|
|
310
|
+
*
|
|
229
311
|
* @param {number} [size=3] square mask size: size x size
|
|
230
312
|
* @returns {Sharp}
|
|
231
313
|
* @throws {Error} Invalid parameters
|
|
@@ -338,6 +420,17 @@ function gamma (gamma, gammaOut) {
|
|
|
338
420
|
|
|
339
421
|
/**
|
|
340
422
|
* Produce the "negative" of the image.
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* const output = await sharp(input)
|
|
426
|
+
* .negate()
|
|
427
|
+
* .toBuffer();
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* const output = await sharp(input)
|
|
431
|
+
* .negate({ alpha: false })
|
|
432
|
+
* .toBuffer();
|
|
433
|
+
*
|
|
341
434
|
* @param {Object} [options]
|
|
342
435
|
* @param {Boolean} [options.alpha=true] Whether or not to negate any alpha channel
|
|
343
436
|
* @returns {Sharp}
|
|
@@ -356,6 +449,10 @@ function negate (options) {
|
|
|
356
449
|
|
|
357
450
|
/**
|
|
358
451
|
* Enhance output image contrast by stretching its luminance to cover the full dynamic range.
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* const output = await sharp(input).normalise().toBuffer();
|
|
455
|
+
*
|
|
359
456
|
* @param {Boolean} [normalise=true]
|
|
360
457
|
* @returns {Sharp}
|
|
361
458
|
*/
|
|
@@ -366,6 +463,10 @@ function normalise (normalise) {
|
|
|
366
463
|
|
|
367
464
|
/**
|
|
368
465
|
* Alternative spelling of normalise.
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* const output = await sharp(input).normalize().toBuffer();
|
|
469
|
+
*
|
|
369
470
|
* @param {Boolean} [normalize=true]
|
|
370
471
|
* @returns {Sharp}
|
|
371
472
|
*/
|
|
@@ -381,6 +482,14 @@ function normalize (normalize) {
|
|
|
381
482
|
*
|
|
382
483
|
* @since 0.28.3
|
|
383
484
|
*
|
|
485
|
+
* @example
|
|
486
|
+
* const output = await sharp(input)
|
|
487
|
+
* .clahe({
|
|
488
|
+
* width: 3,
|
|
489
|
+
* height: 3,
|
|
490
|
+
* })
|
|
491
|
+
* .toBuffer();
|
|
492
|
+
*
|
|
384
493
|
* @param {Object} options
|
|
385
494
|
* @param {number} options.width - integer width of the region in pixels.
|
|
386
495
|
* @param {number} options.height - integer height of the region in pixels.
|
|
@@ -590,28 +699,38 @@ function recomb (inputMatrix) {
|
|
|
590
699
|
* @since 0.22.1
|
|
591
700
|
*
|
|
592
701
|
* @example
|
|
593
|
-
*
|
|
702
|
+
* // increase brightness by a factor of 2
|
|
703
|
+
* const output = await sharp(input)
|
|
594
704
|
* .modulate({
|
|
595
|
-
* brightness: 2
|
|
596
|
-
* })
|
|
705
|
+
* brightness: 2
|
|
706
|
+
* })
|
|
707
|
+
* .toBuffer();
|
|
597
708
|
*
|
|
598
|
-
*
|
|
709
|
+
* @example
|
|
710
|
+
* // hue-rotate by 180 degrees
|
|
711
|
+
* const output = await sharp(input)
|
|
599
712
|
* .modulate({
|
|
600
|
-
* hue: 180
|
|
601
|
-
* })
|
|
713
|
+
* hue: 180
|
|
714
|
+
* })
|
|
715
|
+
* .toBuffer();
|
|
602
716
|
*
|
|
603
|
-
*
|
|
717
|
+
* @example
|
|
718
|
+
* // increase lightness by +50
|
|
719
|
+
* const output = await sharp(input)
|
|
604
720
|
* .modulate({
|
|
605
|
-
* lightness: 50
|
|
606
|
-
* })
|
|
721
|
+
* lightness: 50
|
|
722
|
+
* })
|
|
723
|
+
* .toBuffer();
|
|
607
724
|
*
|
|
725
|
+
* @example
|
|
608
726
|
* // decreate brightness and saturation while also hue-rotating by 90 degrees
|
|
609
|
-
* sharp(input)
|
|
727
|
+
* const output = await sharp(input)
|
|
610
728
|
* .modulate({
|
|
611
729
|
* brightness: 0.5,
|
|
612
730
|
* saturation: 0.5,
|
|
613
|
-
* hue: 90
|
|
614
|
-
* })
|
|
731
|
+
* hue: 90,
|
|
732
|
+
* })
|
|
733
|
+
* .toBuffer();
|
|
615
734
|
*
|
|
616
735
|
* @param {Object} [options]
|
|
617
736
|
* @param {number} [options.brightness] Brightness multiplier
|
package/lib/output.js
CHANGED
|
@@ -82,6 +82,8 @@ function toFile (fileOut, callback) {
|
|
|
82
82
|
* Write output to a Buffer.
|
|
83
83
|
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
|
84
84
|
*
|
|
85
|
+
* Use {@link toFormat} or one of the format-specific functions such as {@link jpeg}, {@link png} etc. to set the output format.
|
|
86
|
+
*
|
|
85
87
|
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
|
86
88
|
*
|
|
87
89
|
* By default all metadata will be removed, which includes EXIF-based orientation.
|
|
@@ -108,6 +110,7 @@ function toFile (fileOut, callback) {
|
|
|
108
110
|
*
|
|
109
111
|
* @example
|
|
110
112
|
* sharp(input)
|
|
113
|
+
* .png()
|
|
111
114
|
* .toBuffer({ resolveWithObject: true })
|
|
112
115
|
* .then(({ data, info }) => { ... })
|
|
113
116
|
* .catch(err => { ... });
|
|
@@ -151,6 +154,8 @@ function toBuffer (options, callback) {
|
|
|
151
154
|
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
|
152
155
|
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
|
153
156
|
*
|
|
157
|
+
* EXIF metadata is unsupported for TIFF output.
|
|
158
|
+
*
|
|
154
159
|
* @example
|
|
155
160
|
* sharp('input.jpg')
|
|
156
161
|
* .withMetadata()
|
|
@@ -514,7 +519,7 @@ function webp (options) {
|
|
|
514
519
|
* // Convert PNG to GIF
|
|
515
520
|
* await sharp(pngBuffer)
|
|
516
521
|
* .gif()
|
|
517
|
-
* .toBuffer()
|
|
522
|
+
* .toBuffer();
|
|
518
523
|
*
|
|
519
524
|
* @example
|
|
520
525
|
* // Convert animated WebP to animated GIF
|
package/lib/platform.js
CHANGED
|
@@ -7,10 +7,12 @@ const env = process.env;
|
|
|
7
7
|
module.exports = function () {
|
|
8
8
|
const arch = env.npm_config_arch || process.arch;
|
|
9
9
|
const platform = env.npm_config_platform || process.platform;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const libc = process.env.npm_config_libc ||
|
|
11
|
+
/* istanbul ignore next */
|
|
12
|
+
(detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '');
|
|
13
|
+
const libcId = platform !== 'linux' || libc === detectLibc.GLIBC ? '' : libc;
|
|
12
14
|
|
|
13
|
-
const platformId = [`${platform}${
|
|
15
|
+
const platformId = [`${platform}${libcId}`];
|
|
14
16
|
|
|
15
17
|
if (arch === 'arm') {
|
|
16
18
|
const fallback = process.versions.electron ? '7' : '6';
|
package/lib/sharp.js
CHANGED
|
@@ -13,7 +13,7 @@ try {
|
|
|
13
13
|
} else {
|
|
14
14
|
const [platform, arch] = platformAndArch.split('-');
|
|
15
15
|
help.push(
|
|
16
|
-
'- Install with
|
|
16
|
+
'- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"',
|
|
17
17
|
`- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"`
|
|
18
18
|
);
|
|
19
19
|
}
|
|
@@ -21,7 +21,7 @@ try {
|
|
|
21
21
|
'- Consult the installation documentation: https://sharp.pixelplumbing.com/install'
|
|
22
22
|
);
|
|
23
23
|
// Check loaded
|
|
24
|
-
if (process.platform === 'win32') {
|
|
24
|
+
if (process.platform === 'win32' || /symbol/.test(err.message)) {
|
|
25
25
|
const loadedModule = Object.keys(require.cache).find((i) => /[\\/]build[\\/]Release[\\/]sharp(.*)\.node$/.test(i));
|
|
26
26
|
if (loadedModule) {
|
|
27
27
|
const [, loadedPackage] = loadedModule.match(/node_modules[\\/]([^\\/]+)[\\/]/);
|
package/lib/utility.js
CHANGED
|
@@ -103,7 +103,11 @@ cache(true);
|
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* Gets or, when a concurrency is provided, sets
|
|
106
|
-
* the number of threads
|
|
106
|
+
* the maximum number of threads _libvips_ should use to process _each image_.
|
|
107
|
+
* These are from a thread pool managed by glib,
|
|
108
|
+
* which helps avoid the overhead of creating new threads.
|
|
109
|
+
*
|
|
110
|
+
* This method always returns the current concurrency.
|
|
107
111
|
*
|
|
108
112
|
* The default value is the number of CPU cores,
|
|
109
113
|
* except when using glibc-based Linux without jemalloc,
|
|
@@ -111,10 +115,19 @@ cache(true);
|
|
|
111
115
|
*
|
|
112
116
|
* A value of `0` will reset this to the number of CPU cores.
|
|
113
117
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
118
|
+
* Some image format libraries spawn additional threads,
|
|
119
|
+
* e.g. libaom manages its own 4 threads when encoding AVIF images,
|
|
120
|
+
* and these are independent of the value set here.
|
|
116
121
|
*
|
|
117
|
-
*
|
|
122
|
+
* The maximum number of images that sharp can process in parallel
|
|
123
|
+
* is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
|
|
124
|
+
* which defaults to 4.
|
|
125
|
+
*
|
|
126
|
+
* https://nodejs.org/api/cli.html#uv_threadpool_sizesize
|
|
127
|
+
*
|
|
128
|
+
* For example, by default, a machine with 8 CPU cores will process
|
|
129
|
+
* 4 images in parallel and use up to 8 threads per image,
|
|
130
|
+
* so there will be up to 32 concurrent threads.
|
|
118
131
|
*
|
|
119
132
|
* @example
|
|
120
133
|
* const threads = sharp.concurrency(); // 4
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sharp",
|
|
3
3
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
|
4
|
-
"version": "0.30.
|
|
4
|
+
"version": "0.30.5",
|
|
5
5
|
"author": "Lovell Fuller <npm@lovell.info>",
|
|
6
6
|
"homepage": "https://github.com/lovell/sharp",
|
|
7
7
|
"contributors": [
|
|
@@ -81,7 +81,9 @@
|
|
|
81
81
|
"Taneli Vatanen <taneli.vatanen@gmail.com>",
|
|
82
82
|
"Joris Dugué <zaruike10@gmail.com>",
|
|
83
83
|
"Chris Banks <christopher.bradley.banks@gmail.com>",
|
|
84
|
-
"Ompal Singh <ompal.hitm09@gmail.com>"
|
|
84
|
+
"Ompal Singh <ompal.hitm09@gmail.com>",
|
|
85
|
+
"Brodan <christopher.hranj@gmail.com",
|
|
86
|
+
"Ankur Parihar <ankur.github@gmail.com>"
|
|
85
87
|
],
|
|
86
88
|
"scripts": {
|
|
87
89
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
|
|
@@ -126,11 +128,11 @@
|
|
|
126
128
|
"vips"
|
|
127
129
|
],
|
|
128
130
|
"dependencies": {
|
|
129
|
-
"color": "^4.2.
|
|
131
|
+
"color": "^4.2.3",
|
|
130
132
|
"detect-libc": "^2.0.1",
|
|
131
|
-
"node-addon-api": "^
|
|
132
|
-
"prebuild-install": "^7.0
|
|
133
|
-
"semver": "^7.3.
|
|
133
|
+
"node-addon-api": "^5.0.0",
|
|
134
|
+
"prebuild-install": "^7.1.0",
|
|
135
|
+
"semver": "^7.3.7",
|
|
134
136
|
"simple-get": "^4.0.1",
|
|
135
137
|
"tar-fs": "^2.1.1",
|
|
136
138
|
"tunnel-agent": "^0.6.0"
|
|
@@ -143,7 +145,7 @@
|
|
|
143
145
|
"exif-reader": "^1.0.3",
|
|
144
146
|
"icc": "^2.0.0",
|
|
145
147
|
"license-checker": "^25.0.1",
|
|
146
|
-
"mocha": "^
|
|
148
|
+
"mocha": "^10.0.0",
|
|
147
149
|
"mock-fs": "^5.1.2",
|
|
148
150
|
"nyc": "^15.1.0",
|
|
149
151
|
"prebuild": "^11.0.3",
|
package/src/common.cc
CHANGED
|
@@ -85,7 +85,9 @@ namespace sharp {
|
|
|
85
85
|
descriptor->buffer = buffer.Data();
|
|
86
86
|
descriptor->isBuffer = TRUE;
|
|
87
87
|
}
|
|
88
|
-
descriptor->
|
|
88
|
+
descriptor->failOn = static_cast<VipsFailOn>(
|
|
89
|
+
vips_enum_from_nick(nullptr, VIPS_TYPE_FAIL_ON,
|
|
90
|
+
AttrAsStr(input, "failOn").data()));
|
|
89
91
|
// Density for vector-based input
|
|
90
92
|
if (HasAttr(input, "density")) {
|
|
91
93
|
descriptor->density = AttrAsDouble(input, "density");
|
|
@@ -329,7 +331,7 @@ namespace sharp {
|
|
|
329
331
|
try {
|
|
330
332
|
vips::VOption *option = VImage::option()
|
|
331
333
|
->set("access", descriptor->access)
|
|
332
|
-
->set("
|
|
334
|
+
->set("fail_on", descriptor->failOn);
|
|
333
335
|
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
|
|
334
336
|
option->set("unlimited", TRUE);
|
|
335
337
|
}
|
|
@@ -375,12 +377,6 @@ namespace sharp {
|
|
|
375
377
|
VImage::option()->set("mean", descriptor->createNoiseMean)->set("sigma", descriptor->createNoiseSigma)));
|
|
376
378
|
}
|
|
377
379
|
image = image.bandjoin(bands);
|
|
378
|
-
image = image.cast(VipsBandFormat::VIPS_FORMAT_UCHAR);
|
|
379
|
-
if (channels < 3) {
|
|
380
|
-
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
|
381
|
-
} else {
|
|
382
|
-
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
383
|
-
}
|
|
384
380
|
} else {
|
|
385
381
|
std::vector<double> background = {
|
|
386
382
|
descriptor->createBackground[0],
|
|
@@ -392,7 +388,8 @@ namespace sharp {
|
|
|
392
388
|
}
|
|
393
389
|
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
|
394
390
|
}
|
|
395
|
-
image.get_image()->Type =
|
|
391
|
+
image.get_image()->Type = image.guess_interpretation();
|
|
392
|
+
image = image.cast(VIPS_FORMAT_UCHAR);
|
|
396
393
|
imageType = ImageType::RAW;
|
|
397
394
|
} else {
|
|
398
395
|
// From filesystem
|
|
@@ -402,13 +399,13 @@ namespace sharp {
|
|
|
402
399
|
throw vips::VError("Input file is missing, did you mean "
|
|
403
400
|
"sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");
|
|
404
401
|
}
|
|
405
|
-
throw vips::VError("Input file is missing");
|
|
402
|
+
throw vips::VError("Input file is missing: " + descriptor->file);
|
|
406
403
|
}
|
|
407
404
|
if (imageType != ImageType::UNKNOWN) {
|
|
408
405
|
try {
|
|
409
406
|
vips::VOption *option = VImage::option()
|
|
410
407
|
->set("access", descriptor->access)
|
|
411
|
-
->set("
|
|
408
|
+
->set("fail_on", descriptor->failOn);
|
|
412
409
|
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
|
|
413
410
|
option->set("unlimited", TRUE);
|
|
414
411
|
}
|
package/src/common.h
CHANGED
|
@@ -48,7 +48,7 @@ namespace sharp {
|
|
|
48
48
|
std::string name;
|
|
49
49
|
std::string file;
|
|
50
50
|
char *buffer;
|
|
51
|
-
|
|
51
|
+
VipsFailOn failOn;
|
|
52
52
|
int limitInputPixels;
|
|
53
53
|
bool unlimited;
|
|
54
54
|
VipsAccess access;
|
|
@@ -74,7 +74,7 @@ namespace sharp {
|
|
|
74
74
|
|
|
75
75
|
InputDescriptor():
|
|
76
76
|
buffer(nullptr),
|
|
77
|
-
|
|
77
|
+
failOn(VIPS_FAIL_ON_WARNING),
|
|
78
78
|
limitInputPixels(0x3FFF * 0x3FFF),
|
|
79
79
|
unlimited(FALSE),
|
|
80
80
|
access(VIPS_ACCESS_RANDOM),
|
package/src/operations.cc
CHANGED
|
@@ -209,7 +209,8 @@ namespace sharp {
|
|
|
209
209
|
/*
|
|
210
210
|
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
|
|
211
211
|
*/
|
|
212
|
-
VImage Sharpen(VImage image, double const sigma, double const
|
|
212
|
+
VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
|
|
213
|
+
double const x1, double const y2, double const y3) {
|
|
213
214
|
if (sigma == -1.0) {
|
|
214
215
|
// Fast, mild sharpen
|
|
215
216
|
VImage sharpen = VImage::new_matrixv(3, 3,
|
|
@@ -224,8 +225,14 @@ namespace sharp {
|
|
|
224
225
|
if (colourspaceBeforeSharpen == VIPS_INTERPRETATION_RGB) {
|
|
225
226
|
colourspaceBeforeSharpen = VIPS_INTERPRETATION_sRGB;
|
|
226
227
|
}
|
|
227
|
-
return image
|
|
228
|
-
VImage::option()
|
|
228
|
+
return image
|
|
229
|
+
.sharpen(VImage::option()
|
|
230
|
+
->set("sigma", sigma)
|
|
231
|
+
->set("m1", m1)
|
|
232
|
+
->set("m2", m2)
|
|
233
|
+
->set("x1", x1)
|
|
234
|
+
->set("y2", y2)
|
|
235
|
+
->set("y3", y3))
|
|
229
236
|
.colourspace(colourspaceBeforeSharpen);
|
|
230
237
|
}
|
|
231
238
|
}
|
package/src/operations.h
CHANGED
|
@@ -64,7 +64,8 @@ namespace sharp {
|
|
|
64
64
|
/*
|
|
65
65
|
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
|
|
66
66
|
*/
|
|
67
|
-
VImage Sharpen(VImage image, double const sigma, double const
|
|
67
|
+
VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
|
|
68
|
+
double const x1, double const y2, double const y3);
|
|
68
69
|
|
|
69
70
|
/*
|
|
70
71
|
Threshold an image
|
package/src/pipeline.cc
CHANGED
|
@@ -95,16 +95,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
95
95
|
if (baton->rotateBeforePreExtract) {
|
|
96
96
|
if (rotation != VIPS_ANGLE_D0) {
|
|
97
97
|
image = image.rot(rotation);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
}
|
|
99
|
+
if (flip) {
|
|
100
|
+
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
101
|
+
}
|
|
102
|
+
if (flop) {
|
|
103
|
+
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
104
|
+
}
|
|
105
|
+
if (rotation != VIPS_ANGLE_D0 || flip || flop) {
|
|
106
106
|
image = sharp::RemoveExifOrientation(image);
|
|
107
107
|
}
|
|
108
|
+
flop = FALSE;
|
|
109
|
+
flip = FALSE;
|
|
108
110
|
if (baton->rotationAngle != 0.0) {
|
|
109
111
|
MultiPageUnsupported(nPages, "Rotate");
|
|
110
112
|
std::vector<double> background;
|
|
@@ -200,7 +202,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
200
202
|
vips::VOption *option = VImage::option()
|
|
201
203
|
->set("access", baton->input->access)
|
|
202
204
|
->set("shrink", jpegShrinkOnLoad)
|
|
203
|
-
->set("
|
|
205
|
+
->set("fail_on", baton->input->failOn);
|
|
204
206
|
if (baton->input->buffer != nullptr) {
|
|
205
207
|
// Reload JPEG buffer
|
|
206
208
|
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
|
@@ -214,7 +216,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
214
216
|
vips::VOption *option = VImage::option()
|
|
215
217
|
->set("access", baton->input->access)
|
|
216
218
|
->set("scale", scale)
|
|
217
|
-
->set("
|
|
219
|
+
->set("fail_on", baton->input->failOn);
|
|
218
220
|
if (inputImageType == sharp::ImageType::WEBP) {
|
|
219
221
|
option->set("n", baton->input->pages);
|
|
220
222
|
option->set("page", baton->input->page);
|
|
@@ -241,8 +243,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
241
243
|
// Reload SVG file
|
|
242
244
|
image = VImage::svgload(const_cast<char*>(baton->input->file.data()), option);
|
|
243
245
|
}
|
|
244
|
-
|
|
245
246
|
sharp::SetDensity(image, baton->input->density);
|
|
247
|
+
if (image.width() > 32767 || image.height() > 32767) {
|
|
248
|
+
throw vips::VError("Input SVG image will exceed 32767x32767 pixel limit when scaled");
|
|
249
|
+
}
|
|
246
250
|
} else if (inputImageType == sharp::ImageType::PDF) {
|
|
247
251
|
option->set("n", baton->input->pages);
|
|
248
252
|
option->set("page", baton->input->page);
|
|
@@ -260,6 +264,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
260
264
|
|
|
261
265
|
sharp::SetDensity(image, baton->input->density);
|
|
262
266
|
}
|
|
267
|
+
} else {
|
|
268
|
+
if (inputImageType == sharp::ImageType::SVG && (image.width() > 32767 || image.height() > 32767)) {
|
|
269
|
+
throw vips::VError("Input SVG image exceeds 32767x32767 pixel limit");
|
|
270
|
+
}
|
|
263
271
|
}
|
|
264
272
|
|
|
265
273
|
// Any pre-shrinking may already have been done
|
|
@@ -354,7 +362,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
354
362
|
}
|
|
355
363
|
|
|
356
364
|
bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
|
|
357
|
-
(shouldResize || shouldBlur || shouldConv || shouldSharpen
|
|
365
|
+
(shouldResize || shouldBlur || shouldConv || shouldSharpen);
|
|
358
366
|
|
|
359
367
|
// Premultiply image alpha channel before all transformations to avoid
|
|
360
368
|
// dark fringing around bright pixels
|
|
@@ -577,7 +585,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
577
585
|
|
|
578
586
|
// Sharpen
|
|
579
587
|
if (shouldSharpen) {
|
|
580
|
-
image = sharp::Sharpen(image, baton->sharpenSigma, baton->
|
|
588
|
+
image = sharp::Sharpen(image, baton->sharpenSigma, baton->sharpenM1, baton->sharpenM2,
|
|
589
|
+
baton->sharpenX1, baton->sharpenY2, baton->sharpenY3);
|
|
581
590
|
}
|
|
582
591
|
|
|
583
592
|
// Composite
|
|
@@ -1400,8 +1409,11 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1400
1409
|
baton->lightness = sharp::AttrAsDouble(options, "lightness");
|
|
1401
1410
|
baton->medianSize = sharp::AttrAsUint32(options, "medianSize");
|
|
1402
1411
|
baton->sharpenSigma = sharp::AttrAsDouble(options, "sharpenSigma");
|
|
1403
|
-
baton->
|
|
1404
|
-
baton->
|
|
1412
|
+
baton->sharpenM1 = sharp::AttrAsDouble(options, "sharpenM1");
|
|
1413
|
+
baton->sharpenM2 = sharp::AttrAsDouble(options, "sharpenM2");
|
|
1414
|
+
baton->sharpenX1 = sharp::AttrAsDouble(options, "sharpenX1");
|
|
1415
|
+
baton->sharpenY2 = sharp::AttrAsDouble(options, "sharpenY2");
|
|
1416
|
+
baton->sharpenY3 = sharp::AttrAsDouble(options, "sharpenY3");
|
|
1405
1417
|
baton->threshold = sharp::AttrAsInt32(options, "threshold");
|
|
1406
1418
|
baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
|
|
1407
1419
|
baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
|
package/src/pipeline.h
CHANGED
|
@@ -90,8 +90,11 @@ struct PipelineBaton {
|
|
|
90
90
|
double lightness;
|
|
91
91
|
int medianSize;
|
|
92
92
|
double sharpenSigma;
|
|
93
|
-
double
|
|
94
|
-
double
|
|
93
|
+
double sharpenM1;
|
|
94
|
+
double sharpenM2;
|
|
95
|
+
double sharpenX1;
|
|
96
|
+
double sharpenY2;
|
|
97
|
+
double sharpenY3;
|
|
95
98
|
int threshold;
|
|
96
99
|
bool thresholdGrayscale;
|
|
97
100
|
double trimThreshold;
|
|
@@ -234,8 +237,11 @@ struct PipelineBaton {
|
|
|
234
237
|
lightness(0),
|
|
235
238
|
medianSize(0),
|
|
236
239
|
sharpenSigma(0.0),
|
|
237
|
-
|
|
238
|
-
|
|
240
|
+
sharpenM1(1.0),
|
|
241
|
+
sharpenM2(2.0),
|
|
242
|
+
sharpenX1(2.0),
|
|
243
|
+
sharpenY2(10.0),
|
|
244
|
+
sharpenY3(20.0),
|
|
239
245
|
threshold(0),
|
|
240
246
|
thresholdGrayscale(true),
|
|
241
247
|
trimThreshold(0.0),
|