sharp 0.31.2 → 0.32.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 +1 -1
- package/install/can-compile.js +3 -0
- package/install/dll-copy.js +3 -0
- package/install/libvips.js +6 -6
- package/lib/agent.js +5 -1
- package/lib/channel.js +4 -1
- package/lib/colour.js +3 -0
- package/lib/composite.js +3 -0
- package/lib/constructor.js +23 -9
- package/lib/index.d.ts +1614 -0
- package/lib/index.js +3 -0
- package/lib/input.js +39 -8
- package/lib/is.js +12 -0
- package/lib/libvips.js +4 -0
- package/lib/operation.js +94 -52
- package/lib/output.js +113 -11
- package/lib/platform.js +3 -0
- package/lib/resize.js +54 -19
- package/lib/sharp.js +3 -0
- package/lib/utility.js +5 -1
- package/package.json +30 -23
- package/src/common.cc +43 -14
- package/src/common.h +22 -16
- package/src/libvips/cplusplus/vips-operators.cpp +7 -0
- package/src/metadata.cc +17 -22
- package/src/metadata.h +3 -13
- package/src/operations.cc +13 -23
- package/src/operations.h +4 -15
- package/src/pipeline.cc +129 -69
- package/src/pipeline.h +30 -15
- package/src/sharp.cc +2 -13
- package/src/stats.cc +7 -17
- package/src/stats.h +2 -13
- package/src/utilities.cc +17 -28
- package/src/utilities.h +2 -13
package/lib/index.js
CHANGED
package/lib/input.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Copyright 2013 Lovell Fuller and others.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
1
4
|
'use strict';
|
|
2
5
|
|
|
3
6
|
const color = require('color');
|
|
@@ -21,9 +24,9 @@ const align = {
|
|
|
21
24
|
* @private
|
|
22
25
|
*/
|
|
23
26
|
function _inputOptionsFromObject (obj) {
|
|
24
|
-
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd } = obj;
|
|
25
|
-
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd].some(is.defined)
|
|
26
|
-
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd }
|
|
27
|
+
const { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd } = obj;
|
|
28
|
+
return [raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd].some(is.defined)
|
|
29
|
+
? { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd }
|
|
27
30
|
: undefined;
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -35,8 +38,9 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
35
38
|
const inputDescriptor = {
|
|
36
39
|
failOn: 'warning',
|
|
37
40
|
limitInputPixels: Math.pow(0x3FFF, 2),
|
|
41
|
+
ignoreIcc: false,
|
|
38
42
|
unlimited: false,
|
|
39
|
-
sequentialRead:
|
|
43
|
+
sequentialRead: true
|
|
40
44
|
};
|
|
41
45
|
if (is.string(input)) {
|
|
42
46
|
// filesystem
|
|
@@ -47,6 +51,11 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
47
51
|
throw Error('Input Buffer is empty');
|
|
48
52
|
}
|
|
49
53
|
inputDescriptor.buffer = input;
|
|
54
|
+
} else if (is.arrayBuffer(input)) {
|
|
55
|
+
if (input.byteLength === 0) {
|
|
56
|
+
throw Error('Input bit Array is empty');
|
|
57
|
+
}
|
|
58
|
+
inputDescriptor.buffer = Buffer.from(input, 0, input.byteLength);
|
|
50
59
|
} else if (is.typedArray(input)) {
|
|
51
60
|
if (input.length === 0) {
|
|
52
61
|
throw Error('Input Bit Array is empty');
|
|
@@ -92,6 +101,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
92
101
|
throw is.invalidParameterError('density', 'number between 1 and 100000', inputOptions.density);
|
|
93
102
|
}
|
|
94
103
|
}
|
|
104
|
+
// Ignore embeddded ICC profile
|
|
105
|
+
if (is.defined(inputOptions.ignoreIcc)) {
|
|
106
|
+
if (is.bool(inputOptions.ignoreIcc)) {
|
|
107
|
+
inputDescriptor.ignoreIcc = inputOptions.ignoreIcc;
|
|
108
|
+
} else {
|
|
109
|
+
throw is.invalidParameterError('ignoreIcc', 'boolean', inputOptions.ignoreIcc);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
95
112
|
// limitInputPixels
|
|
96
113
|
if (is.defined(inputOptions.limitInputPixels)) {
|
|
97
114
|
if (is.bool(inputOptions.limitInputPixels)) {
|
|
@@ -327,6 +344,13 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
327
344
|
throw is.invalidParameterError('text.spacing', 'number', inputOptions.text.spacing);
|
|
328
345
|
}
|
|
329
346
|
}
|
|
347
|
+
if (is.defined(inputOptions.text.wrap)) {
|
|
348
|
+
if (is.string(inputOptions.text.wrap) && is.inArray(inputOptions.text.wrap, ['word', 'char', 'wordChar', 'none'])) {
|
|
349
|
+
inputDescriptor.textWrap = inputOptions.text.wrap;
|
|
350
|
+
} else {
|
|
351
|
+
throw is.invalidParameterError('text.wrap', 'one of: word, char, wordChar, none', inputOptions.text.wrap);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
330
354
|
delete inputDescriptor.buffer;
|
|
331
355
|
} else {
|
|
332
356
|
throw new Error('Expected a valid string to create an image with text.');
|
|
@@ -387,8 +411,9 @@ function _isStreamInput () {
|
|
|
387
411
|
/**
|
|
388
412
|
* Fast access to (uncached) image metadata without decoding any compressed pixel data.
|
|
389
413
|
*
|
|
390
|
-
* This is
|
|
391
|
-
* It does not
|
|
414
|
+
* This is read from the header of the input image.
|
|
415
|
+
* It does not take into consideration any operations to be applied to the output image,
|
|
416
|
+
* such as resize or rotate.
|
|
392
417
|
*
|
|
393
418
|
* Dimensions in the response will respect the `page` and `pages` properties of the
|
|
394
419
|
* {@link /api-constructor#parameters|constructor parameters}.
|
|
@@ -423,6 +448,7 @@ function _isStreamInput () {
|
|
|
423
448
|
* - `iptc`: Buffer containing raw IPTC data, if present
|
|
424
449
|
* - `xmp`: Buffer containing raw XMP data, if present
|
|
425
450
|
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
|
451
|
+
* - `formatMagick`: String containing format for images loaded via *magick
|
|
426
452
|
*
|
|
427
453
|
* @example
|
|
428
454
|
* const metadata = await sharp(input).metadata();
|
|
@@ -469,7 +495,7 @@ function metadata (callback) {
|
|
|
469
495
|
} else {
|
|
470
496
|
if (this._isStreamInput()) {
|
|
471
497
|
return new Promise((resolve, reject) => {
|
|
472
|
-
|
|
498
|
+
const finished = () => {
|
|
473
499
|
this._flattenBufferIn();
|
|
474
500
|
sharp.metadata(this.options, (err, metadata) => {
|
|
475
501
|
if (err) {
|
|
@@ -478,7 +504,12 @@ function metadata (callback) {
|
|
|
478
504
|
resolve(metadata);
|
|
479
505
|
}
|
|
480
506
|
});
|
|
481
|
-
}
|
|
507
|
+
};
|
|
508
|
+
if (this.writableFinished) {
|
|
509
|
+
finished();
|
|
510
|
+
} else {
|
|
511
|
+
this.once('finish', finished);
|
|
512
|
+
}
|
|
482
513
|
});
|
|
483
514
|
} else {
|
|
484
515
|
return new Promise((resolve, reject) => {
|
package/lib/is.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Copyright 2013 Lovell Fuller and others.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
1
4
|
'use strict';
|
|
2
5
|
|
|
3
6
|
/**
|
|
@@ -71,6 +74,14 @@ const typedArray = function (val) {
|
|
|
71
74
|
return false;
|
|
72
75
|
};
|
|
73
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Is this value an ArrayBuffer object?
|
|
79
|
+
* @private
|
|
80
|
+
*/
|
|
81
|
+
const arrayBuffer = function (val) {
|
|
82
|
+
return val instanceof ArrayBuffer;
|
|
83
|
+
};
|
|
84
|
+
|
|
74
85
|
/**
|
|
75
86
|
* Is this value a non-empty string?
|
|
76
87
|
* @private
|
|
@@ -134,6 +145,7 @@ module.exports = {
|
|
|
134
145
|
bool: bool,
|
|
135
146
|
buffer: buffer,
|
|
136
147
|
typedArray: typedArray,
|
|
148
|
+
arrayBuffer: arrayBuffer,
|
|
137
149
|
string: string,
|
|
138
150
|
number: number,
|
|
139
151
|
integer: integer,
|
package/lib/libvips.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Copyright 2013 Lovell Fuller and others.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
1
4
|
'use strict';
|
|
2
5
|
|
|
3
6
|
const fs = require('fs');
|
|
@@ -115,6 +118,7 @@ const useGlobalLibvips = function () {
|
|
|
115
118
|
}
|
|
116
119
|
/* istanbul ignore next */
|
|
117
120
|
if (isRosetta()) {
|
|
121
|
+
log('Detected Rosetta, skipping search for globally-installed libvips');
|
|
118
122
|
return false;
|
|
119
123
|
}
|
|
120
124
|
const globalVipsVersion = globalLibvipsVersion();
|
package/lib/operation.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Copyright 2013 Lovell Fuller and others.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
1
4
|
'use strict';
|
|
2
5
|
|
|
3
6
|
const color = require('color');
|
|
@@ -205,9 +208,11 @@ function affine (matrix, options) {
|
|
|
205
208
|
|
|
206
209
|
/**
|
|
207
210
|
* Sharpen the image.
|
|
211
|
+
*
|
|
208
212
|
* When used without parameters, performs a fast, mild sharpen of the output image.
|
|
213
|
+
*
|
|
209
214
|
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
|
|
210
|
-
*
|
|
215
|
+
* Fine-grained control over the level of sharpening in "flat" (m1) and "jagged" (m2) areas is available.
|
|
211
216
|
*
|
|
212
217
|
* See {@link https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen|libvips sharpen} operation.
|
|
213
218
|
*
|
|
@@ -229,13 +234,13 @@ function affine (matrix, options) {
|
|
|
229
234
|
* })
|
|
230
235
|
* .toBuffer();
|
|
231
236
|
*
|
|
232
|
-
* @param {Object|number} [options] - if present, is an Object with attributes
|
|
233
|
-
* @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2
|
|
234
|
-
* @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas
|
|
235
|
-
* @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas
|
|
236
|
-
* @param {number} [options.x1=2.0] - threshold between "flat" and "jagged"
|
|
237
|
-
* @param {number} [options.y2=10.0] - maximum amount of brightening
|
|
238
|
-
* @param {number} [options.y3=20.0] - maximum amount of darkening
|
|
237
|
+
* @param {Object|number} [options] - if present, is an Object with attributes
|
|
238
|
+
* @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`, between 0.000001 and 10
|
|
239
|
+
* @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas, between 0 and 1000000
|
|
240
|
+
* @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas, between 0 and 1000000
|
|
241
|
+
* @param {number} [options.x1=2.0] - threshold between "flat" and "jagged", between 0 and 1000000
|
|
242
|
+
* @param {number} [options.y2=10.0] - maximum amount of brightening, between 0 and 1000000
|
|
243
|
+
* @param {number} [options.y3=20.0] - maximum amount of darkening, between 0 and 1000000
|
|
239
244
|
* @param {number} [flat] - (deprecated) see `options.m1`.
|
|
240
245
|
* @param {number} [jagged] - (deprecated) see `options.m2`.
|
|
241
246
|
* @returns {Sharp}
|
|
@@ -268,44 +273,44 @@ function sharpen (options, flat, jagged) {
|
|
|
268
273
|
}
|
|
269
274
|
}
|
|
270
275
|
} else if (is.plainObject(options)) {
|
|
271
|
-
if (is.number(options.sigma) && is.inRange(options.sigma, 0.
|
|
276
|
+
if (is.number(options.sigma) && is.inRange(options.sigma, 0.000001, 10)) {
|
|
272
277
|
this.options.sharpenSigma = options.sigma;
|
|
273
278
|
} else {
|
|
274
|
-
throw is.invalidParameterError('options.sigma', 'number between 0.
|
|
279
|
+
throw is.invalidParameterError('options.sigma', 'number between 0.000001 and 10', options.sigma);
|
|
275
280
|
}
|
|
276
281
|
if (is.defined(options.m1)) {
|
|
277
|
-
if (is.number(options.m1) && is.inRange(options.m1, 0,
|
|
282
|
+
if (is.number(options.m1) && is.inRange(options.m1, 0, 1000000)) {
|
|
278
283
|
this.options.sharpenM1 = options.m1;
|
|
279
284
|
} else {
|
|
280
|
-
throw is.invalidParameterError('options.m1', 'number between 0 and
|
|
285
|
+
throw is.invalidParameterError('options.m1', 'number between 0 and 1000000', options.m1);
|
|
281
286
|
}
|
|
282
287
|
}
|
|
283
288
|
if (is.defined(options.m2)) {
|
|
284
|
-
if (is.number(options.m2) && is.inRange(options.m2, 0,
|
|
289
|
+
if (is.number(options.m2) && is.inRange(options.m2, 0, 1000000)) {
|
|
285
290
|
this.options.sharpenM2 = options.m2;
|
|
286
291
|
} else {
|
|
287
|
-
throw is.invalidParameterError('options.m2', 'number between 0 and
|
|
292
|
+
throw is.invalidParameterError('options.m2', 'number between 0 and 1000000', options.m2);
|
|
288
293
|
}
|
|
289
294
|
}
|
|
290
295
|
if (is.defined(options.x1)) {
|
|
291
|
-
if (is.number(options.x1) && is.inRange(options.x1, 0,
|
|
296
|
+
if (is.number(options.x1) && is.inRange(options.x1, 0, 1000000)) {
|
|
292
297
|
this.options.sharpenX1 = options.x1;
|
|
293
298
|
} else {
|
|
294
|
-
throw is.invalidParameterError('options.x1', 'number between 0 and
|
|
299
|
+
throw is.invalidParameterError('options.x1', 'number between 0 and 1000000', options.x1);
|
|
295
300
|
}
|
|
296
301
|
}
|
|
297
302
|
if (is.defined(options.y2)) {
|
|
298
|
-
if (is.number(options.y2) && is.inRange(options.y2, 0,
|
|
303
|
+
if (is.number(options.y2) && is.inRange(options.y2, 0, 1000000)) {
|
|
299
304
|
this.options.sharpenY2 = options.y2;
|
|
300
305
|
} else {
|
|
301
|
-
throw is.invalidParameterError('options.y2', 'number between 0 and
|
|
306
|
+
throw is.invalidParameterError('options.y2', 'number between 0 and 1000000', options.y2);
|
|
302
307
|
}
|
|
303
308
|
}
|
|
304
309
|
if (is.defined(options.y3)) {
|
|
305
|
-
if (is.number(options.y3) && is.inRange(options.y3, 0,
|
|
310
|
+
if (is.number(options.y3) && is.inRange(options.y3, 0, 1000000)) {
|
|
306
311
|
this.options.sharpenY3 = options.y3;
|
|
307
312
|
} else {
|
|
308
|
-
throw is.invalidParameterError('options.y3', 'number between 0 and
|
|
313
|
+
throw is.invalidParameterError('options.y3', 'number between 0 and 1000000', options.y3);
|
|
309
314
|
}
|
|
310
315
|
}
|
|
311
316
|
} else {
|
|
@@ -464,16 +469,50 @@ function negate (options) {
|
|
|
464
469
|
}
|
|
465
470
|
|
|
466
471
|
/**
|
|
467
|
-
* Enhance output image contrast by stretching its luminance to cover
|
|
472
|
+
* Enhance output image contrast by stretching its luminance to cover a full dynamic range.
|
|
473
|
+
*
|
|
474
|
+
* Uses a histogram-based approach, taking a default range of 1% to 99% to reduce sensitivity to noise at the extremes.
|
|
475
|
+
*
|
|
476
|
+
* Luminance values below the `lower` percentile will be underexposed by clipping to zero.
|
|
477
|
+
* Luminance values above the `upper` percentile will be overexposed by clipping to the max pixel value.
|
|
468
478
|
*
|
|
469
479
|
* @example
|
|
470
|
-
* const output = await sharp(input)
|
|
480
|
+
* const output = await sharp(input)
|
|
481
|
+
* .normalise()
|
|
482
|
+
* .toBuffer();
|
|
471
483
|
*
|
|
472
|
-
* @
|
|
484
|
+
* @example
|
|
485
|
+
* const output = await sharp(input)
|
|
486
|
+
* .normalise({ lower: 0, upper: 100 })
|
|
487
|
+
* .toBuffer();
|
|
488
|
+
*
|
|
489
|
+
* @param {Object} [options]
|
|
490
|
+
* @param {number} [options.lower=1] - Percentile below which luminance values will be underexposed.
|
|
491
|
+
* @param {number} [options.upper=99] - Percentile above which luminance values will be overexposed.
|
|
473
492
|
* @returns {Sharp}
|
|
474
493
|
*/
|
|
475
|
-
function normalise (
|
|
476
|
-
|
|
494
|
+
function normalise (options) {
|
|
495
|
+
if (is.plainObject(options)) {
|
|
496
|
+
if (is.defined(options.lower)) {
|
|
497
|
+
if (is.number(options.lower) && is.inRange(options.lower, 0, 99)) {
|
|
498
|
+
this.options.normaliseLower = options.lower;
|
|
499
|
+
} else {
|
|
500
|
+
throw is.invalidParameterError('lower', 'number between 0 and 99', options.lower);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (is.defined(options.upper)) {
|
|
504
|
+
if (is.number(options.upper) && is.inRange(options.upper, 1, 100)) {
|
|
505
|
+
this.options.normaliseUpper = options.upper;
|
|
506
|
+
} else {
|
|
507
|
+
throw is.invalidParameterError('upper', 'number between 1 and 100', options.upper);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (this.options.normaliseLower >= this.options.normaliseUpper) {
|
|
512
|
+
throw is.invalidParameterError('range', 'lower to be less than upper',
|
|
513
|
+
`${this.options.normaliseLower} >= ${this.options.normaliseUpper}`);
|
|
514
|
+
}
|
|
515
|
+
this.options.normalise = true;
|
|
477
516
|
return this;
|
|
478
517
|
}
|
|
479
518
|
|
|
@@ -481,13 +520,17 @@ function normalise (normalise) {
|
|
|
481
520
|
* Alternative spelling of normalise.
|
|
482
521
|
*
|
|
483
522
|
* @example
|
|
484
|
-
* const output = await sharp(input)
|
|
523
|
+
* const output = await sharp(input)
|
|
524
|
+
* .normalize()
|
|
525
|
+
* .toBuffer();
|
|
485
526
|
*
|
|
486
|
-
* @param {
|
|
527
|
+
* @param {Object} [options]
|
|
528
|
+
* @param {number} [options.lower=1] - Percentile below which luminance values will be underexposed.
|
|
529
|
+
* @param {number} [options.upper=99] - Percentile above which luminance values will be overexposed.
|
|
487
530
|
* @returns {Sharp}
|
|
488
531
|
*/
|
|
489
|
-
function normalize (
|
|
490
|
-
return this.normalise(
|
|
532
|
+
function normalize (options) {
|
|
533
|
+
return this.normalise(options);
|
|
491
534
|
}
|
|
492
535
|
|
|
493
536
|
/**
|
|
@@ -507,34 +550,33 @@ function normalize (normalize) {
|
|
|
507
550
|
* .toBuffer();
|
|
508
551
|
*
|
|
509
552
|
* @param {Object} options
|
|
510
|
-
* @param {number} options.width -
|
|
511
|
-
* @param {number} options.height -
|
|
512
|
-
* @param {number} [options.maxSlope=3] -
|
|
513
|
-
* cumulative histogram. A value of 0 disables contrast limiting. Valid values
|
|
514
|
-
* are integers in the range 0-100 (inclusive)
|
|
553
|
+
* @param {number} options.width - Integral width of the search window, in pixels.
|
|
554
|
+
* @param {number} options.height - Integral height of the search window, in pixels.
|
|
555
|
+
* @param {number} [options.maxSlope=3] - Integral level of brightening, between 0 and 100, where 0 disables contrast limiting.
|
|
515
556
|
* @returns {Sharp}
|
|
516
557
|
* @throws {Error} Invalid parameters
|
|
517
558
|
*/
|
|
518
559
|
function clahe (options) {
|
|
519
|
-
if (
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
560
|
+
if (is.plainObject(options)) {
|
|
561
|
+
if (is.integer(options.width) && options.width > 0) {
|
|
562
|
+
this.options.claheWidth = options.width;
|
|
563
|
+
} else {
|
|
564
|
+
throw is.invalidParameterError('width', 'integer greater than zero', options.width);
|
|
565
|
+
}
|
|
566
|
+
if (is.integer(options.height) && options.height > 0) {
|
|
567
|
+
this.options.claheHeight = options.height;
|
|
568
|
+
} else {
|
|
569
|
+
throw is.invalidParameterError('height', 'integer greater than zero', options.height);
|
|
570
|
+
}
|
|
571
|
+
if (is.defined(options.maxSlope)) {
|
|
572
|
+
if (is.integer(options.maxSlope) && is.inRange(options.maxSlope, 0, 100)) {
|
|
573
|
+
this.options.claheMaxSlope = options.maxSlope;
|
|
574
|
+
} else {
|
|
575
|
+
throw is.invalidParameterError('maxSlope', 'integer between 0 and 100', options.maxSlope);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
536
578
|
} else {
|
|
537
|
-
|
|
579
|
+
throw is.invalidParameterError('options', 'plain object', options);
|
|
538
580
|
}
|
|
539
581
|
return this;
|
|
540
582
|
}
|
package/lib/output.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Copyright 2013 Lovell Fuller and others.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
1
4
|
'use strict';
|
|
2
5
|
|
|
3
6
|
const path = require('path');
|
|
@@ -22,10 +25,13 @@ const formats = new Map([
|
|
|
22
25
|
['jp2', 'jp2'],
|
|
23
26
|
['jpx', 'jp2'],
|
|
24
27
|
['j2k', 'jp2'],
|
|
25
|
-
['j2c', 'jp2']
|
|
28
|
+
['j2c', 'jp2'],
|
|
29
|
+
['jxl', 'jxl']
|
|
26
30
|
]);
|
|
27
31
|
|
|
28
|
-
const
|
|
32
|
+
const jp2Regex = /\.jp[2x]|j2[kc]$/i;
|
|
33
|
+
|
|
34
|
+
const errJp2Save = () => new Error('JP2 output requires libvips with support for OpenJPEG');
|
|
29
35
|
|
|
30
36
|
const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours)));
|
|
31
37
|
|
|
@@ -58,6 +64,7 @@ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math
|
|
|
58
64
|
* `info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
|
59
65
|
* `channels` and `premultiplied` (indicating if premultiplication was used).
|
|
60
66
|
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
|
|
67
|
+
* When using the attention crop strategy also contains `attentionX` and `attentionY`, the focal point of the cropped region.
|
|
61
68
|
* May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
|
|
62
69
|
* @returns {Promise<Object>} - when no callback is provided
|
|
63
70
|
* @throws {Error} Invalid parameters
|
|
@@ -68,6 +75,8 @@ function toFile (fileOut, callback) {
|
|
|
68
75
|
err = new Error('Missing output file path');
|
|
69
76
|
} else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
|
|
70
77
|
err = new Error('Cannot use same file for input and output');
|
|
78
|
+
} else if (jp2Regex.test(fileOut) && !this.constructor.format.jp2k.output.file) {
|
|
79
|
+
err = errJp2Save();
|
|
71
80
|
}
|
|
72
81
|
if (err) {
|
|
73
82
|
if (is.fn(callback)) {
|
|
@@ -187,7 +196,7 @@ function toBuffer (options, callback) {
|
|
|
187
196
|
*
|
|
188
197
|
* @param {Object} [options]
|
|
189
198
|
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
|
|
190
|
-
* @param {string} [options.icc]
|
|
199
|
+
* @param {string} [options.icc='srgb'] Filesystem path to output ICC profile, relative to `process.cwd()`, defaults to built-in sRGB.
|
|
191
200
|
* @param {Object<Object>} [options.exif={}] Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
|
|
192
201
|
* @param {number} [options.density] Number of pixels per inch (DPI).
|
|
193
202
|
* @returns {Sharp}
|
|
@@ -547,13 +556,21 @@ function webp (options) {
|
|
|
547
556
|
* .gif({ dither: 0 })
|
|
548
557
|
* .toBuffer();
|
|
549
558
|
*
|
|
559
|
+
* @example
|
|
560
|
+
* // Lossy file size reduction of animated GIF
|
|
561
|
+
* await sharp('in.gif', { animated: true })
|
|
562
|
+
* .gif({ interFrameMaxError: 8 })
|
|
563
|
+
* .toFile('optim.gif');
|
|
564
|
+
*
|
|
550
565
|
* @param {Object} [options] - output options
|
|
551
|
-
* @param {boolean} [options.
|
|
552
|
-
* @param {boolean} [options.
|
|
566
|
+
* @param {boolean} [options.reuse=true] - re-use existing palette, otherwise generate new (slow)
|
|
567
|
+
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
|
|
553
568
|
* @param {number} [options.colours=256] - maximum number of palette entries, including transparency, between 2 and 256
|
|
554
569
|
* @param {number} [options.colors=256] - alternative spelling of `options.colours`
|
|
555
570
|
* @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 10 (slowest)
|
|
556
571
|
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
|
|
572
|
+
* @param {number} [options.interFrameMaxError=0] - maximum inter-frame error for transparency, between 0 (lossless) and 32
|
|
573
|
+
* @param {number} [options.interPaletteMaxError=3] - maximum inter-palette error for palette reuse, between 0 and 256
|
|
557
574
|
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
|
558
575
|
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
|
559
576
|
* @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
|
|
@@ -562,10 +579,11 @@ function webp (options) {
|
|
|
562
579
|
*/
|
|
563
580
|
function gif (options) {
|
|
564
581
|
if (is.object(options)) {
|
|
565
|
-
if (is.defined(options.
|
|
566
|
-
this._setBooleanOption('
|
|
567
|
-
}
|
|
568
|
-
|
|
582
|
+
if (is.defined(options.reuse)) {
|
|
583
|
+
this._setBooleanOption('gifReuse', options.reuse);
|
|
584
|
+
}
|
|
585
|
+
if (is.defined(options.progressive)) {
|
|
586
|
+
this._setBooleanOption('gifProgressive', options.progressive);
|
|
569
587
|
}
|
|
570
588
|
const colours = options.colours || options.colors;
|
|
571
589
|
if (is.defined(colours)) {
|
|
@@ -589,6 +607,20 @@ function gif (options) {
|
|
|
589
607
|
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
|
|
590
608
|
}
|
|
591
609
|
}
|
|
610
|
+
if (is.defined(options.interFrameMaxError)) {
|
|
611
|
+
if (is.number(options.interFrameMaxError) && is.inRange(options.interFrameMaxError, 0, 32)) {
|
|
612
|
+
this.options.gifInterFrameMaxError = options.interFrameMaxError;
|
|
613
|
+
} else {
|
|
614
|
+
throw is.invalidParameterError('interFrameMaxError', 'number between 0.0 and 32.0', options.interFrameMaxError);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (is.defined(options.interPaletteMaxError)) {
|
|
618
|
+
if (is.number(options.interPaletteMaxError) && is.inRange(options.interPaletteMaxError, 0, 256)) {
|
|
619
|
+
this.options.gifInterPaletteMaxError = options.interPaletteMaxError;
|
|
620
|
+
} else {
|
|
621
|
+
throw is.invalidParameterError('interPaletteMaxError', 'number between 0.0 and 256.0', options.interPaletteMaxError);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
592
624
|
}
|
|
593
625
|
trySetAnimationOptions(options, this.options);
|
|
594
626
|
return this._updateFormatOut('gif', options);
|
|
@@ -630,7 +662,7 @@ function gif (options) {
|
|
|
630
662
|
/* istanbul ignore next */
|
|
631
663
|
function jp2 (options) {
|
|
632
664
|
if (!this.constructor.format.jp2k.output.buffer) {
|
|
633
|
-
throw errJp2Save;
|
|
665
|
+
throw errJp2Save();
|
|
634
666
|
}
|
|
635
667
|
if (is.object(options)) {
|
|
636
668
|
if (is.defined(options.quality)) {
|
|
@@ -663,7 +695,7 @@ function jp2 (options) {
|
|
|
663
695
|
}
|
|
664
696
|
if (is.defined(options.chromaSubsampling)) {
|
|
665
697
|
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
|
|
666
|
-
this.options.
|
|
698
|
+
this.options.jp2ChromaSubsampling = options.chromaSubsampling;
|
|
667
699
|
} else {
|
|
668
700
|
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
|
|
669
701
|
}
|
|
@@ -912,6 +944,71 @@ function heif (options) {
|
|
|
912
944
|
return this._updateFormatOut('heif', options);
|
|
913
945
|
}
|
|
914
946
|
|
|
947
|
+
/**
|
|
948
|
+
* Use these JPEG-XL (JXL) options for output image.
|
|
949
|
+
*
|
|
950
|
+
* This feature is experimental, please do not use in production systems.
|
|
951
|
+
*
|
|
952
|
+
* Requires libvips compiled with support for libjxl.
|
|
953
|
+
* The prebuilt binaries do not include this - see
|
|
954
|
+
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
|
|
955
|
+
*
|
|
956
|
+
* Image metadata (EXIF, XMP) is unsupported.
|
|
957
|
+
*
|
|
958
|
+
* @since 0.31.3
|
|
959
|
+
*
|
|
960
|
+
* @param {Object} [options] - output options
|
|
961
|
+
* @param {number} [options.distance=1.0] - maximum encoding error, between 0 (highest quality) and 15 (lowest quality)
|
|
962
|
+
* @param {number} [options.quality] - calculate `distance` based on JPEG-like quality, between 1 and 100, overrides distance if specified
|
|
963
|
+
* @param {number} [options.decodingTier=0] - target decode speed tier, between 0 (highest quality) and 4 (lowest quality)
|
|
964
|
+
* @param {boolean} [options.lossless=false] - use lossless compression
|
|
965
|
+
* @param {number} [options.effort=7] - CPU effort, between 3 (fastest) and 9 (slowest)
|
|
966
|
+
* @returns {Sharp}
|
|
967
|
+
* @throws {Error} Invalid options
|
|
968
|
+
*/
|
|
969
|
+
function jxl (options) {
|
|
970
|
+
if (is.object(options)) {
|
|
971
|
+
if (is.defined(options.quality)) {
|
|
972
|
+
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
|
973
|
+
// https://github.com/libjxl/libjxl/blob/0aeea7f180bafd6893c1db8072dcb67d2aa5b03d/tools/cjxl_main.cc#L640-L644
|
|
974
|
+
this.options.jxlDistance = options.quality >= 30
|
|
975
|
+
? 0.1 + (100 - options.quality) * 0.09
|
|
976
|
+
: 53 / 3000 * options.quality * options.quality - 23 / 20 * options.quality + 25;
|
|
977
|
+
} else {
|
|
978
|
+
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
|
979
|
+
}
|
|
980
|
+
} else if (is.defined(options.distance)) {
|
|
981
|
+
if (is.number(options.distance) && is.inRange(options.distance, 0, 15)) {
|
|
982
|
+
this.options.jxlDistance = options.distance;
|
|
983
|
+
} else {
|
|
984
|
+
throw is.invalidParameterError('distance', 'number between 0.0 and 15.0', options.distance);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (is.defined(options.decodingTier)) {
|
|
988
|
+
if (is.integer(options.decodingTier) && is.inRange(options.decodingTier, 0, 4)) {
|
|
989
|
+
this.options.jxlDecodingTier = options.decodingTier;
|
|
990
|
+
} else {
|
|
991
|
+
throw is.invalidParameterError('decodingTier', 'integer between 0 and 4', options.decodingTier);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
if (is.defined(options.lossless)) {
|
|
995
|
+
if (is.bool(options.lossless)) {
|
|
996
|
+
this.options.jxlLossless = options.lossless;
|
|
997
|
+
} else {
|
|
998
|
+
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (is.defined(options.effort)) {
|
|
1002
|
+
if (is.integer(options.effort) && is.inRange(options.effort, 3, 9)) {
|
|
1003
|
+
this.options.jxlEffort = options.effort;
|
|
1004
|
+
} else {
|
|
1005
|
+
throw is.invalidParameterError('effort', 'integer between 3 and 9', options.effort);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return this._updateFormatOut('jxl', options);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
915
1012
|
/**
|
|
916
1013
|
* Force output to be raw, uncompressed pixel data.
|
|
917
1014
|
* Pixel ordering is left-to-right, top-to-bottom, without padding.
|
|
@@ -959,6 +1056,10 @@ function raw (options) {
|
|
|
959
1056
|
*
|
|
960
1057
|
* The container will be set to `zip` when the output is a Buffer or Stream, otherwise it will default to `fs`.
|
|
961
1058
|
*
|
|
1059
|
+
* Requires libvips compiled with support for libgsf.
|
|
1060
|
+
* The prebuilt binaries do not include this - see
|
|
1061
|
+
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
|
|
1062
|
+
*
|
|
962
1063
|
* @example
|
|
963
1064
|
* sharp('input.tiff')
|
|
964
1065
|
* .png()
|
|
@@ -1282,6 +1383,7 @@ module.exports = function (Sharp) {
|
|
|
1282
1383
|
tiff,
|
|
1283
1384
|
avif,
|
|
1284
1385
|
heif,
|
|
1386
|
+
jxl,
|
|
1285
1387
|
gif,
|
|
1286
1388
|
raw,
|
|
1287
1389
|
tile,
|