sharp 0.32.6 → 0.33.4
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 +6 -2
- package/install/check.js +41 -0
- package/lib/colour.js +5 -9
- package/lib/constructor.js +29 -18
- package/lib/index.d.ts +100 -26
- package/lib/input.js +44 -18
- package/lib/is.js +28 -14
- package/lib/libvips.js +118 -57
- package/lib/operation.js +2 -0
- package/lib/output.js +247 -75
- package/lib/resize.js +54 -49
- package/lib/sharp.js +100 -23
- package/lib/utility.js +31 -29
- package/package.json +62 -47
- package/{binding.gyp → src/binding.gyp} +92 -48
- package/src/common.cc +62 -13
- package/src/common.h +27 -11
- package/src/metadata.cc +5 -5
- package/src/operations.cc +35 -18
- package/src/operations.h +3 -3
- package/src/pipeline.cc +142 -112
- package/src/pipeline.h +17 -11
- package/src/sharp.cc +6 -7
- package/src/stats.cc +5 -5
- package/src/utilities.cc +20 -5
- package/install/can-compile.js +0 -14
- package/install/dll-copy.js +0 -40
- package/install/libvips.js +0 -222
- package/lib/agent.js +0 -44
- package/lib/platform.js +0 -30
- package/src/libvips/cplusplus/VConnection.cpp +0 -151
- package/src/libvips/cplusplus/VError.cpp +0 -49
- package/src/libvips/cplusplus/VImage.cpp +0 -1548
- package/src/libvips/cplusplus/VInterpolate.cpp +0 -62
- package/src/libvips/cplusplus/VRegion.cpp +0 -27
- package/src/libvips/cplusplus/vips-operators.cpp +0 -3760
package/lib/output.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
|
-
const path = require('path');
|
|
6
|
+
const path = require('node:path');
|
|
7
7
|
const is = require('./is');
|
|
8
8
|
const sharp = require('./sharp');
|
|
9
9
|
|
|
@@ -86,7 +86,8 @@ function toFile (fileOut, callback) {
|
|
|
86
86
|
}
|
|
87
87
|
} else {
|
|
88
88
|
this.options.fileOut = fileOut;
|
|
89
|
-
|
|
89
|
+
const stack = Error();
|
|
90
|
+
return this._pipeline(callback, stack);
|
|
90
91
|
}
|
|
91
92
|
return this;
|
|
92
93
|
}
|
|
@@ -157,43 +158,190 @@ function toBuffer (options, callback) {
|
|
|
157
158
|
this.options.resolveWithObject = false;
|
|
158
159
|
}
|
|
159
160
|
this.options.fileOut = '';
|
|
160
|
-
|
|
161
|
+
const stack = Error();
|
|
162
|
+
return this._pipeline(is.fn(options) ? options : callback, stack);
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
/**
|
|
164
|
-
*
|
|
165
|
-
* This will also convert to and add a web-friendly sRGB ICC profile if appropriate,
|
|
166
|
-
* unless a custom output profile is provided.
|
|
167
|
-
*
|
|
168
|
-
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
|
169
|
-
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
|
166
|
+
* Keep all EXIF metadata from the input image in the output image.
|
|
170
167
|
*
|
|
171
168
|
* EXIF metadata is unsupported for TIFF output.
|
|
172
169
|
*
|
|
170
|
+
* @since 0.33.0
|
|
171
|
+
*
|
|
173
172
|
* @example
|
|
174
|
-
* sharp(
|
|
175
|
-
* .
|
|
176
|
-
* .
|
|
177
|
-
*
|
|
173
|
+
* const outputWithExif = await sharp(inputWithExif)
|
|
174
|
+
* .keepExif()
|
|
175
|
+
* .toBuffer();
|
|
176
|
+
*
|
|
177
|
+
* @returns {Sharp}
|
|
178
|
+
*/
|
|
179
|
+
function keepExif () {
|
|
180
|
+
this.options.keepMetadata |= 0b00001;
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Set EXIF metadata in the output image, ignoring any EXIF in the input image.
|
|
186
|
+
*
|
|
187
|
+
* @since 0.33.0
|
|
178
188
|
*
|
|
179
189
|
* @example
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* GPSLongitudeRef: 'W',
|
|
191
|
-
* GPSLongitude: '0/1 7/1 4366/100'
|
|
192
|
-
* }
|
|
190
|
+
* const dataWithExif = await sharp(input)
|
|
191
|
+
* .withExif({
|
|
192
|
+
* IFD0: {
|
|
193
|
+
* Copyright: 'The National Gallery'
|
|
194
|
+
* },
|
|
195
|
+
* IFD3: {
|
|
196
|
+
* GPSLatitudeRef: 'N',
|
|
197
|
+
* GPSLatitude: '51/1 30/1 3230/100',
|
|
198
|
+
* GPSLongitudeRef: 'W',
|
|
199
|
+
* GPSLongitude: '0/1 7/1 4366/100'
|
|
193
200
|
* }
|
|
194
201
|
* })
|
|
195
202
|
* .toBuffer();
|
|
196
203
|
*
|
|
204
|
+
* @param {Object<string, Object<string, string>>} exif Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
|
|
205
|
+
* @returns {Sharp}
|
|
206
|
+
* @throws {Error} Invalid parameters
|
|
207
|
+
*/
|
|
208
|
+
function withExif (exif) {
|
|
209
|
+
if (is.object(exif)) {
|
|
210
|
+
for (const [ifd, entries] of Object.entries(exif)) {
|
|
211
|
+
if (is.object(entries)) {
|
|
212
|
+
for (const [k, v] of Object.entries(entries)) {
|
|
213
|
+
if (is.string(v)) {
|
|
214
|
+
this.options.withExif[`exif-${ifd.toLowerCase()}-${k}`] = v;
|
|
215
|
+
} else {
|
|
216
|
+
throw is.invalidParameterError(`${ifd}.${k}`, 'string', v);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
throw is.invalidParameterError(ifd, 'object', entries);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
throw is.invalidParameterError('exif', 'object', exif);
|
|
225
|
+
}
|
|
226
|
+
this.options.withExifMerge = false;
|
|
227
|
+
return this.keepExif();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Update EXIF metadata from the input image in the output image.
|
|
232
|
+
*
|
|
233
|
+
* @since 0.33.0
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* const dataWithMergedExif = await sharp(inputWithExif)
|
|
237
|
+
* .withExifMerge({
|
|
238
|
+
* IFD0: {
|
|
239
|
+
* Copyright: 'The National Gallery'
|
|
240
|
+
* }
|
|
241
|
+
* })
|
|
242
|
+
* .toBuffer();
|
|
243
|
+
*
|
|
244
|
+
* @param {Object<string, Object<string, string>>} exif Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
|
|
245
|
+
* @returns {Sharp}
|
|
246
|
+
* @throws {Error} Invalid parameters
|
|
247
|
+
*/
|
|
248
|
+
function withExifMerge (exif) {
|
|
249
|
+
this.withExif(exif);
|
|
250
|
+
this.options.withExifMerge = true;
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Keep ICC profile from the input image in the output image.
|
|
256
|
+
*
|
|
257
|
+
* Where necessary, will attempt to convert the output colour space to match the profile.
|
|
258
|
+
*
|
|
259
|
+
* @since 0.33.0
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* const outputWithIccProfile = await sharp(inputWithIccProfile)
|
|
263
|
+
* .keepIccProfile()
|
|
264
|
+
* .toBuffer();
|
|
265
|
+
*
|
|
266
|
+
* @returns {Sharp}
|
|
267
|
+
*/
|
|
268
|
+
function keepIccProfile () {
|
|
269
|
+
this.options.keepMetadata |= 0b01000;
|
|
270
|
+
return this;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Transform using an ICC profile and attach to the output image.
|
|
275
|
+
*
|
|
276
|
+
* This can either be an absolute filesystem path or
|
|
277
|
+
* built-in profile name (`srgb`, `p3`, `cmyk`).
|
|
278
|
+
*
|
|
279
|
+
* @since 0.33.0
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* const outputWithP3 = await sharp(input)
|
|
283
|
+
* .withIccProfile('p3')
|
|
284
|
+
* .toBuffer();
|
|
285
|
+
*
|
|
286
|
+
* @param {string} icc - Absolute filesystem path to output ICC profile or built-in profile name (srgb, p3, cmyk).
|
|
287
|
+
* @param {Object} [options]
|
|
288
|
+
* @param {number} [options.attach=true] Should the ICC profile be included in the output image metadata?
|
|
289
|
+
* @returns {Sharp}
|
|
290
|
+
* @throws {Error} Invalid parameters
|
|
291
|
+
*/
|
|
292
|
+
function withIccProfile (icc, options) {
|
|
293
|
+
if (is.string(icc)) {
|
|
294
|
+
this.options.withIccProfile = icc;
|
|
295
|
+
} else {
|
|
296
|
+
throw is.invalidParameterError('icc', 'string', icc);
|
|
297
|
+
}
|
|
298
|
+
this.keepIccProfile();
|
|
299
|
+
if (is.object(options)) {
|
|
300
|
+
if (is.defined(options.attach)) {
|
|
301
|
+
if (is.bool(options.attach)) {
|
|
302
|
+
if (!options.attach) {
|
|
303
|
+
this.options.keepMetadata &= ~0b01000;
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
throw is.invalidParameterError('attach', 'boolean', options.attach);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return this;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Keep all metadata (EXIF, ICC, XMP, IPTC) from the input image in the output image.
|
|
315
|
+
*
|
|
316
|
+
* The default behaviour, when `keepMetadata` is not used, is to convert to the device-independent
|
|
317
|
+
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
|
318
|
+
*
|
|
319
|
+
* @since 0.33.0
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* const outputWithMetadata = await sharp(inputWithMetadata)
|
|
323
|
+
* .keepMetadata()
|
|
324
|
+
* .toBuffer();
|
|
325
|
+
*
|
|
326
|
+
* @returns {Sharp}
|
|
327
|
+
*/
|
|
328
|
+
function keepMetadata () {
|
|
329
|
+
this.options.keepMetadata = 0b11111;
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Keep most metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
|
335
|
+
*
|
|
336
|
+
* This will also convert to and add a web-friendly sRGB ICC profile if appropriate.
|
|
337
|
+
*
|
|
338
|
+
* Allows orientation and density to be set or updated.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* const outputSrgbWithMetadata = await sharp(inputRgbWithMetadata)
|
|
342
|
+
* .withMetadata()
|
|
343
|
+
* .toBuffer();
|
|
344
|
+
*
|
|
197
345
|
* @example
|
|
198
346
|
* // Set output metadata to 96 DPI
|
|
199
347
|
* const data = await sharp(input)
|
|
@@ -201,15 +349,14 @@ function toBuffer (options, callback) {
|
|
|
201
349
|
* .toBuffer();
|
|
202
350
|
*
|
|
203
351
|
* @param {Object} [options]
|
|
204
|
-
* @param {number} [options.orientation]
|
|
205
|
-
* @param {string} [options.icc='srgb'] Filesystem path to output ICC profile, relative to `process.cwd()`, defaults to built-in sRGB.
|
|
206
|
-
* @param {Object<Object>} [options.exif={}] Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
|
|
352
|
+
* @param {number} [options.orientation] Used to update the EXIF `Orientation` tag, integer between 1 and 8.
|
|
207
353
|
* @param {number} [options.density] Number of pixels per inch (DPI).
|
|
208
354
|
* @returns {Sharp}
|
|
209
355
|
* @throws {Error} Invalid parameters
|
|
210
356
|
*/
|
|
211
357
|
function withMetadata (options) {
|
|
212
|
-
this.
|
|
358
|
+
this.keepMetadata();
|
|
359
|
+
this.withIccProfile('srgb');
|
|
213
360
|
if (is.object(options)) {
|
|
214
361
|
if (is.defined(options.orientation)) {
|
|
215
362
|
if (is.integer(options.orientation) && is.inRange(options.orientation, 1, 8)) {
|
|
@@ -226,30 +373,10 @@ function withMetadata (options) {
|
|
|
226
373
|
}
|
|
227
374
|
}
|
|
228
375
|
if (is.defined(options.icc)) {
|
|
229
|
-
|
|
230
|
-
this.options.withMetadataIcc = options.icc;
|
|
231
|
-
} else {
|
|
232
|
-
throw is.invalidParameterError('icc', 'string filesystem path to ICC profile', options.icc);
|
|
233
|
-
}
|
|
376
|
+
this.withIccProfile(options.icc);
|
|
234
377
|
}
|
|
235
378
|
if (is.defined(options.exif)) {
|
|
236
|
-
|
|
237
|
-
for (const [ifd, entries] of Object.entries(options.exif)) {
|
|
238
|
-
if (is.object(entries)) {
|
|
239
|
-
for (const [k, v] of Object.entries(entries)) {
|
|
240
|
-
if (is.string(v)) {
|
|
241
|
-
this.options.withMetadataStrs[`exif-${ifd.toLowerCase()}-${k}`] = v;
|
|
242
|
-
} else {
|
|
243
|
-
throw is.invalidParameterError(`exif.${ifd}.${k}`, 'string', v);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
} else {
|
|
247
|
-
throw is.invalidParameterError(`exif.${ifd}`, 'object', entries);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
} else {
|
|
251
|
-
throw is.invalidParameterError('exif', 'object', options.exif);
|
|
252
|
-
}
|
|
379
|
+
this.withExifMerge(options.exif);
|
|
253
380
|
}
|
|
254
381
|
}
|
|
255
382
|
return this;
|
|
@@ -377,10 +504,14 @@ function jpeg (options) {
|
|
|
377
504
|
/**
|
|
378
505
|
* Use these PNG options for output image.
|
|
379
506
|
*
|
|
380
|
-
* By default, PNG output is full colour at 8
|
|
507
|
+
* By default, PNG output is full colour at 8 bits per pixel.
|
|
508
|
+
*
|
|
381
509
|
* Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
|
382
510
|
* Set `palette` to `true` for slower, indexed PNG output.
|
|
383
511
|
*
|
|
512
|
+
* For 16 bits per pixel output, convert to `rgb16` via
|
|
513
|
+
* {@link /api-colour#tocolourspace|toColourspace}.
|
|
514
|
+
*
|
|
384
515
|
* @example
|
|
385
516
|
* // Convert any input to full colour PNG output
|
|
386
517
|
* const data = await sharp(input)
|
|
@@ -393,6 +524,13 @@ function jpeg (options) {
|
|
|
393
524
|
* .png({ palette: true })
|
|
394
525
|
* .toBuffer();
|
|
395
526
|
*
|
|
527
|
+
* @example
|
|
528
|
+
* // Output 16 bits per pixel RGB(A)
|
|
529
|
+
* const data = await sharp(input)
|
|
530
|
+
* .toColourspace('rgb16')
|
|
531
|
+
* .png()
|
|
532
|
+
* .toBuffer();
|
|
533
|
+
*
|
|
396
534
|
* @param {Object} [options]
|
|
397
535
|
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
|
|
398
536
|
* @param {number} [options.compressionLevel=6] - zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest)
|
|
@@ -780,6 +918,7 @@ function trySetAnimationOptions (source, target) {
|
|
|
780
918
|
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
|
|
781
919
|
* @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
|
|
782
920
|
* @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
|
|
921
|
+
* @param {boolean} [options.miniswhite=false] - write 1-bit images as miniswhite
|
|
783
922
|
* @returns {Sharp}
|
|
784
923
|
* @throws {Error} Invalid options
|
|
785
924
|
*/
|
|
@@ -817,6 +956,10 @@ function tiff (options) {
|
|
|
817
956
|
throw is.invalidParameterError('tileHeight', 'integer greater than zero', options.tileHeight);
|
|
818
957
|
}
|
|
819
958
|
}
|
|
959
|
+
// miniswhite
|
|
960
|
+
if (is.defined(options.miniswhite)) {
|
|
961
|
+
this._setBooleanOption('tiffMiniswhite', options.miniswhite);
|
|
962
|
+
}
|
|
820
963
|
// pyramid
|
|
821
964
|
if (is.defined(options.pyramid)) {
|
|
822
965
|
this._setBooleanOption('tiffPyramid', options.pyramid);
|
|
@@ -867,10 +1010,8 @@ function tiff (options) {
|
|
|
867
1010
|
/**
|
|
868
1011
|
* Use these AVIF options for output image.
|
|
869
1012
|
*
|
|
870
|
-
* Whilst it is possible to create AVIF images smaller than 16x16 pixels,
|
|
871
|
-
* most web browsers do not display these properly.
|
|
872
|
-
*
|
|
873
1013
|
* AVIF image sequences are not supported.
|
|
1014
|
+
* Prebuilt binaries support a bitdepth of 8 only.
|
|
874
1015
|
*
|
|
875
1016
|
* @example
|
|
876
1017
|
* const data = await sharp(input)
|
|
@@ -889,6 +1030,7 @@ function tiff (options) {
|
|
|
889
1030
|
* @param {boolean} [options.lossless=false] - use lossless compression
|
|
890
1031
|
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
|
|
891
1032
|
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
|
|
1033
|
+
* @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
|
|
892
1034
|
* @returns {Sharp}
|
|
893
1035
|
* @throws {Error} Invalid options
|
|
894
1036
|
*/
|
|
@@ -909,17 +1051,23 @@ function avif (options) {
|
|
|
909
1051
|
*
|
|
910
1052
|
* @since 0.23.0
|
|
911
1053
|
*
|
|
912
|
-
* @param {Object}
|
|
1054
|
+
* @param {Object} options - output options
|
|
1055
|
+
* @param {string} options.compression - compression format: av1, hevc
|
|
913
1056
|
* @param {number} [options.quality=50] - quality, integer 1-100
|
|
914
|
-
* @param {string} [options.compression='av1'] - compression format: av1, hevc
|
|
915
1057
|
* @param {boolean} [options.lossless=false] - use lossless compression
|
|
916
1058
|
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
|
|
917
1059
|
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
|
|
1060
|
+
* @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
|
|
918
1061
|
* @returns {Sharp}
|
|
919
1062
|
* @throws {Error} Invalid options
|
|
920
1063
|
*/
|
|
921
1064
|
function heif (options) {
|
|
922
1065
|
if (is.object(options)) {
|
|
1066
|
+
if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
|
|
1067
|
+
this.options.heifCompression = options.compression;
|
|
1068
|
+
} else {
|
|
1069
|
+
throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
|
|
1070
|
+
}
|
|
923
1071
|
if (is.defined(options.quality)) {
|
|
924
1072
|
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
|
925
1073
|
this.options.heifQuality = options.quality;
|
|
@@ -934,13 +1082,6 @@ function heif (options) {
|
|
|
934
1082
|
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
|
|
935
1083
|
}
|
|
936
1084
|
}
|
|
937
|
-
if (is.defined(options.compression)) {
|
|
938
|
-
if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
|
|
939
|
-
this.options.heifCompression = options.compression;
|
|
940
|
-
} else {
|
|
941
|
-
throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
1085
|
if (is.defined(options.effort)) {
|
|
945
1086
|
if (is.integer(options.effort) && is.inRange(options.effort, 0, 9)) {
|
|
946
1087
|
this.options.heifEffort = options.effort;
|
|
@@ -955,6 +1096,18 @@ function heif (options) {
|
|
|
955
1096
|
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
|
|
956
1097
|
}
|
|
957
1098
|
}
|
|
1099
|
+
if (is.defined(options.bitdepth)) {
|
|
1100
|
+
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [8, 10, 12])) {
|
|
1101
|
+
if (options.bitdepth !== 8 && this.constructor.versions.heif) {
|
|
1102
|
+
throw is.invalidParameterError('bitdepth when using prebuilt binaries', 8, options.bitdepth);
|
|
1103
|
+
}
|
|
1104
|
+
this.options.heifBitdepth = options.bitdepth;
|
|
1105
|
+
} else {
|
|
1106
|
+
throw is.invalidParameterError('bitdepth', '8, 10 or 12', options.bitdepth);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
} else {
|
|
1110
|
+
throw is.invalidParameterError('options', 'Object', options);
|
|
958
1111
|
}
|
|
959
1112
|
return this._updateFormatOut('heif', options);
|
|
960
1113
|
}
|
|
@@ -1104,7 +1257,7 @@ function raw (options) {
|
|
|
1104
1257
|
* @param {number} [options.angle=0] tile angle of rotation, must be a multiple of 90.
|
|
1105
1258
|
* @param {string|Object} [options.background={r: 255, g: 255, b: 255, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency.
|
|
1106
1259
|
* @param {string} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
|
|
1107
|
-
* @param {number} [options.skipBlanks=-1]
|
|
1260
|
+
* @param {number} [options.skipBlanks=-1] Threshold to skip tile generation. Range is 0-255 for 8-bit images, 0-65535 for 16-bit images. Default is 5 for `google` layout, -1 (no skip) otherwise.
|
|
1108
1261
|
* @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
|
|
1109
1262
|
* @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`.
|
|
1110
1263
|
* @param {boolean} [options.centre=false] centre image in tile.
|
|
@@ -1285,7 +1438,8 @@ function _read () {
|
|
|
1285
1438
|
/* istanbul ignore else */
|
|
1286
1439
|
if (!this.options.streamOut) {
|
|
1287
1440
|
this.options.streamOut = true;
|
|
1288
|
-
|
|
1441
|
+
const stack = Error();
|
|
1442
|
+
this._pipeline(undefined, stack);
|
|
1289
1443
|
}
|
|
1290
1444
|
}
|
|
1291
1445
|
|
|
@@ -1294,18 +1448,30 @@ function _read () {
|
|
|
1294
1448
|
* Supports callback, stream and promise variants
|
|
1295
1449
|
* @private
|
|
1296
1450
|
*/
|
|
1297
|
-
function _pipeline (callback) {
|
|
1451
|
+
function _pipeline (callback, stack) {
|
|
1298
1452
|
if (typeof callback === 'function') {
|
|
1299
1453
|
// output=file/buffer
|
|
1300
1454
|
if (this._isStreamInput()) {
|
|
1301
1455
|
// output=file/buffer, input=stream
|
|
1302
1456
|
this.on('finish', () => {
|
|
1303
1457
|
this._flattenBufferIn();
|
|
1304
|
-
sharp.pipeline(this.options,
|
|
1458
|
+
sharp.pipeline(this.options, (err, data, info) => {
|
|
1459
|
+
if (err) {
|
|
1460
|
+
callback(is.nativeError(err, stack));
|
|
1461
|
+
} else {
|
|
1462
|
+
callback(null, data, info);
|
|
1463
|
+
}
|
|
1464
|
+
});
|
|
1305
1465
|
});
|
|
1306
1466
|
} else {
|
|
1307
1467
|
// output=file/buffer, input=file/buffer
|
|
1308
|
-
sharp.pipeline(this.options,
|
|
1468
|
+
sharp.pipeline(this.options, (err, data, info) => {
|
|
1469
|
+
if (err) {
|
|
1470
|
+
callback(is.nativeError(err, stack));
|
|
1471
|
+
} else {
|
|
1472
|
+
callback(null, data, info);
|
|
1473
|
+
}
|
|
1474
|
+
});
|
|
1309
1475
|
}
|
|
1310
1476
|
return this;
|
|
1311
1477
|
} else if (this.options.streamOut) {
|
|
@@ -1316,7 +1482,7 @@ function _pipeline (callback) {
|
|
|
1316
1482
|
this._flattenBufferIn();
|
|
1317
1483
|
sharp.pipeline(this.options, (err, data, info) => {
|
|
1318
1484
|
if (err) {
|
|
1319
|
-
this.emit('error', err);
|
|
1485
|
+
this.emit('error', is.nativeError(err, stack));
|
|
1320
1486
|
} else {
|
|
1321
1487
|
this.emit('info', info);
|
|
1322
1488
|
this.push(data);
|
|
@@ -1332,7 +1498,7 @@ function _pipeline (callback) {
|
|
|
1332
1498
|
// output=stream, input=file/buffer
|
|
1333
1499
|
sharp.pipeline(this.options, (err, data, info) => {
|
|
1334
1500
|
if (err) {
|
|
1335
|
-
this.emit('error', err);
|
|
1501
|
+
this.emit('error', is.nativeError(err, stack));
|
|
1336
1502
|
} else {
|
|
1337
1503
|
this.emit('info', info);
|
|
1338
1504
|
this.push(data);
|
|
@@ -1351,7 +1517,7 @@ function _pipeline (callback) {
|
|
|
1351
1517
|
this._flattenBufferIn();
|
|
1352
1518
|
sharp.pipeline(this.options, (err, data, info) => {
|
|
1353
1519
|
if (err) {
|
|
1354
|
-
reject(err);
|
|
1520
|
+
reject(is.nativeError(err, stack));
|
|
1355
1521
|
} else {
|
|
1356
1522
|
if (this.options.resolveWithObject) {
|
|
1357
1523
|
resolve({ data, info });
|
|
@@ -1367,10 +1533,10 @@ function _pipeline (callback) {
|
|
|
1367
1533
|
return new Promise((resolve, reject) => {
|
|
1368
1534
|
sharp.pipeline(this.options, (err, data, info) => {
|
|
1369
1535
|
if (err) {
|
|
1370
|
-
reject(err);
|
|
1536
|
+
reject(is.nativeError(err, stack));
|
|
1371
1537
|
} else {
|
|
1372
1538
|
if (this.options.resolveWithObject) {
|
|
1373
|
-
resolve({ data
|
|
1539
|
+
resolve({ data, info });
|
|
1374
1540
|
} else {
|
|
1375
1541
|
resolve(data);
|
|
1376
1542
|
}
|
|
@@ -1390,6 +1556,12 @@ module.exports = function (Sharp) {
|
|
|
1390
1556
|
// Public
|
|
1391
1557
|
toFile,
|
|
1392
1558
|
toBuffer,
|
|
1559
|
+
keepExif,
|
|
1560
|
+
withExif,
|
|
1561
|
+
withExifMerge,
|
|
1562
|
+
keepIccProfile,
|
|
1563
|
+
withIccProfile,
|
|
1564
|
+
keepMetadata,
|
|
1393
1565
|
withMetadata,
|
|
1394
1566
|
toFormat,
|
|
1395
1567
|
jpeg,
|
package/lib/resize.js
CHANGED
|
@@ -68,6 +68,7 @@ const strategy = {
|
|
|
68
68
|
*/
|
|
69
69
|
const kernel = {
|
|
70
70
|
nearest: 'nearest',
|
|
71
|
+
linear: 'linear',
|
|
71
72
|
cubic: 'cubic',
|
|
72
73
|
mitchell: 'mitchell',
|
|
73
74
|
lanczos2: 'lanczos2',
|
|
@@ -135,18 +136,22 @@ function isResizeExpected (options) {
|
|
|
135
136
|
*
|
|
136
137
|
* Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property.
|
|
137
138
|
*
|
|
138
|
-
* The
|
|
139
|
+
* The strategy-based approach initially resizes so one dimension is at its target length
|
|
139
140
|
* then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
|
|
140
141
|
* - `entropy`: focus on the region with the highest [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29).
|
|
141
142
|
* - `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
|
|
142
143
|
*
|
|
143
|
-
* Possible
|
|
144
|
+
* Possible downsizing kernels are:
|
|
144
145
|
* - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
|
|
146
|
+
* - `linear`: Use a [triangle filter](https://en.wikipedia.org/wiki/Triangular_function).
|
|
145
147
|
* - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
|
|
146
148
|
* - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
|
|
147
149
|
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
|
|
148
150
|
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
|
149
151
|
*
|
|
152
|
+
* When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
|
|
153
|
+
* Downsampling kernels without a matching upsampling interpolator map to `cubic`.
|
|
154
|
+
*
|
|
150
155
|
* Only one resize can occur per pipeline.
|
|
151
156
|
* Previous calls to `resize` in the same pipeline will be ignored.
|
|
152
157
|
*
|
|
@@ -239,7 +244,7 @@ function isResizeExpected (options) {
|
|
|
239
244
|
* @param {String} [options.fit='cover'] - How the image should be resized/cropped to fit the target dimension(s), one of `cover`, `contain`, `fill`, `inside` or `outside`.
|
|
240
245
|
* @param {String} [options.position='centre'] - A position, gravity or strategy to use when `fit` is `cover` or `contain`.
|
|
241
246
|
* @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when `fit` is `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
|
|
242
|
-
* @param {String} [options.kernel='lanczos3'] - The kernel to use for image reduction. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load.
|
|
247
|
+
* @param {String} [options.kernel='lanczos3'] - The kernel to use for image reduction and the inferred interpolator to use for upsampling. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load.
|
|
243
248
|
* @param {Boolean} [options.withoutEnlargement=false] - Do not scale up if the width *or* height are already less than the target dimensions, equivalent to GraphicsMagick's `>` geometry option. This may result in output dimensions smaller than the target dimensions.
|
|
244
249
|
* @param {Boolean} [options.withoutReduction=false] - Do not scale down if the width *or* height are already greater than the target dimensions, equivalent to GraphicsMagick's `<` geometry option. This may still result in a crop to reach the target dimensions.
|
|
245
250
|
* @param {Boolean} [options.fastShrinkOnLoad=true] - Take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern or round-down of an auto-scaled dimension.
|
|
@@ -250,6 +255,9 @@ function resize (widthOrOptions, height, options) {
|
|
|
250
255
|
if (isResizeExpected(this.options)) {
|
|
251
256
|
this.options.debuglog('ignoring previous resize options');
|
|
252
257
|
}
|
|
258
|
+
if (this.options.widthPost !== -1) {
|
|
259
|
+
this.options.debuglog('operation order will be: extract, resize, extract');
|
|
260
|
+
}
|
|
253
261
|
if (is.defined(widthOrOptions)) {
|
|
254
262
|
if (is.object(widthOrOptions) && !is.defined(options)) {
|
|
255
263
|
options = widthOrOptions;
|
|
@@ -437,7 +445,7 @@ function extend (extend) {
|
|
|
437
445
|
*
|
|
438
446
|
* - Use `extract` before `resize` for pre-resize extraction.
|
|
439
447
|
* - Use `extract` after `resize` for post-resize extraction.
|
|
440
|
-
* - Use `extract`
|
|
448
|
+
* - Use `extract` twice and `resize` once for extract-then-resize-then-extract in a fixed operation order.
|
|
441
449
|
*
|
|
442
450
|
* @example
|
|
443
451
|
* sharp(input)
|
|
@@ -491,70 +499,67 @@ function extract (options) {
|
|
|
491
499
|
*
|
|
492
500
|
* If the result of this operation would trim an image to nothing then no change is made.
|
|
493
501
|
*
|
|
494
|
-
* The `info` response Object
|
|
495
|
-
* will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
|
502
|
+
* The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
|
496
503
|
*
|
|
497
504
|
* @example
|
|
498
505
|
* // Trim pixels with a colour similar to that of the top-left pixel.
|
|
499
|
-
* sharp(input)
|
|
506
|
+
* await sharp(input)
|
|
500
507
|
* .trim()
|
|
501
|
-
* .toFile(output
|
|
502
|
-
*
|
|
503
|
-
* });
|
|
508
|
+
* .toFile(output);
|
|
509
|
+
*
|
|
504
510
|
* @example
|
|
505
511
|
* // Trim pixels with the exact same colour as that of the top-left pixel.
|
|
506
|
-
* sharp(input)
|
|
507
|
-
* .trim(
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
512
|
+
* await sharp(input)
|
|
513
|
+
* .trim({
|
|
514
|
+
* threshold: 0
|
|
515
|
+
* })
|
|
516
|
+
* .toFile(output);
|
|
517
|
+
*
|
|
511
518
|
* @example
|
|
512
|
-
* //
|
|
513
|
-
* sharp(input)
|
|
514
|
-
* .trim(
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
* })
|
|
519
|
+
* // Assume input is line art and trim only pixels with a similar colour to red.
|
|
520
|
+
* const output = await sharp(input)
|
|
521
|
+
* .trim({
|
|
522
|
+
* background: "#FF0000",
|
|
523
|
+
* lineArt: true
|
|
524
|
+
* })
|
|
525
|
+
* .toBuffer();
|
|
526
|
+
*
|
|
518
527
|
* @example
|
|
519
528
|
* // Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
|
|
520
|
-
* sharp(input)
|
|
529
|
+
* const output = await sharp(input)
|
|
521
530
|
* .trim({
|
|
522
531
|
* background: "yellow",
|
|
523
532
|
* threshold: 42,
|
|
524
533
|
* })
|
|
525
|
-
* .
|
|
526
|
-
* ...
|
|
527
|
-
* });
|
|
534
|
+
* .toBuffer();
|
|
528
535
|
*
|
|
529
|
-
* @param {
|
|
530
|
-
* @param {string|Object} [
|
|
531
|
-
* @param {number} [
|
|
536
|
+
* @param {Object} [options]
|
|
537
|
+
* @param {string|Object} [options.background='top-left pixel'] - Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
|
|
538
|
+
* @param {number} [options.threshold=10] - Allowed difference from the above colour, a positive number.
|
|
539
|
+
* @param {boolean} [options.lineArt=false] - Does the input more closely resemble line art (e.g. vector) rather than being photographic?
|
|
532
540
|
* @returns {Sharp}
|
|
533
541
|
* @throws {Error} Invalid parameters
|
|
534
542
|
*/
|
|
535
|
-
function trim (
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
this.options.trimThreshold = trim.threshold;
|
|
543
|
+
function trim (options) {
|
|
544
|
+
this.options.trimThreshold = 10;
|
|
545
|
+
if (is.defined(options)) {
|
|
546
|
+
if (is.object(options)) {
|
|
547
|
+
if (is.defined(options.background)) {
|
|
548
|
+
this._setBackgroundColourOption('trimBackground', options.background);
|
|
549
|
+
}
|
|
550
|
+
if (is.defined(options.threshold)) {
|
|
551
|
+
if (is.number(options.threshold) && options.threshold >= 0) {
|
|
552
|
+
this.options.trimThreshold = options.threshold;
|
|
553
|
+
} else {
|
|
554
|
+
throw is.invalidParameterError('threshold', 'positive number', options.threshold);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (is.defined(options.lineArt)) {
|
|
558
|
+
this._setBooleanOption('trimLineArt', options.lineArt);
|
|
559
|
+
}
|
|
553
560
|
} else {
|
|
554
|
-
throw is.invalidParameterError('
|
|
561
|
+
throw is.invalidParameterError('trim', 'object', options);
|
|
555
562
|
}
|
|
556
|
-
} else {
|
|
557
|
-
throw is.invalidParameterError('trim', 'string, number or object', trim);
|
|
558
563
|
}
|
|
559
564
|
if (isRotationExpected(this.options)) {
|
|
560
565
|
this.options.rotateBeforePreExtract = true;
|