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/README.md +14 -13
- package/binding.gyp +3 -5
- package/install/can-compile.js +11 -0
- package/install/dll-copy.js +4 -4
- package/install/libvips.js +78 -40
- package/lib/agent.js +1 -1
- package/lib/channel.js +27 -7
- package/lib/composite.js +2 -0
- package/lib/constructor.js +12 -5
- package/lib/input.js +19 -3
- package/lib/libvips.js +14 -3
- package/lib/operation.js +54 -5
- package/lib/output.js +111 -25
- package/lib/resize.js +42 -15
- package/lib/utility.js +13 -2
- package/package.json +17 -15
- package/src/common.cc +24 -5
- package/src/common.h +8 -3
- package/src/metadata.cc +6 -0
- package/src/metadata.h +2 -0
- package/src/operations.cc +7 -0
- package/src/operations.h +5 -0
- package/src/pipeline.cc +51 -9
- package/src/pipeline.h +14 -3
- package/src/sharp.cc +1 -0
- package/src/utilities.cc +16 -0
- package/src/utilities.h +1 -0
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 =
|
|
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 -
|
|
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
|
|
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
|
-
*
|
|
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.
|
|
219
|
-
* @param {boolean} [options.
|
|
220
|
-
* @param {boolean} [options.
|
|
221
|
-
* @param {boolean} [options.
|
|
222
|
-
* @param {
|
|
223
|
-
* @param {number} [options.
|
|
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
|
|
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=
|
|
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
|
|
296
|
-
* @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, sets `palette` to `true
|
|
297
|
-
* @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true
|
|
298
|
-
* @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true
|
|
299
|
-
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true
|
|
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.
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
*
|
|
88
|
-
*
|
|
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.
|
|
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": "
|
|
83
|
-
"test-
|
|
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.
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
"
|
|
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.
|
|
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.
|
|
140
|
-
"mock-fs": "^4.
|
|
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.
|
|
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.
|
|
509
|
-
copy.
|
|
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;
|