sharp 0.30.2 → 0.30.3

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/colour.js CHANGED
@@ -19,6 +19,11 @@ const colourspace = {
19
19
  * Tint the image using the provided chroma while preserving the image luminance.
20
20
  * An alpha channel may be present and will be unchanged by the operation.
21
21
  *
22
+ * @example
23
+ * const output = await sharp(input)
24
+ * .tint({ r: 255, g: 240, b: 16 })
25
+ * .toBuffer();
26
+ *
22
27
  * @param {string|Object} rgb - parsed by the [color](https://www.npmjs.org/package/color) module to extract chroma values.
23
28
  * @returns {Sharp}
24
29
  * @throws {Error} Invalid parameter
@@ -37,6 +42,10 @@ function tint (rgb) {
37
42
  * This may be overridden by other sharp operations such as `toColourspace('b-w')`,
38
43
  * which will produce an output image containing one color channel.
39
44
  * An alpha channel may be present, and will be unchanged by the operation.
45
+ *
46
+ * @example
47
+ * const output = await sharp(input).greyscale().toBuffer();
48
+ *
40
49
  * @param {Boolean} [greyscale=true]
41
50
  * @returns {Sharp}
42
51
  */
package/lib/composite.js CHANGED
@@ -56,6 +56,21 @@ const blend = {
56
56
  * @since 0.22.0
57
57
  *
58
58
  * @example
59
+ * await sharp(background)
60
+ * .composite([
61
+ * { input: layer1, gravity: 'northwest' },
62
+ * { input: layer2, gravity: 'southeast' },
63
+ * ])
64
+ * .toFile('combined.png');
65
+ *
66
+ * @example
67
+ * const output = await sharp('input.gif', { animated: true })
68
+ * .composite([
69
+ * { input: 'overlay.png', tile: true, blend: 'saturate' }
70
+ * ])
71
+ * .toBuffer();
72
+ *
73
+ * @example
59
74
  * sharp('input.png')
60
75
  * .rotate(180)
61
76
  * .resize(300)
@@ -89,6 +104,7 @@ const blend = {
89
104
  * @param {Number} [images[].raw.width]
90
105
  * @param {Number} [images[].raw.height]
91
106
  * @param {Number} [images[].raw.channels]
107
+ * @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
92
108
  * @param {boolean} [images[].failOnError=true] - @see {@link /api-constructor#parameters|constructor parameters}
93
109
  * @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
94
110
  * @returns {Sharp}
@@ -186,8 +186,11 @@ const Sharp = function (input, options) {
186
186
  medianSize: 0,
187
187
  blurSigma: 0,
188
188
  sharpenSigma: 0,
189
- sharpenFlat: 1,
190
- sharpenJagged: 2,
189
+ sharpenM1: 1,
190
+ sharpenM2: 2,
191
+ sharpenX1: 2,
192
+ sharpenY2: 10,
193
+ sharpenY3: 20,
191
194
  threshold: 0,
192
195
  thresholdGrayscale: true,
193
196
  trimThreshold: 0,
package/lib/input.js CHANGED
@@ -299,8 +299,8 @@ function _isStreamInput () {
299
299
  *
300
300
  * - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
301
301
  * - `size`: Total size of image in bytes, for Stream and Buffer input only
302
- * - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
303
- * - `height`: Number of pixels high (EXIF orientation is not taken into consideration)
302
+ * - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
303
+ * - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
304
304
  * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation)
305
305
  * - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
306
306
  * - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat)
@@ -343,6 +343,17 @@ function _isStreamInput () {
343
343
  * // data contains a WebP image half the width and height of the original JPEG
344
344
  * });
345
345
  *
346
+ * @example
347
+ * // Based on EXIF rotation metadata, get the right-side-up width and height:
348
+ *
349
+ * const size = getNormalSize(await sharp(input).metadata());
350
+ *
351
+ * function getNormalSize({ width, height, orientation }) {
352
+ * return orientation || 0 >= 5
353
+ * ? { width: height, height: width }
354
+ * : { width, height };
355
+ * }
356
+ *
346
357
  * @param {Function} [callback] - called with the arguments `(err, metadata)`
347
358
  * @returns {Promise<Object>|Sharp}
348
359
  */
@@ -401,9 +412,9 @@ function metadata (callback) {
401
412
  * - `maxX` (x-coordinate of one of the pixel where the maximum lies)
402
413
  * - `maxY` (y-coordinate of one of the pixel where the maximum lies)
403
414
  * - `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
404
- * - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
405
- * - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
406
- * - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
415
+ * - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any.
416
+ * - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any.
417
+ * - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram.
407
418
  *
408
419
  * **Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
409
420
  * written to a buffer in order to run `stats` on the result (see third example).
package/lib/operation.js CHANGED
@@ -63,6 +63,10 @@ function rotate (angle, options) {
63
63
  /**
64
64
  * Flip the image about the vertical Y axis. This always occurs after rotation, if any.
65
65
  * The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
66
+ *
67
+ * @example
68
+ * const output = await sharp(input).flip().toBuffer();
69
+ *
66
70
  * @param {Boolean} [flip=true]
67
71
  * @returns {Sharp}
68
72
  */
@@ -74,6 +78,10 @@ function flip (flip) {
74
78
  /**
75
79
  * Flop the image about the horizontal X axis. This always occurs after rotation, if any.
76
80
  * The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
81
+ *
82
+ * @example
83
+ * const output = await sharp(input).flop().toBuffer();
84
+ *
77
85
  * @param {Boolean} [flop=true]
78
86
  * @returns {Sharp}
79
87
  */
@@ -185,40 +193,107 @@ function affine (matrix, options) {
185
193
  * When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
186
194
  * Separate control over the level of sharpening in "flat" and "jagged" areas is available.
187
195
  *
188
- * @param {number} [sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
189
- * @param {number} [flat=1.0] - the level of sharpening to apply to "flat" areas.
190
- * @param {number} [jagged=2.0] - the level of sharpening to apply to "jagged" areas.
196
+ * See {@link https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen|libvips sharpen} operation.
197
+ *
198
+ * @example
199
+ * const data = await sharp(input).sharpen().toBuffer();
200
+ *
201
+ * @example
202
+ * const data = await sharp(input).sharpen({ sigma: 2 }).toBuffer();
203
+ *
204
+ * @example
205
+ * const data = await sharp(input)
206
+ * .sharpen({
207
+ * sigma: 2,
208
+ * m1: 0
209
+ * m2: 3,
210
+ * x1: 3,
211
+ * y2: 15,
212
+ * y3: 15,
213
+ * })
214
+ * .toBuffer();
215
+ *
216
+ * @param {Object|number} [options] - if present, is an Object with attributes or (deprecated) a number for `options.sigma`.
217
+ * @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
218
+ * @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas.
219
+ * @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas.
220
+ * @param {number} [options.x1=2.0] - threshold between "flat" and "jagged"
221
+ * @param {number} [options.y2=10.0] - maximum amount of brightening.
222
+ * @param {number} [options.y3=20.0] - maximum amount of darkening.
223
+ * @param {number} [flat] - (deprecated) see `options.m1`.
224
+ * @param {number} [jagged] - (deprecated) see `options.m2`.
191
225
  * @returns {Sharp}
192
226
  * @throws {Error} Invalid parameters
193
227
  */
194
- function sharpen (sigma, flat, jagged) {
195
- if (!is.defined(sigma)) {
228
+ function sharpen (options, flat, jagged) {
229
+ if (!is.defined(options)) {
196
230
  // No arguments: default to mild sharpen
197
231
  this.options.sharpenSigma = -1;
198
- } else if (is.bool(sigma)) {
199
- // Boolean argument: apply mild sharpen?
200
- this.options.sharpenSigma = sigma ? -1 : 0;
201
- } else if (is.number(sigma) && is.inRange(sigma, 0.01, 10000)) {
202
- // Numeric argument: specific sigma
203
- this.options.sharpenSigma = sigma;
204
- // Control over flat areas
232
+ } else if (is.bool(options)) {
233
+ // Deprecated boolean argument: apply mild sharpen?
234
+ this.options.sharpenSigma = options ? -1 : 0;
235
+ } else if (is.number(options) && is.inRange(options, 0.01, 10000)) {
236
+ // Deprecated numeric argument: specific sigma
237
+ this.options.sharpenSigma = options;
238
+ // Deprecated control over flat areas
205
239
  if (is.defined(flat)) {
206
240
  if (is.number(flat) && is.inRange(flat, 0, 10000)) {
207
- this.options.sharpenFlat = flat;
241
+ this.options.sharpenM1 = flat;
208
242
  } else {
209
243
  throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
210
244
  }
211
245
  }
212
- // Control over jagged areas
246
+ // Deprecated control over jagged areas
213
247
  if (is.defined(jagged)) {
214
248
  if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
215
- this.options.sharpenJagged = jagged;
249
+ this.options.sharpenM2 = jagged;
216
250
  } else {
217
251
  throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
218
252
  }
219
253
  }
254
+ } else if (is.plainObject(options)) {
255
+ if (is.number(options.sigma) && is.inRange(options.sigma, 0.01, 10000)) {
256
+ this.options.sharpenSigma = options.sigma;
257
+ } else {
258
+ throw is.invalidParameterError('options.sigma', 'number between 0.01 and 10000', options.sigma);
259
+ }
260
+ if (is.defined(options.m1)) {
261
+ if (is.number(options.m1) && is.inRange(options.m1, 0, 10000)) {
262
+ this.options.sharpenM1 = options.m1;
263
+ } else {
264
+ throw is.invalidParameterError('options.m1', 'number between 0 and 10000', options.m1);
265
+ }
266
+ }
267
+ if (is.defined(options.m2)) {
268
+ if (is.number(options.m2) && is.inRange(options.m2, 0, 10000)) {
269
+ this.options.sharpenM2 = options.m2;
270
+ } else {
271
+ throw is.invalidParameterError('options.m2', 'number between 0 and 10000', options.m2);
272
+ }
273
+ }
274
+ if (is.defined(options.x1)) {
275
+ if (is.number(options.x1) && is.inRange(options.x1, 0, 10000)) {
276
+ this.options.sharpenX1 = options.x1;
277
+ } else {
278
+ throw is.invalidParameterError('options.x1', 'number between 0 and 10000', options.x1);
279
+ }
280
+ }
281
+ if (is.defined(options.y2)) {
282
+ if (is.number(options.y2) && is.inRange(options.y2, 0, 10000)) {
283
+ this.options.sharpenY2 = options.y2;
284
+ } else {
285
+ throw is.invalidParameterError('options.y2', 'number between 0 and 10000', options.y2);
286
+ }
287
+ }
288
+ if (is.defined(options.y3)) {
289
+ if (is.number(options.y3) && is.inRange(options.y3, 0, 10000)) {
290
+ this.options.sharpenY3 = options.y3;
291
+ } else {
292
+ throw is.invalidParameterError('options.y3', 'number between 0 and 10000', options.y3);
293
+ }
294
+ }
220
295
  } else {
221
- throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', sigma);
296
+ throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', options);
222
297
  }
223
298
  return this;
224
299
  }
@@ -226,6 +301,13 @@ function sharpen (sigma, flat, jagged) {
226
301
  /**
227
302
  * Apply median filter.
228
303
  * When used without parameters the default window is 3x3.
304
+ *
305
+ * @example
306
+ * const output = await sharp(input).median().toBuffer();
307
+ *
308
+ * @example
309
+ * const output = await sharp(input).median(5).toBuffer();
310
+ *
229
311
  * @param {number} [size=3] square mask size: size x size
230
312
  * @returns {Sharp}
231
313
  * @throws {Error} Invalid parameters
@@ -356,6 +438,10 @@ function negate (options) {
356
438
 
357
439
  /**
358
440
  * Enhance output image contrast by stretching its luminance to cover the full dynamic range.
441
+ *
442
+ * @example
443
+ * const output = await sharp(input).normalise().toBuffer();
444
+ *
359
445
  * @param {Boolean} [normalise=true]
360
446
  * @returns {Sharp}
361
447
  */
@@ -366,6 +452,10 @@ function normalise (normalise) {
366
452
 
367
453
  /**
368
454
  * Alternative spelling of normalise.
455
+ *
456
+ * @example
457
+ * const output = await sharp(input).normalize().toBuffer();
458
+ *
369
459
  * @param {Boolean} [normalize=true]
370
460
  * @returns {Sharp}
371
461
  */
@@ -381,6 +471,14 @@ function normalize (normalize) {
381
471
  *
382
472
  * @since 0.28.3
383
473
  *
474
+ * @example
475
+ * const output = await sharp(input)
476
+ * .clahe({
477
+ * width: 3,
478
+ * height: 3,
479
+ * })
480
+ * .toBuffer();
481
+ *
384
482
  * @param {Object} options
385
483
  * @param {number} options.width - integer width of the region in pixels.
386
484
  * @param {number} options.height - integer height of the region in pixels.
@@ -590,28 +688,38 @@ function recomb (inputMatrix) {
590
688
  * @since 0.22.1
591
689
  *
592
690
  * @example
593
- * sharp(input)
691
+ * // increase brightness by a factor of 2
692
+ * const output = await sharp(input)
594
693
  * .modulate({
595
- * brightness: 2 // increase brightness by a factor of 2
596
- * });
694
+ * brightness: 2
695
+ * })
696
+ * .toBuffer();
597
697
  *
598
- * sharp(input)
698
+ * @example
699
+ * // hue-rotate by 180 degrees
700
+ * const output = await sharp(input)
599
701
  * .modulate({
600
- * hue: 180 // hue-rotate by 180 degrees
601
- * });
702
+ * hue: 180
703
+ * })
704
+ * .toBuffer();
602
705
  *
603
- * sharp(input)
706
+ * @example
707
+ * // increase lightness by +50
708
+ * const output = await sharp(input)
604
709
  * .modulate({
605
- * lightness: 50 // increase lightness by +50
606
- * });
710
+ * lightness: 50
711
+ * })
712
+ * .toBuffer();
607
713
  *
714
+ * @example
608
715
  * // decreate brightness and saturation while also hue-rotating by 90 degrees
609
- * sharp(input)
716
+ * const output = await sharp(input)
610
717
  * .modulate({
611
718
  * brightness: 0.5,
612
719
  * saturation: 0.5,
613
- * hue: 90
614
- * });
720
+ * hue: 90,
721
+ * })
722
+ * .toBuffer();
615
723
  *
616
724
  * @param {Object} [options]
617
725
  * @param {number} [options.brightness] Brightness multiplier
package/lib/sharp.js CHANGED
@@ -21,7 +21,7 @@ try {
21
21
  '- Consult the installation documentation: https://sharp.pixelplumbing.com/install'
22
22
  );
23
23
  // Check loaded
24
- if (process.platform === 'win32') {
24
+ if (process.platform === 'win32' || /symbol/.test(err.message)) {
25
25
  const loadedModule = Object.keys(require.cache).find((i) => /[\\/]build[\\/]Release[\\/]sharp(.*)\.node$/.test(i));
26
26
  if (loadedModule) {
27
27
  const [, loadedPackage] = loadedModule.match(/node_modules[\\/]([^\\/]+)[\\/]/);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sharp",
3
3
  "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
4
- "version": "0.30.2",
4
+ "version": "0.30.3",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -143,7 +143,7 @@
143
143
  "exif-reader": "^1.0.3",
144
144
  "icc": "^2.0.0",
145
145
  "license-checker": "^25.0.1",
146
- "mocha": "^9.2.1",
146
+ "mocha": "^9.2.2",
147
147
  "mock-fs": "^5.1.2",
148
148
  "nyc": "^15.1.0",
149
149
  "prebuild": "^11.0.3",
package/src/operations.cc CHANGED
@@ -209,7 +209,8 @@ namespace sharp {
209
209
  /*
210
210
  * Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
211
211
  */
212
- VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged) {
212
+ VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
213
+ double const x1, double const y2, double const y3) {
213
214
  if (sigma == -1.0) {
214
215
  // Fast, mild sharpen
215
216
  VImage sharpen = VImage::new_matrixv(3, 3,
@@ -224,8 +225,14 @@ namespace sharp {
224
225
  if (colourspaceBeforeSharpen == VIPS_INTERPRETATION_RGB) {
225
226
  colourspaceBeforeSharpen = VIPS_INTERPRETATION_sRGB;
226
227
  }
227
- return image.sharpen(
228
- VImage::option()->set("sigma", sigma)->set("m1", flat)->set("m2", jagged))
228
+ return image
229
+ .sharpen(VImage::option()
230
+ ->set("sigma", sigma)
231
+ ->set("m1", m1)
232
+ ->set("m2", m2)
233
+ ->set("x1", x1)
234
+ ->set("y2", y2)
235
+ ->set("y3", y3))
229
236
  .colourspace(colourspaceBeforeSharpen);
230
237
  }
231
238
  }
package/src/operations.h CHANGED
@@ -64,7 +64,8 @@ namespace sharp {
64
64
  /*
65
65
  * Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
66
66
  */
67
- VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged);
67
+ VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
68
+ double const x1, double const y2, double const y3);
68
69
 
69
70
  /*
70
71
  Threshold an image
package/src/pipeline.cc CHANGED
@@ -354,7 +354,7 @@ class PipelineWorker : public Napi::AsyncWorker {
354
354
  }
355
355
 
356
356
  bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
357
- (shouldResize || shouldBlur || shouldConv || shouldSharpen || shouldComposite);
357
+ (shouldResize || shouldBlur || shouldConv || shouldSharpen);
358
358
 
359
359
  // Premultiply image alpha channel before all transformations to avoid
360
360
  // dark fringing around bright pixels
@@ -577,7 +577,8 @@ class PipelineWorker : public Napi::AsyncWorker {
577
577
 
578
578
  // Sharpen
579
579
  if (shouldSharpen) {
580
- image = sharp::Sharpen(image, baton->sharpenSigma, baton->sharpenFlat, baton->sharpenJagged);
580
+ image = sharp::Sharpen(image, baton->sharpenSigma, baton->sharpenM1, baton->sharpenM2,
581
+ baton->sharpenX1, baton->sharpenY2, baton->sharpenY3);
581
582
  }
582
583
 
583
584
  // Composite
@@ -1400,8 +1401,11 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1400
1401
  baton->lightness = sharp::AttrAsDouble(options, "lightness");
1401
1402
  baton->medianSize = sharp::AttrAsUint32(options, "medianSize");
1402
1403
  baton->sharpenSigma = sharp::AttrAsDouble(options, "sharpenSigma");
1403
- baton->sharpenFlat = sharp::AttrAsDouble(options, "sharpenFlat");
1404
- baton->sharpenJagged = sharp::AttrAsDouble(options, "sharpenJagged");
1404
+ baton->sharpenM1 = sharp::AttrAsDouble(options, "sharpenM1");
1405
+ baton->sharpenM2 = sharp::AttrAsDouble(options, "sharpenM2");
1406
+ baton->sharpenX1 = sharp::AttrAsDouble(options, "sharpenX1");
1407
+ baton->sharpenY2 = sharp::AttrAsDouble(options, "sharpenY2");
1408
+ baton->sharpenY3 = sharp::AttrAsDouble(options, "sharpenY3");
1405
1409
  baton->threshold = sharp::AttrAsInt32(options, "threshold");
1406
1410
  baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
1407
1411
  baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
package/src/pipeline.h CHANGED
@@ -90,8 +90,11 @@ struct PipelineBaton {
90
90
  double lightness;
91
91
  int medianSize;
92
92
  double sharpenSigma;
93
- double sharpenFlat;
94
- double sharpenJagged;
93
+ double sharpenM1;
94
+ double sharpenM2;
95
+ double sharpenX1;
96
+ double sharpenY2;
97
+ double sharpenY3;
95
98
  int threshold;
96
99
  bool thresholdGrayscale;
97
100
  double trimThreshold;
@@ -234,8 +237,11 @@ struct PipelineBaton {
234
237
  lightness(0),
235
238
  medianSize(0),
236
239
  sharpenSigma(0.0),
237
- sharpenFlat(1.0),
238
- sharpenJagged(2.0),
240
+ sharpenM1(1.0),
241
+ sharpenM2(2.0),
242
+ sharpenX1(2.0),
243
+ sharpenY2(10.0),
244
+ sharpenY3(20.0),
239
245
  threshold(0),
240
246
  thresholdGrayscale(true),
241
247
  trimThreshold(0.0),