sharp 0.27.1 → 0.30.5

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/composite.js CHANGED
@@ -50,12 +50,27 @@ const blend = {
50
50
  * `hard-light`, `soft-light`, `difference`, `exclusion`.
51
51
  *
52
52
  * More information about blend modes can be found at
53
- * https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
53
+ * https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
54
54
  * and https://www.cairographics.org/operators/
55
55
  *
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,9 @@ 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.
108
+ * @param {string} [images[].failOn='warning'] - @see {@link /api-constructor#parameters|constructor parameters}
109
+ * @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
92
110
  * @returns {Sharp}
93
111
  * @throws {Error} Invalid parameters
94
112
  */
@@ -160,7 +178,6 @@ function composite (images) {
160
178
  throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
161
179
  }
162
180
  }
163
-
164
181
  return composite;
165
182
  });
166
183
  return this;
@@ -5,34 +5,7 @@ const stream = require('stream');
5
5
  const is = require('./is');
6
6
 
7
7
  require('./libvips').hasVendoredLibvips();
8
-
9
- /* istanbul ignore next */
10
- try {
11
- require('../build/Release/sharp.node');
12
- } catch (err) {
13
- // Bail early if bindings aren't available
14
- const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, ''];
15
- if (/NODE_MODULE_VERSION/.test(err.message)) {
16
- help.push('- Ensure the version of Node.js used at install time matches that used at runtime');
17
- } else if (/invalid ELF header/.test(err.message)) {
18
- help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
19
- } else if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
20
- help.push('- Run "brew update && brew upgrade vips"');
21
- } else if (/Cannot find module/.test(err.message)) {
22
- help.push('- Run "npm rebuild --verbose sharp" and look for errors');
23
- } else {
24
- help.push(
25
- '- Remove the "node_modules/sharp" directory then run',
26
- ' "npm install --ignore-scripts=false --verbose" and look for errors'
27
- );
28
- }
29
- help.push(
30
- '- Consult the installation documentation at https://sharp.pixelplumbing.com/install',
31
- '- Search for this error at https://github.com/lovell/sharp/issues', ''
32
- );
33
- const error = help.join('\n');
34
- throw new Error(error);
35
- }
8
+ require('./sharp');
36
9
 
37
10
  // Use NODE_DEBUG=sharp to enable libvips warnings
38
11
  const debuglog = util.debuglog('sharp');
@@ -40,7 +13,7 @@ const debuglog = util.debuglog('sharp');
40
13
  /**
41
14
  * Constructor factory to create an instance of `sharp`, to which further methods are chained.
42
15
  *
43
- * JPEG, PNG, WebP, AVIF or TIFF format image data can be streamed out from this object.
16
+ * JPEG, PNG, WebP, GIF, AVIF or TIFF format image data can be streamed out from this object.
44
17
  * When using Stream based output, derived attributes are available from the `info` event.
45
18
  *
46
19
  * Non-critical problems encountered during processing are emitted as `warning` events.
@@ -117,29 +90,33 @@ const debuglog = util.debuglog('sharp');
117
90
  * sigma: 30
118
91
  * }
119
92
  * }
120
- * }.toFile('noise.png');
93
+ * }).toFile('noise.png');
121
94
  *
122
- * @param {(Buffer|Uint8Array|Uint8ClampedArray|string)} [input] - if present, can be
123
- * a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
95
+ * @param {(Buffer|Uint8Array|Uint8ClampedArray|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array|Float32Array|Float64Array|string)} [input] - if present, can be
96
+ * a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
97
+ * a TypedArray containing raw pixel image data, or
124
98
  * a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
125
99
  * JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
126
100
  * @param {Object} [options] - if present, is an Object with optional attributes.
127
- * @param {boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
128
- * Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid.
101
+ * @param {string} [options.failOn='warning'] - level of sensitivity to invalid images, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels.
129
102
  * @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
130
103
  * (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
131
104
  * An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
105
+ * @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG).
132
106
  * @param {boolean} [options.sequentialRead=false] - Set this to `true` to use sequential rather than random access where possible.
133
107
  * This can reduce memory usage and might improve performance on some systems.
134
108
  * @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
135
109
  * @param {number} [options.pages=1] - number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages.
136
110
  * @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based.
111
+ * @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
137
112
  * @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
138
113
  * @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
139
114
  * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
140
115
  * @param {number} [options.raw.width] - integral number of pixels wide.
141
116
  * @param {number} [options.raw.height] - integral number of pixels high.
142
117
  * @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
118
+ * @param {boolean} [options.raw.premultiplied] - specifies that the raw input has already been premultiplied, set to `true`
119
+ * to avoid sharp premultiplying the image. (optional, default `false`)
143
120
  * @param {Object} [options.create] - describes a new image to be created.
144
121
  * @param {number} [options.create.width] - integral number of pixels wide.
145
122
  * @param {number} [options.create.height] - integral number of pixels high.
@@ -188,6 +165,7 @@ const Sharp = function (input, options) {
188
165
  extendRight: 0,
189
166
  extendBackground: [0, 0, 0, 255],
190
167
  withoutEnlargement: false,
168
+ withoutReduction: false,
191
169
  affineMatrix: [],
192
170
  affineBackground: [0, 0, 0, 255],
193
171
  affineIdx: 0,
@@ -203,11 +181,15 @@ const Sharp = function (input, options) {
203
181
  flatten: false,
204
182
  flattenBackground: [0, 0, 0],
205
183
  negate: false,
184
+ negateAlpha: true,
206
185
  medianSize: 0,
207
186
  blurSigma: 0,
208
187
  sharpenSigma: 0,
209
- sharpenFlat: 1,
210
- sharpenJagged: 2,
188
+ sharpenM1: 1,
189
+ sharpenM2: 2,
190
+ sharpenX1: 2,
191
+ sharpenY2: 10,
192
+ sharpenY3: 20,
211
193
  threshold: 0,
212
194
  thresholdGrayscale: true,
213
195
  trimThreshold: 0,
@@ -215,16 +197,21 @@ const Sharp = function (input, options) {
215
197
  gammaOut: 0,
216
198
  greyscale: false,
217
199
  normalise: false,
200
+ claheWidth: 0,
201
+ claheHeight: 0,
202
+ claheMaxSlope: 3,
218
203
  brightness: 1,
219
204
  saturation: 1,
220
205
  hue: 0,
206
+ lightness: 0,
221
207
  booleanBufferIn: null,
222
208
  booleanFileIn: '',
223
209
  joinChannelIn: [],
224
210
  extractChannel: -1,
225
211
  removeAlpha: false,
226
- ensureAlpha: false,
212
+ ensureAlpha: -1,
227
213
  colourspace: 'srgb',
214
+ colourspaceInput: 'last',
228
215
  composite: [],
229
216
  // output
230
217
  fileOut: '',
@@ -232,7 +219,9 @@ const Sharp = function (input, options) {
232
219
  streamOut: false,
233
220
  withMetadata: false,
234
221
  withMetadataOrientation: -1,
222
+ withMetadataDensity: 0,
235
223
  withMetadataIcc: '',
224
+ withMetadataStrs: {},
236
225
  resolveWithObject: false,
237
226
  // output format
238
227
  jpegQuality: 80,
@@ -244,18 +233,27 @@ const Sharp = function (input, options) {
244
233
  jpegOptimiseCoding: true,
245
234
  jpegQuantisationTable: 0,
246
235
  pngProgressive: false,
247
- pngCompressionLevel: 9,
236
+ pngCompressionLevel: 6,
248
237
  pngAdaptiveFiltering: false,
249
238
  pngPalette: false,
250
239
  pngQuality: 100,
251
- pngColours: 256,
240
+ pngEffort: 7,
241
+ pngBitdepth: 8,
252
242
  pngDither: 1,
243
+ jp2Quality: 80,
244
+ jp2TileHeight: 512,
245
+ jp2TileWidth: 512,
246
+ jp2Lossless: false,
247
+ jp2ChromaSubsampling: '4:4:4',
253
248
  webpQuality: 80,
254
249
  webpAlphaQuality: 100,
255
250
  webpLossless: false,
256
251
  webpNearLossless: false,
257
252
  webpSmartSubsample: false,
258
- webpReductionEffort: 4,
253
+ webpEffort: 4,
254
+ gifBitdepth: 8,
255
+ gifEffort: 7,
256
+ gifDither: 1,
259
257
  tiffQuality: 80,
260
258
  tiffCompression: 'jpeg',
261
259
  tiffPredictor: 'horizontal',
@@ -266,11 +264,13 @@ const Sharp = function (input, options) {
266
264
  tiffTileWidth: 256,
267
265
  tiffXres: 1.0,
268
266
  tiffYres: 1.0,
267
+ tiffResolutionUnit: 'inch',
269
268
  heifQuality: 50,
270
269
  heifLossless: false,
271
270
  heifCompression: 'av1',
272
- heifSpeed: 5,
273
- heifChromaSubsampling: '4:2:0',
271
+ heifEffort: 4,
272
+ heifChromaSubsampling: '4:4:4',
273
+ rawDepth: 'uchar',
274
274
  tileSize: 256,
275
275
  tileOverlap: 0,
276
276
  tileContainer: 'fs',
@@ -281,6 +281,8 @@ const Sharp = function (input, options) {
281
281
  tileSkipBlanks: -1,
282
282
  tileBackground: [255, 255, 255, 255],
283
283
  tileCentre: false,
284
+ tileId: 'https://example.com/iiif',
285
+ timeoutSeconds: 0,
284
286
  linearA: 1,
285
287
  linearB: 0,
286
288
  // Function to notify of libvips warnings
@@ -296,7 +298,8 @@ const Sharp = function (input, options) {
296
298
  this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
297
299
  return this;
298
300
  };
299
- util.inherits(Sharp, stream.Duplex);
301
+ Object.setPrototypeOf(Sharp.prototype, stream.Duplex.prototype);
302
+ Object.setPrototypeOf(Sharp, stream.Duplex);
300
303
 
301
304
  /**
302
305
  * Take a "snapshot" of the Sharp instance, returning a new instance.
@@ -316,9 +319,7 @@ util.inherits(Sharp, stream.Duplex);
316
319
  * // Using Promises to know when the pipeline is complete
317
320
  * const fs = require("fs");
318
321
  * const got = require("got");
319
- * const sharpStream = sharp({
320
- * failOnError: false
321
- * });
322
+ * const sharpStream = sharp({ failOn: 'none' });
322
323
  *
323
324
  * const promises = [];
324
325
  *
package/lib/input.js CHANGED
@@ -2,16 +2,16 @@
2
2
 
3
3
  const color = require('color');
4
4
  const is = require('./is');
5
- const sharp = require('../build/Release/sharp.node');
5
+ const sharp = require('./sharp');
6
6
 
7
7
  /**
8
8
  * Extract input options, if any, from an object.
9
9
  * @private
10
10
  */
11
11
  function _inputOptionsFromObject (obj) {
12
- const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages } = obj;
13
- return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages].some(is.defined)
14
- ? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages }
12
+ const { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd } = obj;
13
+ return [raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd].some(is.defined)
14
+ ? { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd }
15
15
  : undefined;
16
16
  }
17
17
 
@@ -21,8 +21,9 @@ function _inputOptionsFromObject (obj) {
21
21
  */
22
22
  function _createInputDescriptor (input, inputOptions, containerOptions) {
23
23
  const inputDescriptor = {
24
- failOnError: true,
24
+ failOn: 'warning',
25
25
  limitInputPixels: Math.pow(0x3FFF, 2),
26
+ unlimited: false,
26
27
  sequentialRead: false
27
28
  };
28
29
  if (is.string(input)) {
@@ -30,10 +31,15 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
30
31
  inputDescriptor.file = input;
31
32
  } else if (is.buffer(input)) {
32
33
  // Buffer
34
+ if (input.length === 0) {
35
+ throw Error('Input Buffer is empty');
36
+ }
33
37
  inputDescriptor.buffer = input;
34
- } else if (is.uint8Array(input)) {
35
- // Uint8Array or Uint8ClampedArray
36
- inputDescriptor.buffer = Buffer.from(input.buffer);
38
+ } else if (is.typedArray(input)) {
39
+ if (input.length === 0) {
40
+ throw Error('Input Bit Array is empty');
41
+ }
42
+ inputDescriptor.buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
37
43
  } else if (is.plainObject(input) && !is.defined(inputOptions)) {
38
44
  // Plain Object descriptor, e.g. create
39
45
  inputOptions = input;
@@ -50,14 +56,22 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
50
56
  }`);
51
57
  }
52
58
  if (is.object(inputOptions)) {
53
- // Fail on error
59
+ // Deprecated: failOnError
54
60
  if (is.defined(inputOptions.failOnError)) {
55
61
  if (is.bool(inputOptions.failOnError)) {
56
- inputDescriptor.failOnError = inputOptions.failOnError;
62
+ inputDescriptor.failOn = inputOptions.failOnError ? 'warning' : 'none';
57
63
  } else {
58
64
  throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
59
65
  }
60
66
  }
67
+ // failOn
68
+ if (is.defined(inputOptions.failOn)) {
69
+ if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {
70
+ inputDescriptor.failOn = inputOptions.failOn;
71
+ } else {
72
+ throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);
73
+ }
74
+ }
61
75
  // Density
62
76
  if (is.defined(inputOptions.density)) {
63
77
  if (is.inRange(inputOptions.density, 1, 100000)) {
@@ -78,6 +92,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
78
92
  throw is.invalidParameterError('limitInputPixels', 'integer >= 0', inputOptions.limitInputPixels);
79
93
  }
80
94
  }
95
+ // unlimited
96
+ if (is.defined(inputOptions.unlimited)) {
97
+ if (is.bool(inputOptions.unlimited)) {
98
+ inputDescriptor.unlimited = inputOptions.unlimited;
99
+ } else {
100
+ throw is.invalidParameterError('unlimited', 'boolean', inputOptions.unlimited);
101
+ }
102
+ }
81
103
  // sequentialRead
82
104
  if (is.defined(inputOptions.sequentialRead)) {
83
105
  if (is.bool(inputOptions.sequentialRead)) {
@@ -97,6 +119,38 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
97
119
  inputDescriptor.rawWidth = inputOptions.raw.width;
98
120
  inputDescriptor.rawHeight = inputOptions.raw.height;
99
121
  inputDescriptor.rawChannels = inputOptions.raw.channels;
122
+ inputDescriptor.rawPremultiplied = !!inputOptions.raw.premultiplied;
123
+
124
+ switch (input.constructor) {
125
+ case Uint8Array:
126
+ case Uint8ClampedArray:
127
+ inputDescriptor.rawDepth = 'uchar';
128
+ break;
129
+ case Int8Array:
130
+ inputDescriptor.rawDepth = 'char';
131
+ break;
132
+ case Uint16Array:
133
+ inputDescriptor.rawDepth = 'ushort';
134
+ break;
135
+ case Int16Array:
136
+ inputDescriptor.rawDepth = 'short';
137
+ break;
138
+ case Uint32Array:
139
+ inputDescriptor.rawDepth = 'uint';
140
+ break;
141
+ case Int32Array:
142
+ inputDescriptor.rawDepth = 'int';
143
+ break;
144
+ case Float32Array:
145
+ inputDescriptor.rawDepth = 'float';
146
+ break;
147
+ case Float64Array:
148
+ inputDescriptor.rawDepth = 'double';
149
+ break;
150
+ default:
151
+ inputDescriptor.rawDepth = 'uchar';
152
+ break;
153
+ }
100
154
  } else {
101
155
  throw new Error('Expected width, height and channels for raw pixel input');
102
156
  }
@@ -131,6 +185,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
131
185
  throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
132
186
  }
133
187
  }
188
+ // Sub Image File Directory (TIFF)
189
+ if (is.defined(inputOptions.subifd)) {
190
+ if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
191
+ inputDescriptor.subifd = inputOptions.subifd;
192
+ } else {
193
+ throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
194
+ }
195
+ }
134
196
  // Create new image
135
197
  if (is.defined(inputOptions.create)) {
136
198
  if (
@@ -236,16 +298,20 @@ function _isStreamInput () {
236
298
  }
237
299
 
238
300
  /**
239
- * Fast access to (uncached) image metadata without decoding any compressed image data.
301
+ * Fast access to (uncached) image metadata without decoding any compressed pixel data.
302
+ *
303
+ * This is taken from the header of the input image.
304
+ * It does not include operations, such as resize, to be applied to the output image.
305
+ *
240
306
  * A `Promise` is returned when `callback` is not provided.
241
307
  *
242
308
  * - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
243
309
  * - `size`: Total size of image in bytes, for Stream and Buffer input only
244
- * - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
245
- * - `height`: Number of pixels high (EXIF orientation is not taken into consideration)
246
- * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation)
310
+ * - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
311
+ * - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
312
+ * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
247
313
  * - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
248
- * - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat)
314
+ * - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
249
315
  * - `density`: Number of pixels per inch (DPI), if present
250
316
  * - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
251
317
  * - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
@@ -255,6 +321,10 @@ function _isStreamInput () {
255
321
  * - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
256
322
  * - `pagePrimary`: Number of the primary page in a HEIF image
257
323
  * - `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
324
+ * - `subifds`: Number of Sub Image File Directories in an OME-TIFF image
325
+ * - `background`: Default background colour, if present, for PNG (bKGD) and GIF images, either an RGB Object or a single greyscale value
326
+ * - `compression`: The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC)
327
+ * - `resolutionUnit`: The unit of resolution (density), either `inch` or `cm`, if present
258
328
  * - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
259
329
  * - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
260
330
  * - `orientation`: Number value of the EXIF Orientation header, if present
@@ -265,6 +335,9 @@ function _isStreamInput () {
265
335
  * - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
266
336
  *
267
337
  * @example
338
+ * const metadata = await sharp(input).metadata();
339
+ *
340
+ * @example
268
341
  * const image = sharp(inputJpg);
269
342
  * image
270
343
  * .metadata()
@@ -278,6 +351,17 @@ function _isStreamInput () {
278
351
  * // data contains a WebP image half the width and height of the original JPEG
279
352
  * });
280
353
  *
354
+ * @example
355
+ * // Based on EXIF rotation metadata, get the right-side-up width and height:
356
+ *
357
+ * const size = getNormalSize(await sharp(input).metadata());
358
+ *
359
+ * function getNormalSize({ width, height, orientation }) {
360
+ * return orientation || 0 >= 5
361
+ * ? { width: height, height: width }
362
+ * : { width, height };
363
+ * }
364
+ *
281
365
  * @param {Function} [callback] - called with the arguments `(err, metadata)`
282
366
  * @returns {Promise<Object>|Sharp}
283
367
  */
@@ -336,9 +420,12 @@ function metadata (callback) {
336
420
  * - `maxX` (x-coordinate of one of the pixel where the maximum lies)
337
421
  * - `maxY` (y-coordinate of one of the pixel where the maximum lies)
338
422
  * - `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
339
- * - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
340
- * - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
341
- * - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
423
+ * - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any.
424
+ * - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any.
425
+ * - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram.
426
+ *
427
+ * **Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
428
+ * written to a buffer in order to run `stats` on the result (see third example).
342
429
  *
343
430
  * @example
344
431
  * const image = sharp(inputJpg);
@@ -352,6 +439,13 @@ function metadata (callback) {
352
439
  * const { entropy, sharpness, dominant } = await sharp(input).stats();
353
440
  * const { r, g, b } = dominant;
354
441
  *
442
+ * @example
443
+ * const image = sharp(input);
444
+ * // store intermediate result
445
+ * const part = await image.extract(region).toBuffer();
446
+ * // create new instance to obtain statistics of extracted region
447
+ * const stats = await sharp(part).stats();
448
+ *
355
449
  * @param {Function} [callback] - called with the arguments `(err, stats)`
356
450
  * @returns {Promise<Object>}
357
451
  */
package/lib/is.js CHANGED
@@ -49,12 +49,26 @@ const buffer = function (val) {
49
49
  };
50
50
 
51
51
  /**
52
- * Is this value a Uint8Array or Uint8ClampedArray object?
52
+ * Is this value a typed array object?. E.g. Uint8Array or Uint8ClampedArray?
53
53
  * @private
54
54
  */
55
- const uint8Array = function (val) {
56
- // allow both since Uint8ClampedArray simply clamps the values between 0-255
57
- return val instanceof Uint8Array || val instanceof Uint8ClampedArray;
55
+ const typedArray = function (val) {
56
+ if (defined(val)) {
57
+ switch (val.constructor) {
58
+ case Uint8Array:
59
+ case Uint8ClampedArray:
60
+ case Int8Array:
61
+ case Uint16Array:
62
+ case Int16Array:
63
+ case Uint32Array:
64
+ case Int32Array:
65
+ case Float32Array:
66
+ case Float64Array:
67
+ return true;
68
+ }
69
+ }
70
+
71
+ return false;
58
72
  };
59
73
 
60
74
  /**
@@ -119,7 +133,7 @@ module.exports = {
119
133
  fn: fn,
120
134
  bool: bool,
121
135
  buffer: buffer,
122
- uint8Array: uint8Array,
136
+ typedArray: typedArray,
123
137
  string: string,
124
138
  number: number,
125
139
  integer: integer,