sharp 0.27.2 → 0.28.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/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,15 @@ 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
+ * See also {@link /api-channel#removealpha|removeAlpha}.
274
+ *
275
+ * @example
276
+ * await sharp(rgbaInput)
277
+ * .flatten({ background: '#F0A703' })
278
+ * .toBuffer();
279
+ *
273
280
  * @param {Object} [options]
274
281
  * @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
282
  * @returns {Sharp}
@@ -345,6 +352,47 @@ function normalize (normalize) {
345
352
  return this.normalise(normalize);
346
353
  }
347
354
 
355
+ /**
356
+ * Perform contrast limiting adaptive histogram equalization
357
+ * {@link https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE|CLAHE}.
358
+ *
359
+ * This will, in general, enhance the clarity of the image by bringing out darker details.
360
+ *
361
+ * @since 0.28.3
362
+ *
363
+ * @param {Object} options
364
+ * @param {number} options.width - integer width of the region in pixels.
365
+ * @param {number} options.height - integer height of the region in pixels.
366
+ * @param {number} [options.maxSlope=3] - maximum value for the slope of the
367
+ * cumulative histogram. A value of 0 disables contrast limiting. Valid values
368
+ * are integers in the range 0-100 (inclusive)
369
+ * @returns {Sharp}
370
+ * @throws {Error} Invalid parameters
371
+ */
372
+ function clahe (options) {
373
+ if (!is.plainObject(options)) {
374
+ throw is.invalidParameterError('options', 'plain object', options);
375
+ }
376
+ if (!('width' in options) || !is.integer(options.width) || options.width <= 0) {
377
+ throw is.invalidParameterError('width', 'integer above zero', options.width);
378
+ } else {
379
+ this.options.claheWidth = options.width;
380
+ }
381
+ if (!('height' in options) || !is.integer(options.height) || options.height <= 0) {
382
+ throw is.invalidParameterError('height', 'integer above zero', options.height);
383
+ } else {
384
+ this.options.claheHeight = options.height;
385
+ }
386
+ if (!is.defined(options.maxSlope)) {
387
+ this.options.claheMaxSlope = 3;
388
+ } else if (!is.integer(options.maxSlope) || options.maxSlope < 0 || options.maxSlope > 100) {
389
+ throw is.invalidParameterError('maxSlope', 'integer 0-100', options.maxSlope);
390
+ } else {
391
+ this.options.claheMaxSlope = options.maxSlope;
392
+ }
393
+ return this;
394
+ }
395
+
348
396
  /**
349
397
  * Convolve the image with the specified kernel.
350
398
  *
@@ -363,7 +411,7 @@ function normalize (normalize) {
363
411
  *
364
412
  * @param {Object} kernel
365
413
  * @param {number} kernel.width - width of the kernel in pixels.
366
- * @param {number} kernel.height - width of the kernel in pixels.
414
+ * @param {number} kernel.height - height of the kernel in pixels.
367
415
  * @param {Array<number>} kernel.kernel - Array of length `width*height` containing the kernel values.
368
416
  * @param {number} [kernel.scale=sum] - the scale of the kernel in pixels.
369
417
  * @param {number} [kernel.offset=0] - the offset of the kernel in pixels.
@@ -397,7 +445,7 @@ function convolve (kernel) {
397
445
  }
398
446
 
399
447
  /**
400
- * Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
448
+ * Any pixel value greater than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
401
449
  * @param {number} [threshold=128] - a value in the range 0-255 representing the level at which the threshold will be applied.
402
450
  * @param {Object} [options]
403
451
  * @param {Boolean} [options.greyscale=true] - convert to single channel greyscale.
@@ -589,6 +637,7 @@ module.exports = function (Sharp) {
589
637
  negate,
590
638
  normalise,
591
639
  normalize,
640
+ clahe,
592
641
  convolve,
593
642
  threshold,
594
643
  boolean,
package/lib/output.js CHANGED
@@ -105,10 +105,10 @@ function toFile (fileOut, callback) {
105
105
  * .catch(err => { ... });
106
106
  *
107
107
  * @example
108
- * const data = await sharp('my-image.jpg')
108
+ * const { data, info } = await sharp('my-image.jpg')
109
109
  * // output the raw pixels
110
110
  * .raw()
111
- * .toBuffer();
111
+ * .toBuffer({ resolveWithObject: true });
112
112
  *
113
113
  * // create a more type safe way to work with the raw pixel data
114
114
  * // this will not copy the data, instead it will change `data`s underlying ArrayBuffer
@@ -116,7 +116,9 @@ function toFile (fileOut, callback) {
116
116
  * const pixelArray = new Uint8ClampedArray(data.buffer);
117
117
  *
118
118
  * // When you are done changing the pixelArray, sharp takes the `pixelArray` as an input
119
- * await sharp(pixelArray).toFile('my-changed-image.jpg');
119
+ * const { width, height, channels } = info;
120
+ * await sharp(pixelArray, { raw: { width, height, channels } })
121
+ * .toFile('my-changed-image.jpg');
120
122
  *
121
123
  * @param {Object} [options]
122
124
  * @param {boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
@@ -146,9 +148,29 @@ function toBuffer (options, callback) {
146
148
  * .toFile('output-with-metadata.jpg')
147
149
  * .then(info => { ... });
148
150
  *
151
+ * @example
152
+ * // Set "IFD0-Copyright" in output EXIF metadata
153
+ * const data = await sharp(input)
154
+ * .withMetadata({
155
+ * exif: {
156
+ * IFD0: {
157
+ * Copyright: 'Wernham Hogg'
158
+ * }
159
+ * }
160
+ * })
161
+ * .toBuffer();
162
+ *
163
+ * * @example
164
+ * // Set output metadata to 96 DPI
165
+ * const data = await sharp(input)
166
+ * .withMetadata({ density: 96 })
167
+ * .toBuffer();
168
+ *
149
169
  * @param {Object} [options]
150
170
  * @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
151
171
  * @param {string} [options.icc] filesystem path to output ICC profile, defaults to sRGB.
172
+ * @param {Object<Object>} [options.exif={}] Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
173
+ * @param {number} [options.density] Number of pixels per inch (DPI).
152
174
  * @returns {Sharp}
153
175
  * @throws {Error} Invalid parameters
154
176
  */
@@ -162,6 +184,13 @@ function withMetadata (options) {
162
184
  throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
163
185
  }
164
186
  }
187
+ if (is.defined(options.density)) {
188
+ if (is.number(options.density) && options.density > 0) {
189
+ this.options.withMetadataDensity = options.density;
190
+ } else {
191
+ throw is.invalidParameterError('density', 'positive number', options.density);
192
+ }
193
+ }
165
194
  if (is.defined(options.icc)) {
166
195
  if (is.string(options.icc)) {
167
196
  this.options.withMetadataIcc = options.icc;
@@ -169,6 +198,25 @@ function withMetadata (options) {
169
198
  throw is.invalidParameterError('icc', 'string filesystem path to ICC profile', options.icc);
170
199
  }
171
200
  }
201
+ if (is.defined(options.exif)) {
202
+ if (is.object(options.exif)) {
203
+ for (const [ifd, entries] of Object.entries(options.exif)) {
204
+ if (is.object(entries)) {
205
+ for (const [k, v] of Object.entries(entries)) {
206
+ if (is.string(v)) {
207
+ this.options.withMetadataStrs[`exif-${ifd.toLowerCase()}-${k}`] = v;
208
+ } else {
209
+ throw is.invalidParameterError(`exif.${ifd}.${k}`, 'string', v);
210
+ }
211
+ }
212
+ } else {
213
+ throw is.invalidParameterError(`exif.${ifd}`, 'object', entries);
214
+ }
215
+ }
216
+ } else {
217
+ throw is.invalidParameterError('exif', 'object', options.exif);
218
+ }
219
+ }
172
220
  }
173
221
  return this;
174
222
  }
@@ -198,8 +246,6 @@ function toFormat (format, options) {
198
246
  /**
199
247
  * Use these JPEG options for output image.
200
248
  *
201
- * Some of these options require the use of a globally-installed libvips compiled with support for mozjpeg.
202
- *
203
249
  * @example
204
250
  * // Convert any input to very high quality JPEG output
205
251
  * const data = await sharp(input)
@@ -209,18 +255,25 @@ function toFormat (format, options) {
209
255
  * })
210
256
  * .toBuffer();
211
257
  *
258
+ * @example
259
+ * // Use mozjpeg to reduce output JPEG file size (slower)
260
+ * const data = await sharp(input)
261
+ * .jpeg({ mozjpeg: true })
262
+ * .toBuffer();
263
+ *
212
264
  * @param {Object} [options] - output options
213
265
  * @param {number} [options.quality=80] - quality, integer 1-100
214
266
  * @param {boolean} [options.progressive=false] - use progressive (interlace) scan
215
267
  * @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling
216
268
  * @param {boolean} [options.optimiseCoding=true] - optimise Huffman coding tables
217
269
  * @param {boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
218
- * @param {boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires libvips compiled with support for mozjpeg
219
- * @param {boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires libvips compiled with support for mozjpeg
220
- * @param {boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg
221
- * @param {boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans, requires libvips compiled with support for mozjpeg
222
- * @param {number} [options.quantisationTable=0] - quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg
223
- * @param {number} [options.quantizationTable=0] - alternative spelling of quantisationTable, requires libvips compiled with support for mozjpeg
270
+ * @param {boolean} [options.mozjpeg=false] - use mozjpeg defaults, equivalent to `{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }`
271
+ * @param {boolean} [options.trellisQuantisation=false] - apply trellis quantisation
272
+ * @param {boolean} [options.overshootDeringing=false] - apply overshoot deringing
273
+ * @param {boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive
274
+ * @param {boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans
275
+ * @param {number} [options.quantisationTable=0] - quantization table to use, integer 0-8
276
+ * @param {number} [options.quantizationTable=0] - alternative spelling of quantisationTable
224
277
  * @param {boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format
225
278
  * @returns {Sharp}
226
279
  * @throws {Error} Invalid options
@@ -244,6 +297,23 @@ function jpeg (options) {
244
297
  throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
245
298
  }
246
299
  }
300
+ const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
301
+ if (is.defined(optimiseCoding)) {
302
+ this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
303
+ }
304
+ if (is.defined(options.mozjpeg)) {
305
+ if (is.bool(options.mozjpeg)) {
306
+ if (options.mozjpeg) {
307
+ this.options.jpegTrellisQuantisation = true;
308
+ this.options.jpegOvershootDeringing = true;
309
+ this.options.jpegOptimiseScans = true;
310
+ this.options.jpegProgressive = true;
311
+ this.options.jpegQuantisationTable = 3;
312
+ }
313
+ } else {
314
+ throw is.invalidParameterError('mozjpeg', 'boolean', options.mozjpeg);
315
+ }
316
+ }
247
317
  const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
248
318
  if (is.defined(trellisQuantisation)) {
249
319
  this._setBooleanOption('jpegTrellisQuantisation', trellisQuantisation);
@@ -258,10 +328,6 @@ function jpeg (options) {
258
328
  this.options.jpegProgressive = true;
259
329
  }
260
330
  }
261
- const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
262
- if (is.defined(optimiseCoding)) {
263
- this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
264
- }
265
331
  const quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
266
332
  if (is.defined(quantisationTable)) {
267
333
  if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
@@ -277,26 +343,31 @@ function jpeg (options) {
277
343
  /**
278
344
  * Use these PNG options for output image.
279
345
  *
280
- * PNG output is always full colour at 8 or 16 bits per pixel.
346
+ * By default, PNG output is full colour at 8 or 16 bits per pixel.
281
347
  * Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
282
- *
283
- * Some of these options require the use of a globally-installed libvips compiled with support for libimagequant (GPL).
348
+ * Set `palette` to `true` for slower, indexed PNG output.
284
349
  *
285
350
  * @example
286
- * // Convert any input to PNG output
351
+ * // Convert any input to full colour PNG output
287
352
  * const data = await sharp(input)
288
353
  * .png()
289
354
  * .toBuffer();
290
355
  *
356
+ * @example
357
+ * // Convert any input to indexed PNG output (slower)
358
+ * const data = await sharp(input)
359
+ * .png({ palette: true })
360
+ * .toBuffer();
361
+ *
291
362
  * @param {Object} [options]
292
363
  * @param {boolean} [options.progressive=false] - use progressive (interlace) scan
293
- * @param {number} [options.compressionLevel=9] - zlib compression level, 0-9
364
+ * @param {number} [options.compressionLevel=6] - zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest)
294
365
  * @param {boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
295
- * @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libvips compiled with support for libimagequant
296
- * @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
297
- * @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true`, requires libvips compiled with support for libimagequant
298
- * @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true`, requires libvips compiled with support for libimagequant
299
- * @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true`, requires libvips compiled with support for libimagequant
366
+ * @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support
367
+ * @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, sets `palette` to `true`
368
+ * @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true`
369
+ * @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true`
370
+ * @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true`
300
371
  * @param {boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
301
372
  * @returns {Sharp}
302
373
  * @throws {Error} Invalid options
@@ -358,6 +429,12 @@ function png (options) {
358
429
  * .webp({ lossless: true })
359
430
  * .toBuffer();
360
431
  *
432
+ * @example
433
+ * // Optimise the file size of an animated WebP
434
+ * const outputWebp = await sharp(inputWebp, { animated: true })
435
+ * .webp({ reductionEffort: 6 })
436
+ * .toBuffer();
437
+ *
361
438
  * @param {Object} [options] - output options
362
439
  * @param {number} [options.quality=80] - quality, integer 1-100
363
440
  * @param {number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
@@ -704,6 +781,7 @@ function raw () {
704
781
  * @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`.
705
782
  * @param {boolean} [options.centre=false] centre image in tile.
706
783
  * @param {boolean} [options.center=false] alternative spelling of centre.
784
+ * @param {string} [options.id='https://example.com/iiif'] when `layout` is `iiif`, sets the `@id` attribute of `info.json`
707
785
  * @returns {Sharp}
708
786
  * @throws {Error} Invalid parameters
709
787
  */
@@ -777,6 +855,14 @@ function tile (options) {
777
855
  if (is.defined(centre)) {
778
856
  this._setBooleanOption('tileCentre', centre);
779
857
  }
858
+ // @id attribute for IIIF layout
859
+ if (is.defined(options.id)) {
860
+ if (is.string(options.id)) {
861
+ this.options.tileId = options.id;
862
+ } else {
863
+ throw is.invalidParameterError('id', 'string', options.id);
864
+ }
865
+ }
780
866
  }
781
867
  // Format
782
868
  if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
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:
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, AVIF and TIFF images",
4
- "version": "0.27.2",
4
+ "version": "0.28.3",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -74,13 +74,17 @@
74
74
  "Christian Flintrup <chr@gigahost.dk>",
75
75
  "Manan Jadhav <manan@motionden.com>",
76
76
  "Leon Radley <leon@radley.se>",
77
- "alza54 <alza54@thiocod.in>"
77
+ "alza54 <alza54@thiocod.in>",
78
+ "Jacob Smith <jacob@frende.me>",
79
+ "Michael Nutt <michael@nutt.im>",
80
+ "Brad Parham <baparham@gmail.com>"
78
81
  ],
79
82
  "scripts": {
80
- "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
83
+ "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
81
84
  "clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
82
- "test": "semistandard && cpplint && npm run test-unit && npm run test-licensing",
83
- "test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
85
+ "test": "npm run test-lint && npm run test-unit && npm run test-licensing",
86
+ "test-lint": "semistandard && cpplint",
87
+ "test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=1000 --timeout=60000 ./test/unit/*.js",
84
88
  "test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
85
89
  "test-coverage": "./test/coverage/report.sh",
86
90
  "test-leak": "./test/leak/leak.sh",
@@ -117,14 +121,12 @@
117
121
  "vips"
118
122
  ],
119
123
  "dependencies": {
120
- "array-flatten": "^3.0.0",
121
124
  "color": "^3.1.3",
122
125
  "detect-libc": "^1.0.3",
123
- "node-addon-api": "^3.1.0",
124
- "npmlog": "^4.1.2",
125
- "prebuild-install": "^6.0.1",
126
- "semver": "^7.3.4",
127
- "simple-get": "^4.0.0",
126
+ "node-addon-api": "^3.2.0",
127
+ "prebuild-install": "^6.1.2",
128
+ "semver": "^7.3.5",
129
+ "simple-get": "^3.1.0",
128
130
  "tar-fs": "^2.1.1",
129
131
  "tunnel-agent": "^0.6.0"
130
132
  },
@@ -132,12 +134,12 @@
132
134
  "async": "^3.2.0",
133
135
  "cc": "^3.0.1",
134
136
  "decompress-zip": "^0.3.3",
135
- "documentation": "^13.1.1",
137
+ "documentation": "^13.2.5",
136
138
  "exif-reader": "^1.0.3",
137
139
  "icc": "^2.0.0",
138
140
  "license-checker": "^25.0.1",
139
- "mocha": "^8.3.0",
140
- "mock-fs": "^4.13.0",
141
+ "mocha": "^8.4.0",
142
+ "mock-fs": "^4.14.0",
141
143
  "nyc": "^15.1.0",
142
144
  "prebuild": "^10.0.1",
143
145
  "rimraf": "^3.0.2",
@@ -145,7 +147,7 @@
145
147
  },
146
148
  "license": "Apache-2.0",
147
149
  "config": {
148
- "libvips": "8.10.5",
150
+ "libvips": "8.10.6",
149
151
  "runtime": "napi",
150
152
  "target": 3
151
153
  },
package/src/common.cc CHANGED
@@ -36,6 +36,9 @@ namespace sharp {
36
36
  std::string AttrAsStr(Napi::Object obj, std::string attr) {
37
37
  return obj.Get(attr).As<Napi::String>();
38
38
  }
39
+ std::string AttrAsStr(Napi::Object obj, unsigned int const attr) {
40
+ return obj.Get(attr).As<Napi::String>();
41
+ }
39
42
  uint32_t AttrAsUint32(Napi::Object obj, std::string attr) {
40
43
  return obj.Get(attr).As<Napi::Number>().Uint32Value();
41
44
  }
@@ -92,6 +95,7 @@ namespace sharp {
92
95
  descriptor->rawChannels = AttrAsUint32(input, "rawChannels");
93
96
  descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
94
97
  descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
98
+ descriptor->rawPremultiplied = AttrAsBool(input, "rawPremultiplied");
95
99
  }
96
100
  // Multi-page input (GIF, TIFF, PDF)
97
101
  if (HasAttr(input, "pages")) {
@@ -104,6 +108,10 @@ namespace sharp {
104
108
  if (HasAttr(input, "level")) {
105
109
  descriptor->level = AttrAsUint32(input, "level");
106
110
  }
111
+ // subIFD (OME-TIFF)
112
+ if (HasAttr(input, "subifd")) {
113
+ descriptor->subifd = AttrAsInt32(input, "subifd");
114
+ }
107
115
  // Create new image
108
116
  if (HasAttr(input, "createChannels")) {
109
117
  descriptor->createChannels = AttrAsUint32(input, "createChannels");
@@ -213,6 +221,8 @@ namespace sharp {
213
221
  { "VipsForeignLoadTiffBuffer", ImageType::TIFF },
214
222
  { "VipsForeignLoadGifFile", ImageType::GIF },
215
223
  { "VipsForeignLoadGifBuffer", ImageType::GIF },
224
+ { "VipsForeignLoadNsgifFile", ImageType::GIF },
225
+ { "VipsForeignLoadNsgifBuffer", ImageType::GIF },
216
226
  { "VipsForeignLoadSvgFile", ImageType::SVG },
217
227
  { "VipsForeignLoadSvgBuffer", ImageType::SVG },
218
228
  { "VipsForeignLoadHeifFile", ImageType::HEIF },
@@ -226,6 +236,7 @@ namespace sharp {
226
236
  { "VipsForeignLoadFits", ImageType::FITS },
227
237
  { "VipsForeignLoadOpenexr", ImageType::EXR },
228
238
  { "VipsForeignLoadVips", ImageType::VIPS },
239
+ { "VipsForeignLoadVipsFile", ImageType::VIPS },
229
240
  { "VipsForeignLoadRaw", ImageType::RAW }
230
241
  };
231
242
 
@@ -292,6 +303,9 @@ namespace sharp {
292
303
  } else {
293
304
  image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
294
305
  }
306
+ if (descriptor->rawPremultiplied) {
307
+ image = image.unpremultiply();
308
+ }
295
309
  imageType = ImageType::RAW;
296
310
  } else {
297
311
  // Compressed data
@@ -317,6 +331,9 @@ namespace sharp {
317
331
  if (imageType == ImageType::OPENSLIDE) {
318
332
  option->set("level", descriptor->level);
319
333
  }
334
+ if (imageType == ImageType::TIFF) {
335
+ option->set("subifd", descriptor->subifd);
336
+ }
320
337
  image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
321
338
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
322
339
  image = SetDensity(image, descriptor->density);
@@ -389,6 +406,9 @@ namespace sharp {
389
406
  if (imageType == ImageType::OPENSLIDE) {
390
407
  option->set("level", descriptor->level);
391
408
  }
409
+ if (imageType == ImageType::TIFF) {
410
+ option->set("subifd", descriptor->subifd);
411
+ }
392
412
  image = VImage::new_from_file(descriptor->file.data(), option);
393
413
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
394
414
  image = SetDensity(image, descriptor->density);
@@ -505,9 +525,8 @@ namespace sharp {
505
525
  VImage SetDensity(VImage image, const double density) {
506
526
  const double pixelsPerMm = density / 25.4;
507
527
  VImage copy = image.copy();
508
- copy.set("Xres", pixelsPerMm);
509
- copy.set("Yres", pixelsPerMm);
510
- copy.set(VIPS_META_RESOLUTION_UNIT, "in");
528
+ copy.get_image()->Xres = pixelsPerMm;
529
+ copy.get_image()->Yres = pixelsPerMm;
511
530
  return copy;
512
531
  }
513
532
 
@@ -797,10 +816,10 @@ namespace sharp {
797
816
  /*
798
817
  Ensures alpha channel, if missing.
799
818
  */
800
- VImage EnsureAlpha(VImage image) {
819
+ VImage EnsureAlpha(VImage image, double const value) {
801
820
  if (!HasAlpha(image)) {
802
821
  std::vector<double> alpha;
803
- alpha.push_back(sharp::MaximumImageAlpha(image.interpretation()));
822
+ alpha.push_back(value * sharp::MaximumImageAlpha(image.interpretation()));
804
823
  image = image.bandjoin_const(alpha);
805
824
  }
806
825
  return image;