sharp 0.25.4 → 0.26.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/binding.gyp +54 -67
- package/install/dll-copy.js +3 -1
- package/install/libvips.js +28 -13
- package/lib/constructor.js +16 -2
- package/lib/input.js +15 -6
- package/lib/libvips.js +1 -7
- package/lib/operation.js +99 -0
- package/lib/output.js +96 -7
- package/lib/platform.js +2 -1
- package/lib/resize.js +2 -1
- package/lib/utility.js +22 -1
- package/package.json +22 -16
- package/src/common.cc +122 -57
- package/src/common.h +23 -3
- package/src/libvips/cplusplus/VImage.cpp +451 -450
- package/src/libvips/cplusplus/vips-operators.cpp +117 -1
- package/src/metadata.cc +1 -1
- package/src/operations.cc +0 -23
- package/src/operations.h +0 -10
- package/src/pipeline.cc +100 -16
- package/src/pipeline.h +24 -2
- package/src/stats.cc +25 -6
- package/src/stats.h +7 -1
package/lib/output.js
CHANGED
|
@@ -11,7 +11,8 @@ const formats = new Map([
|
|
|
11
11
|
['png', 'png'],
|
|
12
12
|
['raw', 'raw'],
|
|
13
13
|
['tiff', 'tiff'],
|
|
14
|
-
['webp', 'webp']
|
|
14
|
+
['webp', 'webp'],
|
|
15
|
+
['gif', 'gif']
|
|
15
16
|
]);
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -118,7 +119,8 @@ function toBuffer (options, callback) {
|
|
|
118
119
|
|
|
119
120
|
/**
|
|
120
121
|
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
|
121
|
-
* This will also convert to and add a web-friendly sRGB ICC profile
|
|
122
|
+
* This will also convert to and add a web-friendly sRGB ICC profile unless a custom
|
|
123
|
+
* output profile is provided.
|
|
122
124
|
*
|
|
123
125
|
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
|
124
126
|
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
|
@@ -131,6 +133,7 @@ function toBuffer (options, callback) {
|
|
|
131
133
|
*
|
|
132
134
|
* @param {Object} [options]
|
|
133
135
|
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
|
|
136
|
+
* @param {string} [options.icc] filesystem path to output ICC profile, defaults to sRGB.
|
|
134
137
|
* @returns {Sharp}
|
|
135
138
|
* @throws {Error} Invalid parameters
|
|
136
139
|
*/
|
|
@@ -144,6 +147,13 @@ function withMetadata (options) {
|
|
|
144
147
|
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
|
|
145
148
|
}
|
|
146
149
|
}
|
|
150
|
+
if (is.defined(options.icc)) {
|
|
151
|
+
if (is.string(options.icc)) {
|
|
152
|
+
this.options.withMetadataIcc = options.icc;
|
|
153
|
+
} else {
|
|
154
|
+
throw is.invalidParameterError('icc', 'string filesystem path to ICC profile', options.icc);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
147
157
|
}
|
|
148
158
|
return this;
|
|
149
159
|
}
|
|
@@ -187,7 +197,7 @@ function toFormat (format, options) {
|
|
|
187
197
|
* @param {Object} [options] - output options
|
|
188
198
|
* @param {number} [options.quality=80] - quality, integer 1-100
|
|
189
199
|
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
|
|
190
|
-
* @param {string} [options.chromaSubsampling='4:2:0'] -
|
|
200
|
+
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling
|
|
191
201
|
* @param {boolean} [options.optimiseCoding=true] - optimise Huffman coding tables
|
|
192
202
|
* @param {boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
|
|
193
203
|
* @param {boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires libvips compiled with support for mozjpeg
|
|
@@ -340,6 +350,9 @@ function png (options) {
|
|
|
340
350
|
* @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
|
|
341
351
|
* @param {boolean} [options.smartSubsample=false] - use high quality chroma subsampling
|
|
342
352
|
* @param {number} [options.reductionEffort=4] - level of CPU effort to reduce file size, integer 0-6
|
|
353
|
+
* @param {number} [options.pageHeight] - page height for animated output
|
|
354
|
+
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
|
355
|
+
* @param {number[]} [options.delay] - list of delays between animation frames (in milliseconds)
|
|
343
356
|
* @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
|
|
344
357
|
* @returns {Sharp}
|
|
345
358
|
* @throws {Error} Invalid options
|
|
@@ -375,9 +388,73 @@ function webp (options) {
|
|
|
375
388
|
throw is.invalidParameterError('reductionEffort', 'integer between 0 and 6', options.reductionEffort);
|
|
376
389
|
}
|
|
377
390
|
}
|
|
391
|
+
|
|
392
|
+
trySetAnimationOptions(options, this.options);
|
|
378
393
|
return this._updateFormatOut('webp', options);
|
|
379
394
|
}
|
|
380
395
|
|
|
396
|
+
/**
|
|
397
|
+
* Use these GIF options for output image.
|
|
398
|
+
*
|
|
399
|
+
* Requires libvips compiled with support for ImageMagick or GraphicsMagick.
|
|
400
|
+
* The prebuilt binaries do not include this - see
|
|
401
|
+
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
|
|
402
|
+
*
|
|
403
|
+
* @param {Object} [options] - output options
|
|
404
|
+
* @param {number} [options.pageHeight] - page height for animated output
|
|
405
|
+
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
|
406
|
+
* @param {number[]} [options.delay] - list of delays between animation frames (in milliseconds)
|
|
407
|
+
* @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
|
|
408
|
+
* @returns {Sharp}
|
|
409
|
+
* @throws {Error} Invalid options
|
|
410
|
+
*/
|
|
411
|
+
/* istanbul ignore next */
|
|
412
|
+
function gif (options) {
|
|
413
|
+
if (!this.constructor.format.magick.output.buffer) {
|
|
414
|
+
throw new Error('The gif operation requires libvips to have been installed with support for ImageMagick');
|
|
415
|
+
}
|
|
416
|
+
trySetAnimationOptions(options, this.options);
|
|
417
|
+
return this._updateFormatOut('gif', options);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Set animation options if available.
|
|
422
|
+
* @private
|
|
423
|
+
*
|
|
424
|
+
* @param {Object} [source] - output options
|
|
425
|
+
* @param {number} [source.pageHeight] - page height for animated output
|
|
426
|
+
* @param {number} [source.loop=0] - number of animation iterations, use 0 for infinite animation
|
|
427
|
+
* @param {number[]} [source.delay] - list of delays between animation frames (in milliseconds)
|
|
428
|
+
* @param {Object} [target] - target object for valid options
|
|
429
|
+
* @throws {Error} Invalid options
|
|
430
|
+
*/
|
|
431
|
+
function trySetAnimationOptions (source, target) {
|
|
432
|
+
if (is.object(source) && is.defined(source.pageHeight)) {
|
|
433
|
+
if (is.integer(source.pageHeight) && source.pageHeight > 0) {
|
|
434
|
+
target.pageHeight = source.pageHeight;
|
|
435
|
+
} else {
|
|
436
|
+
throw is.invalidParameterError('pageHeight', 'integer larger than 0', source.pageHeight);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (is.object(source) && is.defined(source.loop)) {
|
|
440
|
+
if (is.integer(source.loop) && is.inRange(source.loop, 0, 65535)) {
|
|
441
|
+
target.loop = source.loop;
|
|
442
|
+
} else {
|
|
443
|
+
throw is.invalidParameterError('loop', 'integer between 0 and 65535', source.loop);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (is.object(source) && is.defined(source.delay)) {
|
|
447
|
+
if (
|
|
448
|
+
Array.isArray(source.delay) &&
|
|
449
|
+
source.delay.every(is.integer) &&
|
|
450
|
+
source.delay.every(v => is.inRange(v, 0, 65535))) {
|
|
451
|
+
target.delay = source.delay;
|
|
452
|
+
} else {
|
|
453
|
+
throw is.invalidParameterError('delay', 'array of integers between 0 and 65535', source.delay);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
381
458
|
/**
|
|
382
459
|
* Use these TIFF options for output image.
|
|
383
460
|
*
|
|
@@ -386,7 +463,7 @@ function webp (options) {
|
|
|
386
463
|
* sharp('input.svg')
|
|
387
464
|
* .tiff({
|
|
388
465
|
* compression: 'lzw',
|
|
389
|
-
*
|
|
466
|
+
* bitdepth: 1
|
|
390
467
|
* })
|
|
391
468
|
* .toFile('1-bpp-output.tiff')
|
|
392
469
|
* .then(info => { ... });
|
|
@@ -402,7 +479,7 @@ function webp (options) {
|
|
|
402
479
|
* @param {boolean} [options.tileHeight=256] - vertical tile size
|
|
403
480
|
* @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
|
|
404
481
|
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
|
|
405
|
-
* @param {boolean} [options.
|
|
482
|
+
* @param {boolean} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
|
|
406
483
|
* @returns {Sharp}
|
|
407
484
|
* @throws {Error} Invalid options
|
|
408
485
|
*/
|
|
@@ -415,8 +492,12 @@ function tiff (options) {
|
|
|
415
492
|
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
|
416
493
|
}
|
|
417
494
|
}
|
|
418
|
-
if (is.defined(options.
|
|
419
|
-
|
|
495
|
+
if (is.defined(options.bitdepth)) {
|
|
496
|
+
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [1, 2, 4, 8])) {
|
|
497
|
+
this.options.tiffBitdepth = options.bitdepth;
|
|
498
|
+
} else {
|
|
499
|
+
throw is.invalidParameterError('bitdepth', '1, 2, 4 or 8', options.bitdepth);
|
|
500
|
+
}
|
|
420
501
|
}
|
|
421
502
|
// tiling
|
|
422
503
|
if (is.defined(options.tile)) {
|
|
@@ -577,6 +658,8 @@ function raw () {
|
|
|
577
658
|
* @param {number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images
|
|
578
659
|
* @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
|
|
579
660
|
* @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`.
|
|
661
|
+
* @param {boolean} [options.centre=false] centre image in tile.
|
|
662
|
+
* @param {boolean} [options.center=false] alternative spelling of centre.
|
|
580
663
|
* @returns {Sharp}
|
|
581
664
|
* @throws {Error} Invalid parameters
|
|
582
665
|
*/
|
|
@@ -645,6 +728,11 @@ function tile (options) {
|
|
|
645
728
|
} else if (is.defined(options.layout) && options.layout === 'google') {
|
|
646
729
|
this.options.tileSkipBlanks = 5;
|
|
647
730
|
}
|
|
731
|
+
// Center image in tile
|
|
732
|
+
const centre = is.bool(options.center) ? options.center : options.centre;
|
|
733
|
+
if (is.defined(centre)) {
|
|
734
|
+
this._setBooleanOption('tileCentre', centre);
|
|
735
|
+
}
|
|
648
736
|
}
|
|
649
737
|
// Format
|
|
650
738
|
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
|
|
@@ -804,6 +892,7 @@ module.exports = function (Sharp) {
|
|
|
804
892
|
webp,
|
|
805
893
|
tiff,
|
|
806
894
|
heif,
|
|
895
|
+
gif,
|
|
807
896
|
raw,
|
|
808
897
|
tile,
|
|
809
898
|
// Private
|
package/lib/platform.js
CHANGED
|
@@ -13,7 +13,8 @@ module.exports = function () {
|
|
|
13
13
|
const platformId = [`${platform}${libc}`];
|
|
14
14
|
|
|
15
15
|
if (arch === 'arm') {
|
|
16
|
-
|
|
16
|
+
const fallback = process.versions.electron ? '7' : '6';
|
|
17
|
+
platformId.push(`armv${env.npm_config_arm_version || process.config.variables.arm_version || fallback}`);
|
|
17
18
|
} else if (arch === 'arm64') {
|
|
18
19
|
platformId.push(`arm64v${env.npm_config_arm_version || '8'}`);
|
|
19
20
|
} else {
|
package/lib/resize.js
CHANGED
|
@@ -386,7 +386,8 @@ function extract (options) {
|
|
|
386
386
|
* Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
|
|
387
387
|
* Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
|
|
388
388
|
*
|
|
389
|
-
* The `info` response Object
|
|
389
|
+
* The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
|
|
390
|
+
* will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
|
390
391
|
*
|
|
391
392
|
* @param {number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero.
|
|
392
393
|
* @returns {Sharp}
|
package/lib/utility.js
CHANGED
|
@@ -13,6 +13,26 @@ const sharp = require('../build/Release/sharp.node');
|
|
|
13
13
|
*/
|
|
14
14
|
const format = sharp.format();
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* An Object containing the available interpolators and their proper values
|
|
18
|
+
* @readonly
|
|
19
|
+
* @enum {string}
|
|
20
|
+
*/
|
|
21
|
+
const interpolators = {
|
|
22
|
+
/** [Nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation). Suitable for image enlargement only. */
|
|
23
|
+
nearest: 'nearest',
|
|
24
|
+
/** [Bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation). Faster than bicubic but with less smooth results. */
|
|
25
|
+
bilinear: 'bilinear',
|
|
26
|
+
/** [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). */
|
|
27
|
+
bicubic: 'bicubic',
|
|
28
|
+
/** [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100). Prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2. */
|
|
29
|
+
locallyBoundedBicubic: 'lbb',
|
|
30
|
+
/** [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. */
|
|
31
|
+
nohalo: 'nohalo',
|
|
32
|
+
/** [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */
|
|
33
|
+
vertexSplitQuadraticBasisSpline: 'vsqbs'
|
|
34
|
+
};
|
|
35
|
+
|
|
16
36
|
/**
|
|
17
37
|
* An Object containing the version numbers of libvips and its dependencies.
|
|
18
38
|
* @member
|
|
@@ -23,7 +43,7 @@ let versions = {
|
|
|
23
43
|
vips: sharp.libvipsVersion()
|
|
24
44
|
};
|
|
25
45
|
try {
|
|
26
|
-
versions = require(
|
|
46
|
+
versions = require(`../vendor/${versions.vips}/versions.json`);
|
|
27
47
|
} catch (err) {}
|
|
28
48
|
|
|
29
49
|
/**
|
|
@@ -146,6 +166,7 @@ module.exports = function (Sharp) {
|
|
|
146
166
|
Sharp[f.name] = f;
|
|
147
167
|
});
|
|
148
168
|
Sharp.format = format;
|
|
169
|
+
Sharp.interpolators = interpolators;
|
|
149
170
|
Sharp.versions = versions;
|
|
150
171
|
Sharp.queue = queue;
|
|
151
172
|
};
|
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 and TIFF images",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.26.3",
|
|
5
5
|
"author": "Lovell Fuller <npm@lovell.info>",
|
|
6
6
|
"homepage": "https://github.com/lovell/sharp",
|
|
7
7
|
"contributors": [
|
|
@@ -67,12 +67,15 @@
|
|
|
67
67
|
"Brendan Kennedy <brenwken@gmail.com>",
|
|
68
68
|
"Brychan Bennett-Odlum <git@brychan.io>",
|
|
69
69
|
"Edward Silverton <e.silverton@gmail.com>",
|
|
70
|
-
"Roman Malieiev <aromaleev@gmail.com>"
|
|
70
|
+
"Roman Malieiev <aromaleev@gmail.com>",
|
|
71
|
+
"Tomas Szabo <tomas.szabo@deftomat.com>",
|
|
72
|
+
"Robert O'Rourke <robert@o-rourke.org>",
|
|
73
|
+
"Guillermo Alfonso Varela Chouciño <guillevch@gmail.com>"
|
|
71
74
|
],
|
|
72
75
|
"scripts": {
|
|
73
|
-
"install": "(node install/libvips && node install/dll-copy && prebuild-install
|
|
76
|
+
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
|
74
77
|
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
|
|
75
|
-
"test": "semistandard && cpplint && npm run test-unit && npm run test-licensing && prebuild-ci",
|
|
78
|
+
"test": "semistandard && cpplint && npm run test-unit && npm run test-licensing && node install/prebuild-ci",
|
|
76
79
|
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
|
|
77
80
|
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
|
|
78
81
|
"test-coverage": "./test/coverage/report.sh",
|
|
@@ -85,6 +88,7 @@
|
|
|
85
88
|
"files": [
|
|
86
89
|
"binding.gyp",
|
|
87
90
|
"install/**",
|
|
91
|
+
"!install/prebuild-ci.js",
|
|
88
92
|
"lib/**",
|
|
89
93
|
"src/**"
|
|
90
94
|
],
|
|
@@ -109,35 +113,37 @@
|
|
|
109
113
|
"vips"
|
|
110
114
|
],
|
|
111
115
|
"dependencies": {
|
|
112
|
-
"
|
|
116
|
+
"array-flatten": "^3.0.0",
|
|
117
|
+
"color": "^3.1.3",
|
|
113
118
|
"detect-libc": "^1.0.3",
|
|
114
|
-
"node-addon-api": "^3.0.
|
|
119
|
+
"node-addon-api": "^3.0.2",
|
|
115
120
|
"npmlog": "^4.1.2",
|
|
116
|
-
"prebuild-install": "^
|
|
121
|
+
"prebuild-install": "^6.0.0",
|
|
117
122
|
"semver": "^7.3.2",
|
|
118
123
|
"simple-get": "^4.0.0",
|
|
119
|
-
"tar": "^
|
|
124
|
+
"tar-fs": "^2.1.1",
|
|
120
125
|
"tunnel-agent": "^0.6.0"
|
|
121
126
|
},
|
|
122
127
|
"devDependencies": {
|
|
123
128
|
"async": "^3.2.0",
|
|
124
129
|
"cc": "^2.0.1",
|
|
125
130
|
"decompress-zip": "^0.3.2",
|
|
126
|
-
"documentation": "^13.0
|
|
131
|
+
"documentation": "^13.1.0",
|
|
127
132
|
"exif-reader": "^1.0.3",
|
|
128
|
-
"icc": "^
|
|
133
|
+
"icc": "^2.0.0",
|
|
129
134
|
"license-checker": "^25.0.1",
|
|
130
|
-
"mocha": "^8.
|
|
131
|
-
"mock-fs": "^4.
|
|
135
|
+
"mocha": "^8.2.1",
|
|
136
|
+
"mock-fs": "^4.13.0",
|
|
132
137
|
"nyc": "^15.1.0",
|
|
133
|
-
"prebuild": "^10.0.
|
|
134
|
-
"prebuild-ci": "^3.1.0",
|
|
138
|
+
"prebuild": "^10.0.1",
|
|
135
139
|
"rimraf": "^3.0.2",
|
|
136
|
-
"semistandard": "^
|
|
140
|
+
"semistandard": "^16.0.0"
|
|
137
141
|
},
|
|
138
142
|
"license": "Apache-2.0",
|
|
139
143
|
"config": {
|
|
140
|
-
"libvips": "8.
|
|
144
|
+
"libvips": "8.10.0",
|
|
145
|
+
"runtime": "napi",
|
|
146
|
+
"target": 3
|
|
141
147
|
},
|
|
142
148
|
"engines": {
|
|
143
149
|
"node": ">=10"
|
package/src/common.cc
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
#include <string.h>
|
|
18
18
|
#include <vector>
|
|
19
19
|
#include <queue>
|
|
20
|
+
#include <map>
|
|
20
21
|
#include <mutex> // NOLINT(build/c++11)
|
|
21
22
|
|
|
22
23
|
#include <napi.h>
|
|
@@ -41,6 +42,9 @@ namespace sharp {
|
|
|
41
42
|
int32_t AttrAsInt32(Napi::Object obj, std::string attr) {
|
|
42
43
|
return obj.Get(attr).As<Napi::Number>().Int32Value();
|
|
43
44
|
}
|
|
45
|
+
int32_t AttrAsInt32(Napi::Object obj, unsigned int const attr) {
|
|
46
|
+
return obj.Get(attr).As<Napi::Number>().Int32Value();
|
|
47
|
+
}
|
|
44
48
|
double AttrAsDouble(Napi::Object obj, std::string attr) {
|
|
45
49
|
return obj.Get(attr).As<Napi::Number>().DoubleValue();
|
|
46
50
|
}
|
|
@@ -50,13 +54,21 @@ namespace sharp {
|
|
|
50
54
|
bool AttrAsBool(Napi::Object obj, std::string attr) {
|
|
51
55
|
return obj.Get(attr).As<Napi::Boolean>().Value();
|
|
52
56
|
}
|
|
53
|
-
std::vector<double>
|
|
54
|
-
Napi::Array
|
|
55
|
-
std::vector<double>
|
|
56
|
-
for (unsigned int i = 0; i <
|
|
57
|
-
|
|
57
|
+
std::vector<double> AttrAsVectorOfDouble(Napi::Object obj, std::string attr) {
|
|
58
|
+
Napi::Array napiArray = obj.Get(attr).As<Napi::Array>();
|
|
59
|
+
std::vector<double> vectorOfDouble(napiArray.Length());
|
|
60
|
+
for (unsigned int i = 0; i < napiArray.Length(); i++) {
|
|
61
|
+
vectorOfDouble[i] = AttrAsDouble(napiArray, i);
|
|
58
62
|
}
|
|
59
|
-
return
|
|
63
|
+
return vectorOfDouble;
|
|
64
|
+
}
|
|
65
|
+
std::vector<int32_t> AttrAsInt32Vector(Napi::Object obj, std::string attr) {
|
|
66
|
+
Napi::Array array = obj.Get(attr).As<Napi::Array>();
|
|
67
|
+
std::vector<int32_t> vector(array.Length());
|
|
68
|
+
for (unsigned int i = 0; i < array.Length(); i++) {
|
|
69
|
+
vector[i] = AttrAsInt32(array, i);
|
|
70
|
+
}
|
|
71
|
+
return vector;
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
// Create an InputDescriptor instance from a Napi::Object describing an input image
|
|
@@ -97,7 +109,7 @@ namespace sharp {
|
|
|
97
109
|
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
|
98
110
|
descriptor->createWidth = AttrAsUint32(input, "createWidth");
|
|
99
111
|
descriptor->createHeight = AttrAsUint32(input, "createHeight");
|
|
100
|
-
descriptor->createBackground =
|
|
112
|
+
descriptor->createBackground = AttrAsVectorOfDouble(input, "createBackground");
|
|
101
113
|
}
|
|
102
114
|
// Limit input images to a given number of pixels, where pixels = width * height
|
|
103
115
|
descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
|
|
@@ -125,6 +137,9 @@ namespace sharp {
|
|
|
125
137
|
bool IsWebp(std::string const &str) {
|
|
126
138
|
return EndsWith(str, ".webp") || EndsWith(str, ".WEBP");
|
|
127
139
|
}
|
|
140
|
+
bool IsGif(std::string const &str) {
|
|
141
|
+
return EndsWith(str, ".gif") || EndsWith(str, ".GIF");
|
|
142
|
+
}
|
|
128
143
|
bool IsTiff(std::string const &str) {
|
|
129
144
|
return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
|
|
130
145
|
}
|
|
@@ -165,6 +180,7 @@ namespace sharp {
|
|
|
165
180
|
case ImageType::OPENSLIDE: id = "openslide"; break;
|
|
166
181
|
case ImageType::PPM: id = "ppm"; break;
|
|
167
182
|
case ImageType::FITS: id = "fits"; break;
|
|
183
|
+
case ImageType::EXR: id = "exr"; break;
|
|
168
184
|
case ImageType::VIPS: id = "vips"; break;
|
|
169
185
|
case ImageType::RAW: id = "raw"; break;
|
|
170
186
|
case ImageType::UNKNOWN: id = "unknown"; break;
|
|
@@ -173,32 +189,43 @@ namespace sharp {
|
|
|
173
189
|
return id;
|
|
174
190
|
}
|
|
175
191
|
|
|
192
|
+
std::map<std::string, ImageType> loaderToType = {
|
|
193
|
+
{ "jpegload", ImageType::JPEG },
|
|
194
|
+
{ "jpegload_buffer", ImageType::JPEG },
|
|
195
|
+
{ "pngload", ImageType::PNG },
|
|
196
|
+
{ "pngload_buffer", ImageType::PNG },
|
|
197
|
+
{ "webpload", ImageType::WEBP },
|
|
198
|
+
{ "webpload_buffer", ImageType::WEBP },
|
|
199
|
+
{ "tiffload", ImageType::TIFF },
|
|
200
|
+
{ "tiffload_buffer", ImageType::TIFF },
|
|
201
|
+
{ "gifload", ImageType::GIF },
|
|
202
|
+
{ "gifload_buffer", ImageType::GIF },
|
|
203
|
+
{ "svgload", ImageType::SVG },
|
|
204
|
+
{ "svgload_buffer", ImageType::SVG },
|
|
205
|
+
{ "heifload", ImageType::HEIF },
|
|
206
|
+
{ "heifload_buffer", ImageType::HEIF },
|
|
207
|
+
{ "pdfload", ImageType::PDF },
|
|
208
|
+
{ "pdfload_buffer", ImageType::PDF },
|
|
209
|
+
{ "magickload", ImageType::MAGICK },
|
|
210
|
+
{ "magickload_buffer", ImageType::MAGICK },
|
|
211
|
+
{ "openslideload", ImageType::OPENSLIDE },
|
|
212
|
+
{ "ppmload", ImageType::PPM },
|
|
213
|
+
{ "fitsload", ImageType::FITS },
|
|
214
|
+
{ "openexrload", ImageType::EXR },
|
|
215
|
+
{ "vipsload", ImageType::VIPS },
|
|
216
|
+
{ "rawload", ImageType::RAW }
|
|
217
|
+
};
|
|
218
|
+
|
|
176
219
|
/*
|
|
177
220
|
Determine image format of a buffer.
|
|
178
221
|
*/
|
|
179
222
|
ImageType DetermineImageType(void *buffer, size_t const length) {
|
|
180
223
|
ImageType imageType = ImageType::UNKNOWN;
|
|
181
224
|
char const *load = vips_foreign_find_load_buffer(buffer, length);
|
|
182
|
-
if (load !=
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
imageType =
|
|
186
|
-
} else if (EndsWith(loader, "PngBuffer")) {
|
|
187
|
-
imageType = ImageType::PNG;
|
|
188
|
-
} else if (EndsWith(loader, "WebpBuffer")) {
|
|
189
|
-
imageType = ImageType::WEBP;
|
|
190
|
-
} else if (EndsWith(loader, "TiffBuffer")) {
|
|
191
|
-
imageType = ImageType::TIFF;
|
|
192
|
-
} else if (EndsWith(loader, "GifBuffer")) {
|
|
193
|
-
imageType = ImageType::GIF;
|
|
194
|
-
} else if (EndsWith(loader, "SvgBuffer")) {
|
|
195
|
-
imageType = ImageType::SVG;
|
|
196
|
-
} else if (EndsWith(loader, "HeifBuffer")) {
|
|
197
|
-
imageType = ImageType::HEIF;
|
|
198
|
-
} else if (EndsWith(loader, "PdfBuffer")) {
|
|
199
|
-
imageType = ImageType::PDF;
|
|
200
|
-
} else if (EndsWith(loader, "MagickBuffer")) {
|
|
201
|
-
imageType = ImageType::MAGICK;
|
|
225
|
+
if (load != nullptr) {
|
|
226
|
+
auto it = loaderToType.find(vips_nickname_find(g_type_from_name(load)));
|
|
227
|
+
if (it != loaderToType.end()) {
|
|
228
|
+
imageType = it->second;
|
|
202
229
|
}
|
|
203
230
|
}
|
|
204
231
|
return imageType;
|
|
@@ -211,36 +238,12 @@ namespace sharp {
|
|
|
211
238
|
ImageType imageType = ImageType::UNKNOWN;
|
|
212
239
|
char const *load = vips_foreign_find_load(file);
|
|
213
240
|
if (load != nullptr) {
|
|
214
|
-
|
|
215
|
-
if (
|
|
216
|
-
imageType =
|
|
217
|
-
} else if (EndsWith(loader, "PngFile")) {
|
|
218
|
-
imageType = ImageType::PNG;
|
|
219
|
-
} else if (EndsWith(loader, "WebpFile")) {
|
|
220
|
-
imageType = ImageType::WEBP;
|
|
221
|
-
} else if (EndsWith(loader, "Openslide")) {
|
|
222
|
-
imageType = ImageType::OPENSLIDE;
|
|
223
|
-
} else if (EndsWith(loader, "TiffFile")) {
|
|
224
|
-
imageType = ImageType::TIFF;
|
|
225
|
-
} else if (EndsWith(loader, "GifFile")) {
|
|
226
|
-
imageType = ImageType::GIF;
|
|
227
|
-
} else if (EndsWith(loader, "SvgFile")) {
|
|
228
|
-
imageType = ImageType::SVG;
|
|
229
|
-
} else if (EndsWith(loader, "HeifFile")) {
|
|
230
|
-
imageType = ImageType::HEIF;
|
|
231
|
-
} else if (EndsWith(loader, "PdfFile")) {
|
|
232
|
-
imageType = ImageType::PDF;
|
|
233
|
-
} else if (EndsWith(loader, "Ppm")) {
|
|
234
|
-
imageType = ImageType::PPM;
|
|
235
|
-
} else if (EndsWith(loader, "Fits")) {
|
|
236
|
-
imageType = ImageType::FITS;
|
|
237
|
-
} else if (EndsWith(loader, "Vips")) {
|
|
238
|
-
imageType = ImageType::VIPS;
|
|
239
|
-
} else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) {
|
|
240
|
-
imageType = ImageType::MAGICK;
|
|
241
|
+
auto it = loaderToType.find(vips_nickname_find(g_type_from_name(load)));
|
|
242
|
+
if (it != loaderToType.end()) {
|
|
243
|
+
imageType = it->second;
|
|
241
244
|
}
|
|
242
245
|
} else {
|
|
243
|
-
if (EndsWith(vips::VError().what(), " not
|
|
246
|
+
if (EndsWith(vips::VError().what(), " does not exist\n")) {
|
|
244
247
|
imageType = ImageType::MISSING;
|
|
245
248
|
}
|
|
246
249
|
}
|
|
@@ -252,6 +255,8 @@ namespace sharp {
|
|
|
252
255
|
*/
|
|
253
256
|
bool ImageTypeSupportsPage(ImageType imageType) {
|
|
254
257
|
return
|
|
258
|
+
imageType == ImageType::WEBP ||
|
|
259
|
+
imageType == ImageType::MAGICK ||
|
|
255
260
|
imageType == ImageType::GIF ||
|
|
256
261
|
imageType == ImageType::TIFF ||
|
|
257
262
|
imageType == ImageType::HEIF ||
|
|
@@ -420,6 +425,38 @@ namespace sharp {
|
|
|
420
425
|
return copy;
|
|
421
426
|
}
|
|
422
427
|
|
|
428
|
+
/*
|
|
429
|
+
Set animation properties if necessary.
|
|
430
|
+
Non-provided properties will be loaded from image.
|
|
431
|
+
*/
|
|
432
|
+
VImage SetAnimationProperties(VImage image, int pageHeight, std::vector<int> delay, int loop) {
|
|
433
|
+
bool hasDelay = delay.size() != 1 || delay.front() != -1;
|
|
434
|
+
|
|
435
|
+
if (pageHeight == 0 && image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
|
|
436
|
+
pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (!hasDelay && image.get_typeof("delay") == VIPS_TYPE_ARRAY_INT) {
|
|
440
|
+
delay = image.get_array_int("delay");
|
|
441
|
+
hasDelay = true;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (loop == -1 && image.get_typeof("loop") == G_TYPE_INT) {
|
|
445
|
+
loop = image.get_int("loop");
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (pageHeight == 0) return image;
|
|
449
|
+
|
|
450
|
+
// It is necessary to create the copy as otherwise, pageHeight will be ignored!
|
|
451
|
+
VImage copy = image.copy();
|
|
452
|
+
|
|
453
|
+
copy.set(VIPS_META_PAGE_HEIGHT, pageHeight);
|
|
454
|
+
if (hasDelay) copy.set("delay", delay);
|
|
455
|
+
if (loop != -1) copy.set("loop", loop);
|
|
456
|
+
|
|
457
|
+
return copy;
|
|
458
|
+
}
|
|
459
|
+
|
|
423
460
|
/*
|
|
424
461
|
Does this image have a non-default density?
|
|
425
462
|
*/
|
|
@@ -450,14 +487,21 @@ namespace sharp {
|
|
|
450
487
|
Check the proposed format supports the current dimensions.
|
|
451
488
|
*/
|
|
452
489
|
void AssertImageTypeDimensions(VImage image, ImageType const imageType) {
|
|
490
|
+
const int height = image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT
|
|
491
|
+
? image.get_int(VIPS_META_PAGE_HEIGHT)
|
|
492
|
+
: image.height();
|
|
453
493
|
if (imageType == ImageType::JPEG) {
|
|
454
|
-
if (image.width() > 65535 ||
|
|
494
|
+
if (image.width() > 65535 || height > 65535) {
|
|
455
495
|
throw vips::VError("Processed image is too large for the JPEG format");
|
|
456
496
|
}
|
|
457
497
|
} else if (imageType == ImageType::WEBP) {
|
|
458
|
-
if (image.width() > 16383 ||
|
|
498
|
+
if (image.width() > 16383 || height > 16383) {
|
|
459
499
|
throw vips::VError("Processed image is too large for the WebP format");
|
|
460
500
|
}
|
|
501
|
+
} else if (imageType == ImageType::GIF) {
|
|
502
|
+
if (image.width() > 65535 || height > 65535) {
|
|
503
|
+
throw vips::VError("Processed image is too large for the GIF format");
|
|
504
|
+
}
|
|
461
505
|
}
|
|
462
506
|
}
|
|
463
507
|
|
|
@@ -720,4 +764,25 @@ namespace sharp {
|
|
|
720
764
|
return std::make_tuple(image, alphaColour);
|
|
721
765
|
}
|
|
722
766
|
|
|
767
|
+
/*
|
|
768
|
+
Removes alpha channel, if any.
|
|
769
|
+
*/
|
|
770
|
+
VImage RemoveAlpha(VImage image) {
|
|
771
|
+
if (HasAlpha(image)) {
|
|
772
|
+
image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));
|
|
773
|
+
}
|
|
774
|
+
return image;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/*
|
|
778
|
+
Ensures alpha channel, if missing.
|
|
779
|
+
*/
|
|
780
|
+
VImage EnsureAlpha(VImage image) {
|
|
781
|
+
if (!HasAlpha(image)) {
|
|
782
|
+
std::vector<double> alpha;
|
|
783
|
+
alpha.push_back(sharp::MaximumImageAlpha(image.interpretation()));
|
|
784
|
+
image = image.bandjoin_const(alpha);
|
|
785
|
+
}
|
|
786
|
+
return image;
|
|
787
|
+
}
|
|
723
788
|
} // namespace sharp
|