sharp 0.30.7 → 0.31.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 +1 -1
- package/binding.gyp +4 -3
- package/lib/composite.js +14 -0
- package/lib/constructor.js +41 -4
- package/lib/input.js +92 -0
- package/lib/operation.js +57 -12
- package/lib/output.js +87 -23
- package/lib/resize.js +89 -12
- package/lib/utility.js +4 -0
- package/package.json +23 -21
- package/src/common.cc +90 -43
- package/src/common.h +32 -14
- package/src/libvips/cplusplus/VConnection.cpp +0 -1
- package/src/libvips/cplusplus/VError.cpp +0 -1
- package/src/libvips/cplusplus/VImage.cpp +27 -2
- package/src/libvips/cplusplus/VInterpolate.cpp +0 -1
- package/src/libvips/cplusplus/VRegion.cpp +27 -0
- package/src/libvips/cplusplus/vips-operators.cpp +16 -1
- package/src/operations.cc +69 -25
- package/src/operations.h +7 -2
- package/src/pipeline.cc +156 -160
- package/src/pipeline.h +18 -5
- package/src/sharp.cc +0 -1
- package/src/utilities.cc +13 -2
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
|
|
|
16
16
|
As well as image resizing, operations such as
|
|
17
17
|
rotation, extraction, compositing and gamma correction are available.
|
|
18
18
|
|
|
19
|
-
Most modern macOS, Windows and Linux systems running Node.js >=
|
|
19
|
+
Most modern macOS, Windows and Linux systems running Node.js >= 14.15.0
|
|
20
20
|
do not require any additional install or runtime dependencies.
|
|
21
21
|
|
|
22
22
|
## Documentation
|
package/binding.gyp
CHANGED
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
'_ALLOW_KEYWORD_MACROS'
|
|
16
16
|
],
|
|
17
17
|
'sources': [
|
|
18
|
-
'src/libvips/cplusplus/VError.cpp',
|
|
19
18
|
'src/libvips/cplusplus/VConnection.cpp',
|
|
19
|
+
'src/libvips/cplusplus/VError.cpp',
|
|
20
|
+
'src/libvips/cplusplus/VImage.cpp',
|
|
20
21
|
'src/libvips/cplusplus/VInterpolate.cpp',
|
|
21
|
-
'src/libvips/cplusplus/
|
|
22
|
+
'src/libvips/cplusplus/VRegion.cpp'
|
|
22
23
|
],
|
|
23
24
|
'include_dirs': [
|
|
24
25
|
'<(sharp_vendor_dir)/include',
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
}, {
|
|
70
71
|
'target_name': 'sharp-<(platform_and_arch)',
|
|
71
72
|
'defines': [
|
|
72
|
-
'NAPI_VERSION=
|
|
73
|
+
'NAPI_VERSION=7'
|
|
73
74
|
],
|
|
74
75
|
'dependencies': [
|
|
75
76
|
'<!(node -p "require(\'node-addon-api\').gyp")',
|
package/lib/composite.js
CHANGED
|
@@ -43,6 +43,9 @@ const blend = {
|
|
|
43
43
|
* The images to composite must be the same size or smaller than the processed image.
|
|
44
44
|
* If both `top` and `left` options are provided, they take precedence over `gravity`.
|
|
45
45
|
*
|
|
46
|
+
* Any resize or rotate operations in the same processing pipeline
|
|
47
|
+
* will always be applied to the input image before composition.
|
|
48
|
+
*
|
|
46
49
|
* The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
|
|
47
50
|
* `dest`, `dest-over`, `dest-in`, `dest-out`, `dest-atop`,
|
|
48
51
|
* `xor`, `add`, `saturate`, `multiply`, `screen`, `overlay`, `darken`, `lighten`,
|
|
@@ -93,6 +96,17 @@ const blend = {
|
|
|
93
96
|
* @param {Number} [images[].input.create.height]
|
|
94
97
|
* @param {Number} [images[].input.create.channels] - 3-4
|
|
95
98
|
* @param {String|Object} [images[].input.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
|
99
|
+
* @param {Object} [images[].input.text] - describes a new text image to be created.
|
|
100
|
+
* @param {string} [images[].input.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
|
|
101
|
+
* @param {string} [images[].input.text.font] - font name to render with.
|
|
102
|
+
* @param {string} [images[].input.text.fontfile] - absolute filesystem path to a font file that can be used by `font`.
|
|
103
|
+
* @param {number} [images[].input.text.width=0] - integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries.
|
|
104
|
+
* @param {number} [images[].input.text.height=0] - integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0.
|
|
105
|
+
* @param {string} [images[].input.text.align='left'] - text alignment (`'left'`, `'centre'`, `'center'`, `'right'`).
|
|
106
|
+
* @param {boolean} [images[].input.text.justify=false] - set this to true to apply justification to the text.
|
|
107
|
+
* @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
|
|
108
|
+
* @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`.
|
|
109
|
+
* @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
|
|
96
110
|
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
|
|
97
111
|
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
|
|
98
112
|
* @param {Number} [images[].top] - the pixel offset from the top edge.
|
package/lib/constructor.js
CHANGED
|
@@ -92,6 +92,27 @@ const debuglog = util.debuglog('sharp');
|
|
|
92
92
|
* }
|
|
93
93
|
* }).toFile('noise.png');
|
|
94
94
|
*
|
|
95
|
+
* @example
|
|
96
|
+
* // Generate an image from text
|
|
97
|
+
* await sharp({
|
|
98
|
+
* text: {
|
|
99
|
+
* text: 'Hello, world!',
|
|
100
|
+
* width: 400, // max width
|
|
101
|
+
* height: 300 // max height
|
|
102
|
+
* }
|
|
103
|
+
* }).toFile('text_bw.png');
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* // Generate an rgba image from text using pango markup and font
|
|
107
|
+
* await sharp({
|
|
108
|
+
* text: {
|
|
109
|
+
* text: '<span foreground="red">Red!</span><span background="cyan">blue</span>',
|
|
110
|
+
* font: 'sans',
|
|
111
|
+
* rgba: true,
|
|
112
|
+
* dpi: 300
|
|
113
|
+
* }
|
|
114
|
+
* }).toFile('text_rgba.png');
|
|
115
|
+
*
|
|
95
116
|
* @param {(Buffer|Uint8Array|Uint8ClampedArray|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array|Float32Array|Float64Array|string)} [input] - if present, can be
|
|
96
117
|
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
|
|
97
118
|
* a TypedArray containing raw pixel image data, or
|
|
@@ -102,7 +123,7 @@ const debuglog = util.debuglog('sharp');
|
|
|
102
123
|
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
|
|
103
124
|
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
|
104
125
|
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
|
105
|
-
* @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (SVG,
|
|
126
|
+
* @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF).
|
|
106
127
|
* @param {boolean} [options.sequentialRead=false] - Set this to `true` to use sequential rather than random access where possible.
|
|
107
128
|
* This can reduce memory usage and might improve performance on some systems.
|
|
108
129
|
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
|
|
@@ -126,6 +147,17 @@ const debuglog = util.debuglog('sharp');
|
|
|
126
147
|
* @param {string} [options.create.noise.type] - type of generated noise, currently only `gaussian` is supported.
|
|
127
148
|
* @param {number} [options.create.noise.mean] - mean of pixels in generated noise.
|
|
128
149
|
* @param {number} [options.create.noise.sigma] - standard deviation of pixels in generated noise.
|
|
150
|
+
* @param {Object} [options.text] - describes a new text image to be created.
|
|
151
|
+
* @param {string} [options.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
|
|
152
|
+
* @param {string} [options.text.font] - font name to render with.
|
|
153
|
+
* @param {string} [options.text.fontfile] - absolute filesystem path to a font file that can be used by `font`.
|
|
154
|
+
* @param {number} [options.text.width=0] - integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries.
|
|
155
|
+
* @param {number} [options.text.height=0] - integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0.
|
|
156
|
+
* @param {string} [options.text.align='left'] - text alignment (`'left'`, `'centre'`, `'center'`, `'right'`).
|
|
157
|
+
* @param {boolean} [options.text.justify=false] - set this to true to apply justification to the text.
|
|
158
|
+
* @param {number} [options.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
|
|
159
|
+
* @param {boolean} [options.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`.
|
|
160
|
+
* @param {number} [options.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
|
|
129
161
|
* @returns {Sharp}
|
|
130
162
|
* @throws {Error} Invalid parameters
|
|
131
163
|
*/
|
|
@@ -192,6 +224,7 @@ const Sharp = function (input, options) {
|
|
|
192
224
|
sharpenY3: 20,
|
|
193
225
|
threshold: 0,
|
|
194
226
|
thresholdGrayscale: true,
|
|
227
|
+
trimBackground: [],
|
|
195
228
|
trimThreshold: 0,
|
|
196
229
|
gamma: 0,
|
|
197
230
|
gammaOut: 0,
|
|
@@ -251,9 +284,12 @@ const Sharp = function (input, options) {
|
|
|
251
284
|
webpNearLossless: false,
|
|
252
285
|
webpSmartSubsample: false,
|
|
253
286
|
webpEffort: 4,
|
|
287
|
+
webpMinSize: false,
|
|
288
|
+
webpMixed: false,
|
|
254
289
|
gifBitdepth: 8,
|
|
255
290
|
gifEffort: 7,
|
|
256
291
|
gifDither: 1,
|
|
292
|
+
gifReoptimise: false,
|
|
257
293
|
tiffQuality: 80,
|
|
258
294
|
tiffCompression: 'jpeg',
|
|
259
295
|
tiffPredictor: 'horizontal',
|
|
@@ -282,9 +318,10 @@ const Sharp = function (input, options) {
|
|
|
282
318
|
tileBackground: [255, 255, 255, 255],
|
|
283
319
|
tileCentre: false,
|
|
284
320
|
tileId: 'https://example.com/iiif',
|
|
321
|
+
tileBasename: '',
|
|
285
322
|
timeoutSeconds: 0,
|
|
286
|
-
linearA:
|
|
287
|
-
linearB:
|
|
323
|
+
linearA: [],
|
|
324
|
+
linearB: [],
|
|
288
325
|
// Function to notify of libvips warnings
|
|
289
326
|
debuglog: warning => {
|
|
290
327
|
this.emit('warning', warning);
|
|
@@ -346,7 +383,7 @@ Object.setPrototypeOf(Sharp, stream.Duplex);
|
|
|
346
383
|
* .toFile("optimized-500.webp")
|
|
347
384
|
* );
|
|
348
385
|
*
|
|
349
|
-
* // https://github.com/sindresorhus/got
|
|
386
|
+
* // https://github.com/sindresorhus/got/blob/main/documentation/3-streams.md
|
|
350
387
|
* got.stream("https://www.example.com/some-file.jpg").pipe(sharpStream);
|
|
351
388
|
*
|
|
352
389
|
* Promise.all(promises)
|
package/lib/input.js
CHANGED
|
@@ -4,6 +4,18 @@ const color = require('color');
|
|
|
4
4
|
const is = require('./is');
|
|
5
5
|
const sharp = require('./sharp');
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Justication alignment
|
|
9
|
+
* @member
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
const align = {
|
|
13
|
+
left: 'low',
|
|
14
|
+
center: 'centre',
|
|
15
|
+
centre: 'centre',
|
|
16
|
+
right: 'high'
|
|
17
|
+
};
|
|
18
|
+
|
|
7
19
|
/**
|
|
8
20
|
* Extract input options, if any, from an object.
|
|
9
21
|
* @private
|
|
@@ -245,6 +257,81 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
245
257
|
throw new Error('Expected valid width, height and channels to create a new input image');
|
|
246
258
|
}
|
|
247
259
|
}
|
|
260
|
+
// Create a new image with text
|
|
261
|
+
if (is.defined(inputOptions.text)) {
|
|
262
|
+
if (is.object(inputOptions.text) && is.string(inputOptions.text.text)) {
|
|
263
|
+
inputDescriptor.textValue = inputOptions.text.text;
|
|
264
|
+
if (is.defined(inputOptions.text.height) && is.defined(inputOptions.text.dpi)) {
|
|
265
|
+
throw new Error('Expected only one of dpi or height');
|
|
266
|
+
}
|
|
267
|
+
if (is.defined(inputOptions.text.font)) {
|
|
268
|
+
if (is.string(inputOptions.text.font)) {
|
|
269
|
+
inputDescriptor.textFont = inputOptions.text.font;
|
|
270
|
+
} else {
|
|
271
|
+
throw is.invalidParameterError('text.font', 'string', inputOptions.text.font);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (is.defined(inputOptions.text.fontfile)) {
|
|
275
|
+
if (is.string(inputOptions.text.fontfile)) {
|
|
276
|
+
inputDescriptor.textFontfile = inputOptions.text.fontfile;
|
|
277
|
+
} else {
|
|
278
|
+
throw is.invalidParameterError('text.fontfile', 'string', inputOptions.text.fontfile);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (is.defined(inputOptions.text.width)) {
|
|
282
|
+
if (is.number(inputOptions.text.width)) {
|
|
283
|
+
inputDescriptor.textWidth = inputOptions.text.width;
|
|
284
|
+
} else {
|
|
285
|
+
throw is.invalidParameterError('text.textWidth', 'number', inputOptions.text.width);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (is.defined(inputOptions.text.height)) {
|
|
289
|
+
if (is.number(inputOptions.text.height)) {
|
|
290
|
+
inputDescriptor.textHeight = inputOptions.text.height;
|
|
291
|
+
} else {
|
|
292
|
+
throw is.invalidParameterError('text.height', 'number', inputOptions.text.height);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (is.defined(inputOptions.text.align)) {
|
|
296
|
+
if (is.string(inputOptions.text.align) && is.string(this.constructor.align[inputOptions.text.align])) {
|
|
297
|
+
inputDescriptor.textAlign = this.constructor.align[inputOptions.text.align];
|
|
298
|
+
} else {
|
|
299
|
+
throw is.invalidParameterError('text.align', 'valid alignment', inputOptions.text.align);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (is.defined(inputOptions.text.justify)) {
|
|
303
|
+
if (is.bool(inputOptions.text.justify)) {
|
|
304
|
+
inputDescriptor.textJustify = inputOptions.text.justify;
|
|
305
|
+
} else {
|
|
306
|
+
throw is.invalidParameterError('text.justify', 'boolean', inputOptions.text.justify);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (is.defined(inputOptions.text.dpi)) {
|
|
310
|
+
if (is.number(inputOptions.text.dpi) && is.inRange(inputOptions.text.dpi, 1, 100000)) {
|
|
311
|
+
inputDescriptor.textDpi = inputOptions.text.dpi;
|
|
312
|
+
} else {
|
|
313
|
+
throw is.invalidParameterError('text.dpi', 'number between 1 and 100000', inputOptions.text.dpi);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (is.defined(inputOptions.text.rgba)) {
|
|
317
|
+
if (is.bool(inputOptions.text.rgba)) {
|
|
318
|
+
inputDescriptor.textRgba = inputOptions.text.rgba;
|
|
319
|
+
} else {
|
|
320
|
+
throw is.invalidParameterError('text.rgba', 'bool', inputOptions.text.rgba);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (is.defined(inputOptions.text.spacing)) {
|
|
324
|
+
if (is.number(inputOptions.text.spacing)) {
|
|
325
|
+
inputDescriptor.textSpacing = inputOptions.text.spacing;
|
|
326
|
+
} else {
|
|
327
|
+
throw is.invalidParameterError('text.spacing', 'number', inputOptions.text.spacing);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
delete inputDescriptor.buffer;
|
|
331
|
+
} else {
|
|
332
|
+
throw new Error('Expected a valid string to create an image with text.');
|
|
333
|
+
}
|
|
334
|
+
}
|
|
248
335
|
} else if (is.defined(inputOptions)) {
|
|
249
336
|
throw new Error('Invalid input options ' + inputOptions);
|
|
250
337
|
}
|
|
@@ -303,6 +390,9 @@ function _isStreamInput () {
|
|
|
303
390
|
* This is taken from the header of the input image.
|
|
304
391
|
* It does not include operations, such as resize, to be applied to the output image.
|
|
305
392
|
*
|
|
393
|
+
* Dimensions in the response will respect the `page` and `pages` properties of the
|
|
394
|
+
* {@link /api-constructor#parameters|constructor parameters}.
|
|
395
|
+
*
|
|
306
396
|
* A `Promise` is returned when `callback` is not provided.
|
|
307
397
|
*
|
|
308
398
|
* - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
|
|
@@ -504,4 +594,6 @@ module.exports = function (Sharp) {
|
|
|
504
594
|
metadata,
|
|
505
595
|
stats
|
|
506
596
|
});
|
|
597
|
+
// Class attributes
|
|
598
|
+
Sharp.align = align;
|
|
507
599
|
};
|
package/lib/operation.js
CHANGED
|
@@ -18,8 +18,11 @@ const is = require('./is');
|
|
|
18
18
|
*
|
|
19
19
|
* The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* Only one rotation can occur per pipeline.
|
|
22
|
+
* Previous calls to `rotate` in the same pipeline will be ignored.
|
|
23
|
+
*
|
|
24
|
+
* Method order is important when rotating, resizing and/or extracting regions,
|
|
25
|
+
* for example `.rotate(x).extract(y)` will produce a different result to `.extract(y).rotate(x)`.
|
|
23
26
|
*
|
|
24
27
|
* @example
|
|
25
28
|
* const pipeline = sharp()
|
|
@@ -32,6 +35,16 @@ const is = require('./is');
|
|
|
32
35
|
* });
|
|
33
36
|
* readableStream.pipe(pipeline);
|
|
34
37
|
*
|
|
38
|
+
* @example
|
|
39
|
+
* const rotateThenResize = await sharp(input)
|
|
40
|
+
* .rotate(90)
|
|
41
|
+
* .resize({ width: 16, height: 8, fit: 'fill' })
|
|
42
|
+
* .toBuffer();
|
|
43
|
+
* const resizeThenRotate = await sharp(input)
|
|
44
|
+
* .resize({ width: 16, height: 8, fit: 'fill' })
|
|
45
|
+
* .rotate(90)
|
|
46
|
+
* .toBuffer();
|
|
47
|
+
*
|
|
35
48
|
* @param {number} [angle=auto] angle of rotation.
|
|
36
49
|
* @param {Object} [options] - if present, is an Object with optional attributes.
|
|
37
50
|
* @param {string|Object} [options.background="#000000"] parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
|
@@ -39,6 +52,9 @@ const is = require('./is');
|
|
|
39
52
|
* @throws {Error} Invalid parameters
|
|
40
53
|
*/
|
|
41
54
|
function rotate (angle, options) {
|
|
55
|
+
if (this.options.useExifOrientation || this.options.angle || this.options.rotationAngle) {
|
|
56
|
+
this.options.debuglog('ignoring previous rotate options');
|
|
57
|
+
}
|
|
42
58
|
if (!is.defined(angle)) {
|
|
43
59
|
this.options.useExifOrientation = true;
|
|
44
60
|
} else if (is.integer(angle) && !(angle % 90)) {
|
|
@@ -61,7 +77,7 @@ function rotate (angle, options) {
|
|
|
61
77
|
}
|
|
62
78
|
|
|
63
79
|
/**
|
|
64
|
-
* Flip the image about the vertical Y axis. This always occurs
|
|
80
|
+
* Flip the image about the vertical Y axis. This always occurs before rotation, if any.
|
|
65
81
|
* The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
|
66
82
|
*
|
|
67
83
|
* @example
|
|
@@ -76,7 +92,7 @@ function flip (flip) {
|
|
|
76
92
|
}
|
|
77
93
|
|
|
78
94
|
/**
|
|
79
|
-
* Flop the image about the horizontal X axis. This always occurs
|
|
95
|
+
* Flop the image about the horizontal X axis. This always occurs before rotation, if any.
|
|
80
96
|
* The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
|
81
97
|
*
|
|
82
98
|
* @example
|
|
@@ -205,7 +221,7 @@ function affine (matrix, options) {
|
|
|
205
221
|
* const data = await sharp(input)
|
|
206
222
|
* .sharpen({
|
|
207
223
|
* sigma: 2,
|
|
208
|
-
* m1: 0
|
|
224
|
+
* m1: 0,
|
|
209
225
|
* m2: 3,
|
|
210
226
|
* x1: 3,
|
|
211
227
|
* y2: 15,
|
|
@@ -628,26 +644,55 @@ function boolean (operand, operator, options) {
|
|
|
628
644
|
}
|
|
629
645
|
|
|
630
646
|
/**
|
|
631
|
-
* Apply the linear formula a * input + b to the image
|
|
632
|
-
*
|
|
633
|
-
*
|
|
647
|
+
* Apply the linear formula `a` * input + `b` to the image to adjust image levels.
|
|
648
|
+
*
|
|
649
|
+
* When a single number is provided, it will be used for all image channels.
|
|
650
|
+
* When an array of numbers is provided, the array length must match the number of channels.
|
|
651
|
+
*
|
|
652
|
+
* @example
|
|
653
|
+
* await sharp(input)
|
|
654
|
+
* .linear(0.5, 2)
|
|
655
|
+
* .toBuffer();
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* await sharp(rgbInput)
|
|
659
|
+
* .linear(
|
|
660
|
+
* [0.25, 0.5, 0.75],
|
|
661
|
+
* [150, 100, 50]
|
|
662
|
+
* )
|
|
663
|
+
* .toBuffer();
|
|
664
|
+
*
|
|
665
|
+
* @param {(number|number[])} [a=[]] multiplier
|
|
666
|
+
* @param {(number|number[])} [b=[]] offset
|
|
634
667
|
* @returns {Sharp}
|
|
635
668
|
* @throws {Error} Invalid parameters
|
|
636
669
|
*/
|
|
637
670
|
function linear (a, b) {
|
|
671
|
+
if (!is.defined(a) && is.number(b)) {
|
|
672
|
+
a = 1.0;
|
|
673
|
+
} else if (is.number(a) && !is.defined(b)) {
|
|
674
|
+
b = 0.0;
|
|
675
|
+
}
|
|
638
676
|
if (!is.defined(a)) {
|
|
639
|
-
this.options.linearA =
|
|
677
|
+
this.options.linearA = [];
|
|
640
678
|
} else if (is.number(a)) {
|
|
679
|
+
this.options.linearA = [a];
|
|
680
|
+
} else if (Array.isArray(a) && a.length && a.every(is.number)) {
|
|
641
681
|
this.options.linearA = a;
|
|
642
682
|
} else {
|
|
643
|
-
throw is.invalidParameterError('a', '
|
|
683
|
+
throw is.invalidParameterError('a', 'number or array of numbers', a);
|
|
644
684
|
}
|
|
645
685
|
if (!is.defined(b)) {
|
|
646
|
-
this.options.linearB =
|
|
686
|
+
this.options.linearB = [];
|
|
647
687
|
} else if (is.number(b)) {
|
|
688
|
+
this.options.linearB = [b];
|
|
689
|
+
} else if (Array.isArray(b) && b.length && b.every(is.number)) {
|
|
648
690
|
this.options.linearB = b;
|
|
649
691
|
} else {
|
|
650
|
-
throw is.invalidParameterError('b', '
|
|
692
|
+
throw is.invalidParameterError('b', 'number or array of numbers', b);
|
|
693
|
+
}
|
|
694
|
+
if (this.options.linearA.length !== this.options.linearB.length) {
|
|
695
|
+
throw new Error('Expected a and b to be arrays of the same length');
|
|
651
696
|
}
|
|
652
697
|
return this;
|
|
653
698
|
}
|
package/lib/output.js
CHANGED
|
@@ -10,6 +10,9 @@ const formats = new Map([
|
|
|
10
10
|
['avif', 'avif'],
|
|
11
11
|
['jpeg', 'jpeg'],
|
|
12
12
|
['jpg', 'jpeg'],
|
|
13
|
+
['jpe', 'jpeg'],
|
|
14
|
+
['tile', 'tile'],
|
|
15
|
+
['dz', 'tile'],
|
|
13
16
|
['png', 'png'],
|
|
14
17
|
['raw', 'raw'],
|
|
15
18
|
['tiff', 'tiff'],
|
|
@@ -55,6 +58,7 @@ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math
|
|
|
55
58
|
* `info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
|
56
59
|
* `channels` and `premultiplied` (indicating if premultiplication was used).
|
|
57
60
|
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
|
|
61
|
+
* May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
|
|
58
62
|
* @returns {Promise<Object>} - when no callback is provided
|
|
59
63
|
* @throws {Error} Invalid parameters
|
|
60
64
|
*/
|
|
@@ -95,6 +99,7 @@ function toFile (fileOut, callback) {
|
|
|
95
99
|
* - `info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
|
96
100
|
* `channels` and `premultiplied` (indicating if premultiplication was used).
|
|
97
101
|
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
|
|
102
|
+
* May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
|
|
98
103
|
*
|
|
99
104
|
* A `Promise` is returned when `callback` is not provided.
|
|
100
105
|
*
|
|
@@ -402,6 +407,14 @@ function png (options) {
|
|
|
402
407
|
if (is.defined(options.adaptiveFiltering)) {
|
|
403
408
|
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
|
|
404
409
|
}
|
|
410
|
+
const colours = options.colours || options.colors;
|
|
411
|
+
if (is.defined(colours)) {
|
|
412
|
+
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
|
413
|
+
this.options.pngBitdepth = bitdepthFromColourCount(colours);
|
|
414
|
+
} else {
|
|
415
|
+
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
405
418
|
if (is.defined(options.palette)) {
|
|
406
419
|
this._setBooleanOption('pngPalette', options.palette);
|
|
407
420
|
} else if ([options.quality, options.effort, options.colours, options.colors, options.dither].some(is.defined)) {
|
|
@@ -422,14 +435,6 @@ function png (options) {
|
|
|
422
435
|
throw is.invalidParameterError('effort', 'integer between 1 and 10', options.effort);
|
|
423
436
|
}
|
|
424
437
|
}
|
|
425
|
-
const colours = options.colours || options.colors;
|
|
426
|
-
if (is.defined(colours)) {
|
|
427
|
-
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
|
428
|
-
this.options.pngBitdepth = bitdepthFromColourCount(colours);
|
|
429
|
-
} else {
|
|
430
|
-
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
438
|
if (is.defined(options.dither)) {
|
|
434
439
|
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
|
|
435
440
|
this.options.pngDither = options.dither;
|
|
@@ -466,6 +471,8 @@ function png (options) {
|
|
|
466
471
|
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 6 (slowest)
|
|
467
472
|
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
|
468
473
|
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
|
474
|
+
* @param {boolean} [options.minSize=false] - prevent use of animation key frames to minimise file size (slow)
|
|
475
|
+
* @param {boolean} [options.mixed=false] - allow mixture of lossy and lossless animation frames (slow)
|
|
469
476
|
* @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
|
|
470
477
|
* @returns {Sharp}
|
|
471
478
|
* @throws {Error} Invalid options
|
|
@@ -495,14 +502,19 @@ function webp (options) {
|
|
|
495
502
|
if (is.defined(options.smartSubsample)) {
|
|
496
503
|
this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
|
|
497
504
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
this.options.webpEffort = effort;
|
|
505
|
+
if (is.defined(options.effort)) {
|
|
506
|
+
if (is.integer(options.effort) && is.inRange(options.effort, 0, 6)) {
|
|
507
|
+
this.options.webpEffort = options.effort;
|
|
502
508
|
} else {
|
|
503
|
-
throw is.invalidParameterError('effort', 'integer between 0 and 6', effort);
|
|
509
|
+
throw is.invalidParameterError('effort', 'integer between 0 and 6', options.effort);
|
|
504
510
|
}
|
|
505
511
|
}
|
|
512
|
+
if (is.defined(options.minSize)) {
|
|
513
|
+
this._setBooleanOption('webpMinSize', options.minSize);
|
|
514
|
+
}
|
|
515
|
+
if (is.defined(options.mixed)) {
|
|
516
|
+
this._setBooleanOption('webpMixed', options.mixed);
|
|
517
|
+
}
|
|
506
518
|
}
|
|
507
519
|
trySetAnimationOptions(options, this.options);
|
|
508
520
|
return this._updateFormatOut('webp', options);
|
|
@@ -513,6 +525,8 @@ function webp (options) {
|
|
|
513
525
|
*
|
|
514
526
|
* The first entry in the palette is reserved for transparency.
|
|
515
527
|
*
|
|
528
|
+
* The palette of the input image will be re-used if possible.
|
|
529
|
+
*
|
|
516
530
|
* @since 0.30.0
|
|
517
531
|
*
|
|
518
532
|
* @example
|
|
@@ -534,6 +548,8 @@ function webp (options) {
|
|
|
534
548
|
* .toBuffer();
|
|
535
549
|
*
|
|
536
550
|
* @param {Object} [options] - output options
|
|
551
|
+
* @param {boolean} [options.reoptimise=false] - always generate new palettes (slow), re-use existing by default
|
|
552
|
+
* @param {boolean} [options.reoptimize=false] - alternative spelling of `options.reoptimise`
|
|
537
553
|
* @param {number} [options.colours=256] - maximum number of palette entries, including transparency, between 2 and 256
|
|
538
554
|
* @param {number} [options.colors=256] - alternative spelling of `options.colours`
|
|
539
555
|
* @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 10 (slowest)
|
|
@@ -546,6 +562,11 @@ function webp (options) {
|
|
|
546
562
|
*/
|
|
547
563
|
function gif (options) {
|
|
548
564
|
if (is.object(options)) {
|
|
565
|
+
if (is.defined(options.reoptimise)) {
|
|
566
|
+
this._setBooleanOption('gifReoptimise', options.reoptimise);
|
|
567
|
+
} else if (is.defined(options.reoptimize)) {
|
|
568
|
+
this._setBooleanOption('gifReoptimise', options.reoptimize);
|
|
569
|
+
}
|
|
549
570
|
const colours = options.colours || options.colors;
|
|
550
571
|
if (is.defined(colours)) {
|
|
551
572
|
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
|
@@ -702,7 +723,7 @@ function trySetAnimationOptions (source, target) {
|
|
|
702
723
|
* @param {Object} [options] - output options
|
|
703
724
|
* @param {number} [options.quality=80] - quality, integer 1-100
|
|
704
725
|
* @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
|
|
705
|
-
* @param {string} [options.compression='jpeg'] - compression options:
|
|
726
|
+
* @param {string} [options.compression='jpeg'] - compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k
|
|
706
727
|
* @param {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
|
|
707
728
|
* @param {boolean} [options.pyramid=false] - write an image pyramid
|
|
708
729
|
* @param {boolean} [options.tile=false] - write a tiled tiff
|
|
@@ -770,10 +791,10 @@ function tiff (options) {
|
|
|
770
791
|
}
|
|
771
792
|
// compression
|
|
772
793
|
if (is.defined(options.compression)) {
|
|
773
|
-
if (is.string(options.compression) && is.inArray(options.compression, ['
|
|
794
|
+
if (is.string(options.compression) && is.inArray(options.compression, ['none', 'jpeg', 'deflate', 'packbits', 'ccittfax4', 'lzw', 'webp', 'zstd', 'jp2k'])) {
|
|
774
795
|
this.options.tiffCompression = options.compression;
|
|
775
796
|
} else {
|
|
776
|
-
throw is.invalidParameterError('compression', 'one of:
|
|
797
|
+
throw is.invalidParameterError('compression', 'one of: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k', options.compression);
|
|
777
798
|
}
|
|
778
799
|
}
|
|
779
800
|
// predictor
|
|
@@ -804,6 +825,16 @@ function tiff (options) {
|
|
|
804
825
|
*
|
|
805
826
|
* AVIF image sequences are not supported.
|
|
806
827
|
*
|
|
828
|
+
* @example
|
|
829
|
+
* const data = await sharp(input)
|
|
830
|
+
* .avif({ effort: 2 })
|
|
831
|
+
* .toBuffer();
|
|
832
|
+
*
|
|
833
|
+
* @example
|
|
834
|
+
* const data = await sharp(input)
|
|
835
|
+
* .avif({ lossless: true })
|
|
836
|
+
* .toBuffer();
|
|
837
|
+
*
|
|
807
838
|
* @since 0.27.0
|
|
808
839
|
*
|
|
809
840
|
* @param {Object} [options] - output options
|
|
@@ -821,9 +852,14 @@ function avif (options) {
|
|
|
821
852
|
/**
|
|
822
853
|
* Use these HEIF options for output image.
|
|
823
854
|
*
|
|
824
|
-
* Support for patent-encumbered HEIC images requires the use of a
|
|
855
|
+
* Support for patent-encumbered HEIC images using `hevc` compression requires the use of a
|
|
825
856
|
* globally-installed libvips compiled with support for libheif, libde265 and x265.
|
|
826
857
|
*
|
|
858
|
+
* @example
|
|
859
|
+
* const data = await sharp(input)
|
|
860
|
+
* .heif({ compression: 'hevc' })
|
|
861
|
+
* .toBuffer();
|
|
862
|
+
*
|
|
827
863
|
* @since 0.23.0
|
|
828
864
|
*
|
|
829
865
|
* @param {Object} [options] - output options
|
|
@@ -864,12 +900,6 @@ function heif (options) {
|
|
|
864
900
|
} else {
|
|
865
901
|
throw is.invalidParameterError('effort', 'integer between 0 and 9', options.effort);
|
|
866
902
|
}
|
|
867
|
-
} else if (is.defined(options.speed)) {
|
|
868
|
-
if (is.integer(options.speed) && is.inRange(options.speed, 0, 9)) {
|
|
869
|
-
this.options.heifEffort = 9 - options.speed;
|
|
870
|
-
} else {
|
|
871
|
-
throw is.invalidParameterError('speed', 'integer between 0 and 9', options.speed);
|
|
872
|
-
}
|
|
873
903
|
}
|
|
874
904
|
if (is.defined(options.chromaSubsampling)) {
|
|
875
905
|
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
|
|
@@ -923,9 +953,12 @@ function raw (options) {
|
|
|
923
953
|
|
|
924
954
|
/**
|
|
925
955
|
* Use tile-based deep zoom (image pyramid) output.
|
|
956
|
+
*
|
|
926
957
|
* Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
|
|
927
958
|
* Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
|
|
928
959
|
*
|
|
960
|
+
* The container will be set to `zip` when the output is a Buffer or Stream, otherwise it will default to `fs`.
|
|
961
|
+
*
|
|
929
962
|
* @example
|
|
930
963
|
* sharp('input.tiff')
|
|
931
964
|
* .png()
|
|
@@ -937,6 +970,17 @@ function raw (options) {
|
|
|
937
970
|
* // output_files contains 512x512 tiles grouped by zoom level
|
|
938
971
|
* });
|
|
939
972
|
*
|
|
973
|
+
* @example
|
|
974
|
+
* const zipFileWithTiles = await sharp(input)
|
|
975
|
+
* .tile({ basename: "tiles" })
|
|
976
|
+
* .toBuffer();
|
|
977
|
+
*
|
|
978
|
+
* @example
|
|
979
|
+
* const iiififier = sharp().tile({ layout: "iiif" });
|
|
980
|
+
* readableStream
|
|
981
|
+
* .pipe(iiififier)
|
|
982
|
+
* .pipe(writeableStream);
|
|
983
|
+
*
|
|
940
984
|
* @param {Object} [options]
|
|
941
985
|
* @param {number} [options.size=256] tile size in pixels, a value between 1 and 8192.
|
|
942
986
|
* @param {number} [options.overlap=0] tile overlap in pixels, a value between 0 and 8192.
|
|
@@ -949,6 +993,7 @@ function raw (options) {
|
|
|
949
993
|
* @param {boolean} [options.centre=false] centre image in tile.
|
|
950
994
|
* @param {boolean} [options.center=false] alternative spelling of centre.
|
|
951
995
|
* @param {string} [options.id='https://example.com/iiif'] when `layout` is `iiif`/`iiif3`, sets the `@id`/`id` attribute of `info.json`
|
|
996
|
+
* @param {string} [options.basename] the name of the directory within the zip file when container is `zip`.
|
|
952
997
|
* @returns {Sharp}
|
|
953
998
|
* @throws {Error} Invalid parameters
|
|
954
999
|
*/
|
|
@@ -1030,6 +1075,14 @@ function tile (options) {
|
|
|
1030
1075
|
throw is.invalidParameterError('id', 'string', options.id);
|
|
1031
1076
|
}
|
|
1032
1077
|
}
|
|
1078
|
+
// Basename for zip container
|
|
1079
|
+
if (is.defined(options.basename)) {
|
|
1080
|
+
if (is.string(options.basename)) {
|
|
1081
|
+
this.options.tileBasename = options.basename;
|
|
1082
|
+
} else {
|
|
1083
|
+
throw is.invalidParameterError('basename', 'string', options.basename);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1033
1086
|
}
|
|
1034
1087
|
// Format
|
|
1035
1088
|
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
|
|
@@ -1047,6 +1100,17 @@ function tile (options) {
|
|
|
1047
1100
|
* The clock starts when libvips opens an input image for processing.
|
|
1048
1101
|
* Time spent waiting for a libuv thread to become available is not included.
|
|
1049
1102
|
*
|
|
1103
|
+
* @example
|
|
1104
|
+
* // Ensure processing takes no longer than 3 seconds
|
|
1105
|
+
* try {
|
|
1106
|
+
* const data = await sharp(input)
|
|
1107
|
+
* .blur(1000)
|
|
1108
|
+
* .timeout({ seconds: 3 })
|
|
1109
|
+
* .toBuffer();
|
|
1110
|
+
* } catch (err) {
|
|
1111
|
+
* if (err.message.includes('timeout')) { ... }
|
|
1112
|
+
* }
|
|
1113
|
+
*
|
|
1050
1114
|
* @since 0.29.2
|
|
1051
1115
|
*
|
|
1052
1116
|
* @param {Object} options
|