sharp 0.26.3 → 0.28.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/lib/operation.js CHANGED
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { flatten: flattenArray } = require('array-flatten');
4
3
  const color = require('color');
5
4
  const is = require('./is');
6
5
 
@@ -127,7 +126,7 @@ function flop (flop) {
127
126
  * @throws {Error} Invalid parameters
128
127
  */
129
128
  function affine (matrix, options) {
130
- const flatMatrix = flattenArray(matrix);
129
+ const flatMatrix = [].concat(...matrix);
131
130
  if (flatMatrix.length === 4 && flatMatrix.every(is.number)) {
132
131
  this.options.affineMatrix = flatMatrix;
133
132
  } else {
@@ -269,7 +268,13 @@ function blur (sigma) {
269
268
  }
270
269
 
271
270
  /**
272
- * Merge alpha transparency channel, if any, with a background.
271
+ * Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
272
+ *
273
+ * @example
274
+ * await sharp(rgbaInput)
275
+ * .flatten('#F0A703')
276
+ * .toBuffer();
277
+ *
273
278
  * @param {Object} [options]
274
279
  * @param {string|Object} [options.background={r: 0, g: 0, b: 0}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black.
275
280
  * @returns {Sharp}
@@ -397,7 +402,7 @@ function convolve (kernel) {
397
402
  }
398
403
 
399
404
  /**
400
- * Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
405
+ * Any pixel value greater than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
401
406
  * @param {number} [threshold=128] - a value in the range 0-255 representing the level at which the threshold will be applied.
402
407
  * @param {Object} [options]
403
408
  * @param {Boolean} [options.greyscale=true] - convert to single channel greyscale.
package/lib/output.js CHANGED
@@ -6,6 +6,7 @@ const sharp = require('../build/Release/sharp.node');
6
6
  const formats = new Map([
7
7
  ['heic', 'heif'],
8
8
  ['heif', 'heif'],
9
+ ['avif', 'avif'],
9
10
  ['jpeg', 'jpeg'],
10
11
  ['jpg', 'jpeg'],
11
12
  ['png', 'png'],
@@ -15,11 +16,13 @@ const formats = new Map([
15
16
  ['gif', 'gif']
16
17
  ]);
17
18
 
19
+ const errMagickSave = new Error('GIF output requires libvips with support for ImageMagick');
20
+
18
21
  /**
19
22
  * Write output image data to a file.
20
23
  *
21
24
  * If an explicit output format is not selected, it will be inferred from the extension,
22
- * with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
25
+ * with JPEG, PNG, WebP, AVIF, TIFF, DZI, and libvips' V format supported.
23
26
  * Note that raw pixel data is only supported for buffer output.
24
27
  *
25
28
  * By default all metadata will be removed, which includes EXIF-based orientation.
@@ -46,32 +49,30 @@ const formats = new Map([
46
49
  * @throws {Error} Invalid parameters
47
50
  */
48
51
  function toFile (fileOut, callback) {
49
- if (!fileOut || fileOut.length === 0) {
50
- const errOutputInvalid = new Error('Missing output file path');
52
+ let err;
53
+ if (!is.string(fileOut)) {
54
+ err = new Error('Missing output file path');
55
+ } else if (this.options.input.file === fileOut) {
56
+ err = new Error('Cannot use same file for input and output');
57
+ } else if (this.options.formatOut === 'input' && fileOut.toLowerCase().endsWith('.gif') && !this.constructor.format.magick.output.file) {
58
+ err = errMagickSave;
59
+ }
60
+ if (err) {
51
61
  if (is.fn(callback)) {
52
- callback(errOutputInvalid);
62
+ callback(err);
53
63
  } else {
54
- return Promise.reject(errOutputInvalid);
64
+ return Promise.reject(err);
55
65
  }
56
66
  } else {
57
- if (this.options.input.file === fileOut) {
58
- const errOutputIsInput = new Error('Cannot use same file for input and output');
59
- if (is.fn(callback)) {
60
- callback(errOutputIsInput);
61
- } else {
62
- return Promise.reject(errOutputIsInput);
63
- }
64
- } else {
65
- this.options.fileOut = fileOut;
66
- return this._pipeline(callback);
67
- }
67
+ this.options.fileOut = fileOut;
68
+ return this._pipeline(callback);
68
69
  }
69
70
  return this;
70
71
  }
71
72
 
72
73
  /**
73
74
  * Write output to a Buffer.
74
- * JPEG, PNG, WebP, TIFF and RAW output are supported.
75
+ * JPEG, PNG, WebP, AVIF, TIFF and raw pixel data output are supported.
75
76
  *
76
77
  * If no explicit format is set, the output format will match the input image, except GIF and SVG input which become PNG output.
77
78
  *
@@ -103,6 +104,22 @@ function toFile (fileOut, callback) {
103
104
  * .then(({ data, info }) => { ... })
104
105
  * .catch(err => { ... });
105
106
  *
107
+ * @example
108
+ * const { data, info } = await sharp('my-image.jpg')
109
+ * // output the raw pixels
110
+ * .raw()
111
+ * .toBuffer({ resolveWithObject: true });
112
+ *
113
+ * // create a more type safe way to work with the raw pixel data
114
+ * // this will not copy the data, instead it will change `data`s underlying ArrayBuffer
115
+ * // so `data` and `pixelArray` point to the same memory location
116
+ * const pixelArray = new Uint8ClampedArray(data.buffer);
117
+ *
118
+ * // When you are done changing the pixelArray, sharp takes the `pixelArray` as an input
119
+ * const { width, height, channels } = info;
120
+ * await sharp(pixelArray, { raw: { width, height, channels } })
121
+ * .toFile('my-changed-image.jpg');
122
+ *
106
123
  * @param {Object} [options]
107
124
  * @param {boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
108
125
  * @param {Function} [callback]
@@ -173,7 +190,7 @@ function withMetadata (options) {
173
190
  * @throws {Error} unsupported format or options
174
191
  */
175
192
  function toFormat (format, options) {
176
- const actualFormat = formats.get(is.object(format) && is.string(format.id) ? format.id : format);
193
+ const actualFormat = formats.get((is.object(format) && is.string(format.id) ? format.id : format).toLowerCase());
177
194
  if (!actualFormat) {
178
195
  throw is.invalidParameterError('format', `one of: ${[...formats.keys()].join(', ')}`, format);
179
196
  }
@@ -183,8 +200,6 @@ function toFormat (format, options) {
183
200
  /**
184
201
  * Use these JPEG options for output image.
185
202
  *
186
- * Some of these options require the use of a globally-installed libvips compiled with support for mozjpeg.
187
- *
188
203
  * @example
189
204
  * // Convert any input to very high quality JPEG output
190
205
  * const data = await sharp(input)
@@ -194,18 +209,25 @@ function toFormat (format, options) {
194
209
  * })
195
210
  * .toBuffer();
196
211
  *
212
+ * @example
213
+ * // Use mozjpeg to reduce output JPEG file size (slower)
214
+ * const data = await sharp(input)
215
+ * .jpeg({ mozjpeg: true })
216
+ * .toBuffer();
217
+ *
197
218
  * @param {Object} [options] - output options
198
219
  * @param {number} [options.quality=80] - quality, integer 1-100
199
220
  * @param {boolean} [options.progressive=false] - use progressive (interlace) scan
200
221
  * @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling
201
222
  * @param {boolean} [options.optimiseCoding=true] - optimise Huffman coding tables
202
223
  * @param {boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
203
- * @param {boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires libvips compiled with support for mozjpeg
204
- * @param {boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires libvips compiled with support for mozjpeg
205
- * @param {boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg
206
- * @param {boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans, requires libvips compiled with support for mozjpeg
207
- * @param {number} [options.quantisationTable=0] - quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg
208
- * @param {number} [options.quantizationTable=0] - alternative spelling of quantisationTable, requires libvips compiled with support for mozjpeg
224
+ * @param {boolean} [options.mozjpeg=false] - use mozjpeg defaults, equivalent to `{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }`
225
+ * @param {boolean} [options.trellisQuantisation=false] - apply trellis quantisation
226
+ * @param {boolean} [options.overshootDeringing=false] - apply overshoot deringing
227
+ * @param {boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive
228
+ * @param {boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans
229
+ * @param {number} [options.quantisationTable=0] - quantization table to use, integer 0-8
230
+ * @param {number} [options.quantizationTable=0] - alternative spelling of quantisationTable
209
231
  * @param {boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format
210
232
  * @returns {Sharp}
211
233
  * @throws {Error} Invalid options
@@ -229,6 +251,23 @@ function jpeg (options) {
229
251
  throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
230
252
  }
231
253
  }
254
+ const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
255
+ if (is.defined(optimiseCoding)) {
256
+ this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
257
+ }
258
+ if (is.defined(options.mozjpeg)) {
259
+ if (is.bool(options.mozjpeg)) {
260
+ if (options.mozjpeg) {
261
+ this.options.jpegTrellisQuantisation = true;
262
+ this.options.jpegOvershootDeringing = true;
263
+ this.options.jpegOptimiseScans = true;
264
+ this.options.jpegProgressive = true;
265
+ this.options.jpegQuantisationTable = 3;
266
+ }
267
+ } else {
268
+ throw is.invalidParameterError('mozjpeg', 'boolean', options.mozjpeg);
269
+ }
270
+ }
232
271
  const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
233
272
  if (is.defined(trellisQuantisation)) {
234
273
  this._setBooleanOption('jpegTrellisQuantisation', trellisQuantisation);
@@ -243,10 +282,6 @@ function jpeg (options) {
243
282
  this.options.jpegProgressive = true;
244
283
  }
245
284
  }
246
- const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
247
- if (is.defined(optimiseCoding)) {
248
- this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
249
- }
250
285
  const quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
251
286
  if (is.defined(quantisationTable)) {
252
287
  if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
@@ -262,26 +297,31 @@ function jpeg (options) {
262
297
  /**
263
298
  * Use these PNG options for output image.
264
299
  *
265
- * PNG output is always full colour at 8 or 16 bits per pixel.
300
+ * By default, PNG output is full colour at 8 or 16 bits per pixel.
266
301
  * Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
267
- *
268
- * Some of these options require the use of a globally-installed libvips compiled with support for libimagequant (GPL).
302
+ * Set `palette` to `true` for slower, indexed PNG output.
269
303
  *
270
304
  * @example
271
- * // Convert any input to PNG output
305
+ * // Convert any input to full colour PNG output
272
306
  * const data = await sharp(input)
273
307
  * .png()
274
308
  * .toBuffer();
275
309
  *
310
+ * @example
311
+ * // Convert any input to indexed PNG output (slower)
312
+ * const data = await sharp(input)
313
+ * .png({ palette: true })
314
+ * .toBuffer();
315
+ *
276
316
  * @param {Object} [options]
277
317
  * @param {boolean} [options.progressive=false] - use progressive (interlace) scan
278
- * @param {number} [options.compressionLevel=9] - zlib compression level, 0-9
318
+ * @param {number} [options.compressionLevel=6] - zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest)
279
319
  * @param {boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
280
- * @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libvips compiled with support for libimagequant
281
- * @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, sets `palette` to `true`, requires libvips compiled with support for libimagequant
282
- * @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true`, requires libvips compiled with support for libimagequant
283
- * @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true`, requires libvips compiled with support for libimagequant
284
- * @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true`, requires libvips compiled with support for libimagequant
320
+ * @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support
321
+ * @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, sets `palette` to `true`
322
+ * @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true`
323
+ * @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true`
324
+ * @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true`
285
325
  * @param {boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
286
326
  * @returns {Sharp}
287
327
  * @throws {Error} Invalid options
@@ -411,7 +451,7 @@ function webp (options) {
411
451
  /* istanbul ignore next */
412
452
  function gif (options) {
413
453
  if (!this.constructor.format.magick.output.buffer) {
414
- throw new Error('The gif operation requires libvips to have been installed with support for ImageMagick');
454
+ throw errMagickSave;
415
455
  }
416
456
  trySetAnimationOptions(options, this.options);
417
457
  return this._updateFormatOut('gif', options);
@@ -471,15 +511,15 @@ function trySetAnimationOptions (source, target) {
471
511
  * @param {Object} [options] - output options
472
512
  * @param {number} [options.quality=80] - quality, integer 1-100
473
513
  * @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
474
- * @param {boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
475
- * @param {boolean} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
514
+ * @param {string} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
515
+ * @param {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
476
516
  * @param {boolean} [options.pyramid=false] - write an image pyramid
477
517
  * @param {boolean} [options.tile=false] - write a tiled tiff
478
- * @param {boolean} [options.tileWidth=256] - horizontal tile size
479
- * @param {boolean} [options.tileHeight=256] - vertical tile size
518
+ * @param {number} [options.tileWidth=256] - horizontal tile size
519
+ * @param {number} [options.tileHeight=256] - vertical tile size
480
520
  * @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
481
521
  * @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
482
- * @param {boolean} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
522
+ * @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
483
523
  * @returns {Sharp}
484
524
  * @throws {Error} Invalid options
485
525
  */
@@ -557,28 +597,43 @@ function tiff (options) {
557
597
  }
558
598
 
559
599
  /**
560
- * Use these HEIF options for output image.
600
+ * Use these AVIF options for output image.
561
601
  *
562
- * Support for HEIF (HEIC/AVIF) is experimental.
563
- * Do not use this in production systems.
602
+ * Whilst it is possible to create AVIF images smaller than 16x16 pixels,
603
+ * most web browsers do not display these properly.
564
604
  *
565
- * Requires a custom, globally-installed libvips compiled with support for libheif.
605
+ * @since 0.27.0
606
+ *
607
+ * @param {Object} [options] - output options
608
+ * @param {number} [options.quality=50] - quality, integer 1-100
609
+ * @param {boolean} [options.lossless=false] - use lossless compression
610
+ * @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
611
+ * @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
612
+ * @returns {Sharp}
613
+ * @throws {Error} Invalid options
614
+ */
615
+ function avif (options) {
616
+ return this.heif({ ...options, compression: 'av1' });
617
+ }
618
+
619
+ /**
620
+ * Use these HEIF options for output image.
566
621
  *
567
- * Most versions of libheif support only the patent-encumbered HEVC compression format.
622
+ * Support for patent-encumbered HEIC images requires the use of a
623
+ * globally-installed libvips compiled with support for libheif, libde265 and x265.
568
624
  *
569
625
  * @since 0.23.0
570
626
  *
571
627
  * @param {Object} [options] - output options
572
- * @param {number} [options.quality=80] - quality, integer 1-100
573
- * @param {boolean} [options.compression='hevc'] - compression format: hevc, avc, jpeg, av1
628
+ * @param {number} [options.quality=50] - quality, integer 1-100
629
+ * @param {string} [options.compression='av1'] - compression format: av1, hevc
574
630
  * @param {boolean} [options.lossless=false] - use lossless compression
631
+ * @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
632
+ * @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
575
633
  * @returns {Sharp}
576
634
  * @throws {Error} Invalid options
577
635
  */
578
636
  function heif (options) {
579
- if (!this.constructor.format.heif.output.buffer) {
580
- throw new Error('The heif operation requires libvips to have been installed with support for libheif');
581
- }
582
637
  if (is.object(options)) {
583
638
  if (is.defined(options.quality)) {
584
639
  if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
@@ -595,10 +650,24 @@ function heif (options) {
595
650
  }
596
651
  }
597
652
  if (is.defined(options.compression)) {
598
- if (is.string(options.compression) && is.inArray(options.compression, ['hevc', 'avc', 'jpeg', 'av1'])) {
653
+ if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
599
654
  this.options.heifCompression = options.compression;
600
655
  } else {
601
- throw is.invalidParameterError('compression', 'one of: hevc, avc, jpeg, av1', options.compression);
656
+ throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
657
+ }
658
+ }
659
+ if (is.defined(options.speed)) {
660
+ if (is.integer(options.speed) && is.inRange(options.speed, 0, 8)) {
661
+ this.options.heifSpeed = options.speed;
662
+ } else {
663
+ throw is.invalidParameterError('speed', 'integer between 0 and 8', options.speed);
664
+ }
665
+ }
666
+ if (is.defined(options.chromaSubsampling)) {
667
+ if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
668
+ this.options.heifChromaSubsampling = options.chromaSubsampling;
669
+ } else {
670
+ throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
602
671
  }
603
672
  }
604
673
  }
@@ -660,6 +729,7 @@ function raw () {
660
729
  * @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`.
661
730
  * @param {boolean} [options.centre=false] centre image in tile.
662
731
  * @param {boolean} [options.center=false] alternative spelling of centre.
732
+ * @param {string} [options.id='https://example.com/iiif'] when `layout` is `iiif`, sets the `@id` attribute of `info.json`
663
733
  * @returns {Sharp}
664
734
  * @throws {Error} Invalid parameters
665
735
  */
@@ -733,6 +803,14 @@ function tile (options) {
733
803
  if (is.defined(centre)) {
734
804
  this._setBooleanOption('tileCentre', centre);
735
805
  }
806
+ // @id attribute for IIIF layout
807
+ if (is.defined(options.id)) {
808
+ if (is.string(options.id)) {
809
+ this.options.tileId = options.id;
810
+ } else {
811
+ throw is.invalidParameterError('id', 'string', options.id);
812
+ }
813
+ }
736
814
  }
737
815
  // Format
738
816
  if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
@@ -891,6 +969,7 @@ module.exports = function (Sharp) {
891
969
  png,
892
970
  webp,
893
971
  tiff,
972
+ avif,
894
973
  heif,
895
974
  gif,
896
975
  raw,
package/lib/resize.js CHANGED
@@ -302,11 +302,20 @@ function resize (width, height, options) {
302
302
  * })
303
303
  * ...
304
304
  *
305
+ * @example
306
+ * // Add a row of 10 red pixels to the bottom
307
+ * sharp(input)
308
+ * .extend({
309
+ * bottom: 10,
310
+ * background: 'red'
311
+ * })
312
+ * ...
313
+ *
305
314
  * @param {(number|Object)} extend - single pixel count to add to all edges or an Object with per-edge counts
306
- * @param {number} [extend.top]
307
- * @param {number} [extend.left]
308
- * @param {number} [extend.bottom]
309
- * @param {number} [extend.right]
315
+ * @param {number} [extend.top=0]
316
+ * @param {number} [extend.left=0]
317
+ * @param {number} [extend.bottom=0]
318
+ * @param {number} [extend.right=0]
310
319
  * @param {String|Object} [extend.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
311
320
  * @returns {Sharp}
312
321
  * @throws {Error} Invalid parameters
@@ -317,17 +326,35 @@ function extend (extend) {
317
326
  this.options.extendBottom = extend;
318
327
  this.options.extendLeft = extend;
319
328
  this.options.extendRight = extend;
320
- } else if (
321
- is.object(extend) &&
322
- is.integer(extend.top) && extend.top >= 0 &&
323
- is.integer(extend.bottom) && extend.bottom >= 0 &&
324
- is.integer(extend.left) && extend.left >= 0 &&
325
- is.integer(extend.right) && extend.right >= 0
326
- ) {
327
- this.options.extendTop = extend.top;
328
- this.options.extendBottom = extend.bottom;
329
- this.options.extendLeft = extend.left;
330
- this.options.extendRight = extend.right;
329
+ } else if (is.object(extend)) {
330
+ if (is.defined(extend.top)) {
331
+ if (is.integer(extend.top) && extend.top >= 0) {
332
+ this.options.extendTop = extend.top;
333
+ } else {
334
+ throw is.invalidParameterError('top', 'positive integer', extend.top);
335
+ }
336
+ }
337
+ if (is.defined(extend.bottom)) {
338
+ if (is.integer(extend.bottom) && extend.bottom >= 0) {
339
+ this.options.extendBottom = extend.bottom;
340
+ } else {
341
+ throw is.invalidParameterError('bottom', 'positive integer', extend.bottom);
342
+ }
343
+ }
344
+ if (is.defined(extend.left)) {
345
+ if (is.integer(extend.left) && extend.left >= 0) {
346
+ this.options.extendLeft = extend.left;
347
+ } else {
348
+ throw is.invalidParameterError('left', 'positive integer', extend.left);
349
+ }
350
+ }
351
+ if (is.defined(extend.right)) {
352
+ if (is.integer(extend.right) && extend.right >= 0) {
353
+ this.options.extendRight = extend.right;
354
+ } else {
355
+ throw is.invalidParameterError('right', 'positive integer', extend.right);
356
+ }
357
+ }
331
358
  this._setBackgroundColourOption('extendBackground', extend.background);
332
359
  } else {
333
360
  throw is.invalidParameterError('extend', 'integer or object', extend);
package/lib/utility.js CHANGED
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const events = require('events');
4
+ const detectLibc = require('detect-libc');
5
+
4
6
  const is = require('./is');
5
7
  const sharp = require('../build/Release/sharp.node');
6
8
 
@@ -84,8 +86,12 @@ cache(true);
84
86
  /**
85
87
  * Gets or, when a concurrency is provided, sets
86
88
  * the number of threads _libvips'_ should create to process each image.
87
- * The default value is the number of CPU cores.
88
- * A value of `0` will reset to this default.
89
+ *
90
+ * The default value is the number of CPU cores,
91
+ * except when using glibc-based Linux without jemalloc,
92
+ * where the default is `1` to help reduce memory fragmentation.
93
+ *
94
+ * A value of `0` will reset this to the number of CPU cores.
89
95
  *
90
96
  * The maximum number of images that can be processed in parallel
91
97
  * is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
@@ -103,6 +109,11 @@ cache(true);
103
109
  function concurrency (concurrency) {
104
110
  return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
105
111
  }
112
+ /* istanbul ignore next */
113
+ if (detectLibc.family === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
114
+ // Reduce default concurrency to 1 when using glibc memory allocator
115
+ sharp.concurrency(1);
116
+ }
106
117
 
107
118
  /**
108
119
  * An EventEmitter that emits a `change` event when a task is either:
@@ -157,14 +168,10 @@ simd(true);
157
168
  * @private
158
169
  */
159
170
  module.exports = function (Sharp) {
160
- [
161
- cache,
162
- concurrency,
163
- counters,
164
- simd
165
- ].forEach(function (f) {
166
- Sharp[f.name] = f;
167
- });
171
+ Sharp.cache = cache;
172
+ Sharp.concurrency = concurrency;
173
+ Sharp.counters = counters;
174
+ Sharp.simd = simd;
168
175
  Sharp.format = format;
169
176
  Sharp.interpolators = interpolators;
170
177
  Sharp.versions = versions;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sharp",
3
- "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
4
- "version": "0.26.3",
3
+ "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images",
4
+ "version": "0.28.0",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -70,17 +70,21 @@
70
70
  "Roman Malieiev <aromaleev@gmail.com>",
71
71
  "Tomas Szabo <tomas.szabo@deftomat.com>",
72
72
  "Robert O'Rourke <robert@o-rourke.org>",
73
- "Guillermo Alfonso Varela Chouciño <guillevch@gmail.com>"
73
+ "Guillermo Alfonso Varela Chouciño <guillevch@gmail.com>",
74
+ "Christian Flintrup <chr@gigahost.dk>",
75
+ "Manan Jadhav <manan@motionden.com>",
76
+ "Leon Radley <leon@radley.se>",
77
+ "alza54 <alza54@thiocod.in>"
74
78
  ],
75
79
  "scripts": {
76
80
  "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
77
81
  "clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
78
- "test": "semistandard && cpplint && npm run test-unit && npm run test-licensing && node install/prebuild-ci",
79
- "test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
82
+ "test": "semistandard && cpplint && npm run test-unit && npm run test-licensing",
83
+ "test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=1000 --timeout=60000 ./test/unit/*.js",
80
84
  "test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
81
85
  "test-coverage": "./test/coverage/report.sh",
82
86
  "test-leak": "./test/leak/leak.sh",
83
- "docs-build": "documentation lint lib && for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done && node docs/search-index/build",
87
+ "docs-build": "documentation lint lib && node docs/build && node docs/search-index/build",
84
88
  "docs-serve": "cd docs && npx serve",
85
89
  "docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
86
90
  },
@@ -88,7 +92,6 @@
88
92
  "files": [
89
93
  "binding.gyp",
90
94
  "install/**",
91
- "!install/prebuild-ci.js",
92
95
  "lib/**",
93
96
  "src/**"
94
97
  ],
@@ -100,6 +103,7 @@
100
103
  "jpeg",
101
104
  "png",
102
105
  "webp",
106
+ "avif",
103
107
  "tiff",
104
108
  "gif",
105
109
  "svg",
@@ -113,26 +117,24 @@
113
117
  "vips"
114
118
  ],
115
119
  "dependencies": {
116
- "array-flatten": "^3.0.0",
117
120
  "color": "^3.1.3",
118
121
  "detect-libc": "^1.0.3",
119
- "node-addon-api": "^3.0.2",
120
- "npmlog": "^4.1.2",
121
- "prebuild-install": "^6.0.0",
122
- "semver": "^7.3.2",
123
- "simple-get": "^4.0.0",
122
+ "node-addon-api": "^3.1.0",
123
+ "prebuild-install": "^6.0.1",
124
+ "semver": "^7.3.5",
125
+ "simple-get": "^3.1.0",
124
126
  "tar-fs": "^2.1.1",
125
127
  "tunnel-agent": "^0.6.0"
126
128
  },
127
129
  "devDependencies": {
128
130
  "async": "^3.2.0",
129
- "cc": "^2.0.1",
130
- "decompress-zip": "^0.3.2",
131
- "documentation": "^13.1.0",
131
+ "cc": "^3.0.1",
132
+ "decompress-zip": "^0.3.3",
133
+ "documentation": "^13.2.0",
132
134
  "exif-reader": "^1.0.3",
133
135
  "icc": "^2.0.0",
134
136
  "license-checker": "^25.0.1",
135
- "mocha": "^8.2.1",
137
+ "mocha": "^8.3.2",
136
138
  "mock-fs": "^4.13.0",
137
139
  "nyc": "^15.1.0",
138
140
  "prebuild": "^10.0.1",
@@ -141,7 +143,7 @@
141
143
  },
142
144
  "license": "Apache-2.0",
143
145
  "config": {
144
- "libvips": "8.10.0",
146
+ "libvips": "8.10.6",
145
147
  "runtime": "napi",
146
148
  "target": 3
147
149
  },