sharp 0.26.2 → 0.27.2
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 +3 -3
- package/install/libvips.js +14 -2
- package/lib/channel.js +1 -1
- package/lib/colour.js +7 -0
- package/lib/composite.js +10 -7
- package/lib/constructor.js +57 -15
- package/lib/input.js +41 -10
- package/lib/is.js +10 -0
- package/lib/libvips.js +16 -3
- package/lib/operation.js +99 -0
- package/lib/output.js +81 -36
- package/lib/utility.js +25 -8
- package/package.json +24 -18
- package/src/common.cc +77 -57
- package/src/common.h +11 -4
- package/src/metadata.cc +6 -0
- package/src/metadata.h +1 -0
- package/src/operations.cc +1 -1
- package/src/pipeline.cc +50 -17
- package/src/pipeline.h +24 -4
- package/src/sharp.cc +1 -0
package/lib/output.js
CHANGED
|
@@ -6,6 +6,7 @@ const sharp = require('../build/Release/sharp.node');
|
|
|
6
6
|
const formats = new Map([
|
|
7
7
|
['heic', 'heif'],
|
|
8
8
|
['heif', 'heif'],
|
|
9
|
+
['avif', 'avif'],
|
|
9
10
|
['jpeg', 'jpeg'],
|
|
10
11
|
['jpg', 'jpeg'],
|
|
11
12
|
['png', 'png'],
|
|
@@ -15,11 +16,13 @@ const formats = new Map([
|
|
|
15
16
|
['gif', 'gif']
|
|
16
17
|
]);
|
|
17
18
|
|
|
19
|
+
const errMagickSave = new Error('GIF output requires libvips with support for ImageMagick');
|
|
20
|
+
|
|
18
21
|
/**
|
|
19
22
|
* Write output image data to a file.
|
|
20
23
|
*
|
|
21
24
|
* If an explicit output format is not selected, it will be inferred from the extension,
|
|
22
|
-
* with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
|
|
25
|
+
* with JPEG, PNG, WebP, AVIF, TIFF, DZI, and libvips' V format supported.
|
|
23
26
|
* Note that raw pixel data is only supported for buffer output.
|
|
24
27
|
*
|
|
25
28
|
* By default all metadata will be removed, which includes EXIF-based orientation.
|
|
@@ -46,32 +49,30 @@ const formats = new Map([
|
|
|
46
49
|
* @throws {Error} Invalid parameters
|
|
47
50
|
*/
|
|
48
51
|
function toFile (fileOut, callback) {
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
let err;
|
|
53
|
+
if (!is.string(fileOut)) {
|
|
54
|
+
err = new Error('Missing output file path');
|
|
55
|
+
} else if (this.options.input.file === fileOut) {
|
|
56
|
+
err = new Error('Cannot use same file for input and output');
|
|
57
|
+
} else if (this.options.formatOut === 'input' && fileOut.toLowerCase().endsWith('.gif') && !this.constructor.format.magick.output.file) {
|
|
58
|
+
err = errMagickSave;
|
|
59
|
+
}
|
|
60
|
+
if (err) {
|
|
51
61
|
if (is.fn(callback)) {
|
|
52
|
-
callback(
|
|
62
|
+
callback(err);
|
|
53
63
|
} else {
|
|
54
|
-
return Promise.reject(
|
|
64
|
+
return Promise.reject(err);
|
|
55
65
|
}
|
|
56
66
|
} else {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (is.fn(callback)) {
|
|
60
|
-
callback(errOutputIsInput);
|
|
61
|
-
} else {
|
|
62
|
-
return Promise.reject(errOutputIsInput);
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
this.options.fileOut = fileOut;
|
|
66
|
-
return this._pipeline(callback);
|
|
67
|
-
}
|
|
67
|
+
this.options.fileOut = fileOut;
|
|
68
|
+
return this._pipeline(callback);
|
|
68
69
|
}
|
|
69
70
|
return this;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
/**
|
|
73
74
|
* Write output to a Buffer.
|
|
74
|
-
* JPEG, PNG, WebP, TIFF and
|
|
75
|
+
* JPEG, PNG, WebP, AVIF, TIFF and raw pixel data output are supported.
|
|
75
76
|
*
|
|
76
77
|
* If no explicit format is set, the output format will match the input image, except GIF and SVG input which become PNG output.
|
|
77
78
|
*
|
|
@@ -103,6 +104,20 @@ function toFile (fileOut, callback) {
|
|
|
103
104
|
* .then(({ data, info }) => { ... })
|
|
104
105
|
* .catch(err => { ... });
|
|
105
106
|
*
|
|
107
|
+
* @example
|
|
108
|
+
* const data = await sharp('my-image.jpg')
|
|
109
|
+
* // output the raw pixels
|
|
110
|
+
* .raw()
|
|
111
|
+
* .toBuffer();
|
|
112
|
+
*
|
|
113
|
+
* // create a more type safe way to work with the raw pixel data
|
|
114
|
+
* // this will not copy the data, instead it will change `data`s underlying ArrayBuffer
|
|
115
|
+
* // so `data` and `pixelArray` point to the same memory location
|
|
116
|
+
* const pixelArray = new Uint8ClampedArray(data.buffer);
|
|
117
|
+
*
|
|
118
|
+
* // When you are done changing the pixelArray, sharp takes the `pixelArray` as an input
|
|
119
|
+
* await sharp(pixelArray).toFile('my-changed-image.jpg');
|
|
120
|
+
*
|
|
106
121
|
* @param {Object} [options]
|
|
107
122
|
* @param {boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
|
108
123
|
* @param {Function} [callback]
|
|
@@ -173,7 +188,7 @@ function withMetadata (options) {
|
|
|
173
188
|
* @throws {Error} unsupported format or options
|
|
174
189
|
*/
|
|
175
190
|
function toFormat (format, options) {
|
|
176
|
-
const actualFormat = formats.get(is.object(format) && is.string(format.id) ? format.id : format);
|
|
191
|
+
const actualFormat = formats.get((is.object(format) && is.string(format.id) ? format.id : format).toLowerCase());
|
|
177
192
|
if (!actualFormat) {
|
|
178
193
|
throw is.invalidParameterError('format', `one of: ${[...formats.keys()].join(', ')}`, format);
|
|
179
194
|
}
|
|
@@ -411,7 +426,7 @@ function webp (options) {
|
|
|
411
426
|
/* istanbul ignore next */
|
|
412
427
|
function gif (options) {
|
|
413
428
|
if (!this.constructor.format.magick.output.buffer) {
|
|
414
|
-
throw
|
|
429
|
+
throw errMagickSave;
|
|
415
430
|
}
|
|
416
431
|
trySetAnimationOptions(options, this.options);
|
|
417
432
|
return this._updateFormatOut('gif', options);
|
|
@@ -471,15 +486,15 @@ function trySetAnimationOptions (source, target) {
|
|
|
471
486
|
* @param {Object} [options] - output options
|
|
472
487
|
* @param {number} [options.quality=80] - quality, integer 1-100
|
|
473
488
|
* @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
|
|
474
|
-
* @param {
|
|
475
|
-
* @param {
|
|
489
|
+
* @param {string} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
|
|
490
|
+
* @param {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
|
|
476
491
|
* @param {boolean} [options.pyramid=false] - write an image pyramid
|
|
477
492
|
* @param {boolean} [options.tile=false] - write a tiled tiff
|
|
478
|
-
* @param {
|
|
479
|
-
* @param {
|
|
493
|
+
* @param {number} [options.tileWidth=256] - horizontal tile size
|
|
494
|
+
* @param {number} [options.tileHeight=256] - vertical tile size
|
|
480
495
|
* @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
|
|
481
496
|
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
|
|
482
|
-
* @param {
|
|
497
|
+
* @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
|
|
483
498
|
* @returns {Sharp}
|
|
484
499
|
* @throws {Error} Invalid options
|
|
485
500
|
*/
|
|
@@ -557,28 +572,43 @@ function tiff (options) {
|
|
|
557
572
|
}
|
|
558
573
|
|
|
559
574
|
/**
|
|
560
|
-
* Use these
|
|
575
|
+
* Use these AVIF options for output image.
|
|
561
576
|
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
577
|
+
* Whilst it is possible to create AVIF images smaller than 16x16 pixels,
|
|
578
|
+
* most web browsers do not display these properly.
|
|
564
579
|
*
|
|
565
|
-
*
|
|
580
|
+
* @since 0.27.0
|
|
581
|
+
*
|
|
582
|
+
* @param {Object} [options] - output options
|
|
583
|
+
* @param {number} [options.quality=50] - quality, integer 1-100
|
|
584
|
+
* @param {boolean} [options.lossless=false] - use lossless compression
|
|
585
|
+
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
|
|
586
|
+
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
|
|
587
|
+
* @returns {Sharp}
|
|
588
|
+
* @throws {Error} Invalid options
|
|
589
|
+
*/
|
|
590
|
+
function avif (options) {
|
|
591
|
+
return this.heif({ ...options, compression: 'av1' });
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Use these HEIF options for output image.
|
|
566
596
|
*
|
|
567
|
-
*
|
|
597
|
+
* Support for patent-encumbered HEIC images requires the use of a
|
|
598
|
+
* globally-installed libvips compiled with support for libheif, libde265 and x265.
|
|
568
599
|
*
|
|
569
600
|
* @since 0.23.0
|
|
570
601
|
*
|
|
571
602
|
* @param {Object} [options] - output options
|
|
572
|
-
* @param {number} [options.quality=
|
|
573
|
-
* @param {
|
|
603
|
+
* @param {number} [options.quality=50] - quality, integer 1-100
|
|
604
|
+
* @param {string} [options.compression='av1'] - compression format: av1, hevc
|
|
574
605
|
* @param {boolean} [options.lossless=false] - use lossless compression
|
|
606
|
+
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
|
|
607
|
+
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
|
|
575
608
|
* @returns {Sharp}
|
|
576
609
|
* @throws {Error} Invalid options
|
|
577
610
|
*/
|
|
578
611
|
function heif (options) {
|
|
579
|
-
if (!this.constructor.format.heif.output.buffer) {
|
|
580
|
-
throw new Error('The heif operation requires libvips to have been installed with support for libheif');
|
|
581
|
-
}
|
|
582
612
|
if (is.object(options)) {
|
|
583
613
|
if (is.defined(options.quality)) {
|
|
584
614
|
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
|
@@ -595,10 +625,24 @@ function heif (options) {
|
|
|
595
625
|
}
|
|
596
626
|
}
|
|
597
627
|
if (is.defined(options.compression)) {
|
|
598
|
-
if (is.string(options.compression) && is.inArray(options.compression, ['
|
|
628
|
+
if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
|
|
599
629
|
this.options.heifCompression = options.compression;
|
|
600
630
|
} else {
|
|
601
|
-
throw is.invalidParameterError('compression', 'one of:
|
|
631
|
+
throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (is.defined(options.speed)) {
|
|
635
|
+
if (is.integer(options.speed) && is.inRange(options.speed, 0, 8)) {
|
|
636
|
+
this.options.heifSpeed = options.speed;
|
|
637
|
+
} else {
|
|
638
|
+
throw is.invalidParameterError('speed', 'integer between 0 and 8', options.speed);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (is.defined(options.chromaSubsampling)) {
|
|
642
|
+
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
|
|
643
|
+
this.options.heifChromaSubsampling = options.chromaSubsampling;
|
|
644
|
+
} else {
|
|
645
|
+
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
|
|
602
646
|
}
|
|
603
647
|
}
|
|
604
648
|
}
|
|
@@ -891,6 +935,7 @@ module.exports = function (Sharp) {
|
|
|
891
935
|
png,
|
|
892
936
|
webp,
|
|
893
937
|
tiff,
|
|
938
|
+
avif,
|
|
894
939
|
heif,
|
|
895
940
|
gif,
|
|
896
941
|
raw,
|
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
|
|
@@ -137,15 +157,12 @@ simd(true);
|
|
|
137
157
|
* @private
|
|
138
158
|
*/
|
|
139
159
|
module.exports = function (Sharp) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
simd
|
|
145
|
-
].forEach(function (f) {
|
|
146
|
-
Sharp[f.name] = f;
|
|
147
|
-
});
|
|
160
|
+
Sharp.cache = cache;
|
|
161
|
+
Sharp.concurrency = concurrency;
|
|
162
|
+
Sharp.counters = counters;
|
|
163
|
+
Sharp.simd = simd;
|
|
148
164
|
Sharp.format = format;
|
|
165
|
+
Sharp.interpolators = interpolators;
|
|
149
166
|
Sharp.versions = versions;
|
|
150
167
|
Sharp.queue = queue;
|
|
151
168
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sharp",
|
|
3
|
-
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
|
4
|
-
"version": "0.
|
|
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",
|
|
5
5
|
"author": "Lovell Fuller <npm@lovell.info>",
|
|
6
6
|
"homepage": "https://github.com/lovell/sharp",
|
|
7
7
|
"contributors": [
|
|
@@ -69,17 +69,22 @@
|
|
|
69
69
|
"Edward Silverton <e.silverton@gmail.com>",
|
|
70
70
|
"Roman Malieiev <aromaleev@gmail.com>",
|
|
71
71
|
"Tomas Szabo <tomas.szabo@deftomat.com>",
|
|
72
|
-
"Robert O'Rourke <robert@o-rourke.org>"
|
|
72
|
+
"Robert O'Rourke <robert@o-rourke.org>",
|
|
73
|
+
"Guillermo Alfonso Varela Chouciño <guillevch@gmail.com>",
|
|
74
|
+
"Christian Flintrup <chr@gigahost.dk>",
|
|
75
|
+
"Manan Jadhav <manan@motionden.com>",
|
|
76
|
+
"Leon Radley <leon@radley.se>",
|
|
77
|
+
"alza54 <alza54@thiocod.in>"
|
|
73
78
|
],
|
|
74
79
|
"scripts": {
|
|
75
80
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
|
76
81
|
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
|
|
77
|
-
"test": "semistandard && cpplint && npm run test-unit && npm run test-licensing
|
|
82
|
+
"test": "semistandard && cpplint && npm run test-unit && npm run test-licensing",
|
|
78
83
|
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
|
|
79
84
|
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
|
|
80
85
|
"test-coverage": "./test/coverage/report.sh",
|
|
81
86
|
"test-leak": "./test/leak/leak.sh",
|
|
82
|
-
"docs-build": "documentation lint lib &&
|
|
87
|
+
"docs-build": "documentation lint lib && node docs/build && node docs/search-index/build",
|
|
83
88
|
"docs-serve": "cd docs && npx serve",
|
|
84
89
|
"docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
|
|
85
90
|
},
|
|
@@ -87,7 +92,6 @@
|
|
|
87
92
|
"files": [
|
|
88
93
|
"binding.gyp",
|
|
89
94
|
"install/**",
|
|
90
|
-
"!install/prebuild-ci.js",
|
|
91
95
|
"lib/**",
|
|
92
96
|
"src/**"
|
|
93
97
|
],
|
|
@@ -99,6 +103,7 @@
|
|
|
99
103
|
"jpeg",
|
|
100
104
|
"png",
|
|
101
105
|
"webp",
|
|
106
|
+
"avif",
|
|
102
107
|
"tiff",
|
|
103
108
|
"gif",
|
|
104
109
|
"svg",
|
|
@@ -112,39 +117,40 @@
|
|
|
112
117
|
"vips"
|
|
113
118
|
],
|
|
114
119
|
"dependencies": {
|
|
115
|
-
"
|
|
120
|
+
"array-flatten": "^3.0.0",
|
|
121
|
+
"color": "^3.1.3",
|
|
116
122
|
"detect-libc": "^1.0.3",
|
|
117
|
-
"node-addon-api": "^3.0
|
|
123
|
+
"node-addon-api": "^3.1.0",
|
|
118
124
|
"npmlog": "^4.1.2",
|
|
119
|
-
"prebuild-install": "^
|
|
120
|
-
"semver": "^7.3.
|
|
125
|
+
"prebuild-install": "^6.0.1",
|
|
126
|
+
"semver": "^7.3.4",
|
|
121
127
|
"simple-get": "^4.0.0",
|
|
122
|
-
"tar-fs": "^2.1.
|
|
128
|
+
"tar-fs": "^2.1.1",
|
|
123
129
|
"tunnel-agent": "^0.6.0"
|
|
124
130
|
},
|
|
125
131
|
"devDependencies": {
|
|
126
132
|
"async": "^3.2.0",
|
|
127
|
-
"cc": "^
|
|
128
|
-
"decompress-zip": "^0.3.
|
|
129
|
-
"documentation": "^13.
|
|
133
|
+
"cc": "^3.0.1",
|
|
134
|
+
"decompress-zip": "^0.3.3",
|
|
135
|
+
"documentation": "^13.1.1",
|
|
130
136
|
"exif-reader": "^1.0.3",
|
|
131
137
|
"icc": "^2.0.0",
|
|
132
138
|
"license-checker": "^25.0.1",
|
|
133
|
-
"mocha": "^8.
|
|
139
|
+
"mocha": "^8.3.0",
|
|
134
140
|
"mock-fs": "^4.13.0",
|
|
135
141
|
"nyc": "^15.1.0",
|
|
136
142
|
"prebuild": "^10.0.1",
|
|
137
143
|
"rimraf": "^3.0.2",
|
|
138
|
-
"semistandard": "^
|
|
144
|
+
"semistandard": "^16.0.0"
|
|
139
145
|
},
|
|
140
146
|
"license": "Apache-2.0",
|
|
141
147
|
"config": {
|
|
142
|
-
"libvips": "8.10.
|
|
148
|
+
"libvips": "8.10.5",
|
|
143
149
|
"runtime": "napi",
|
|
144
150
|
"target": 3
|
|
145
151
|
},
|
|
146
152
|
"engines": {
|
|
147
|
-
"node": ">=10
|
|
153
|
+
"node": ">=10"
|
|
148
154
|
},
|
|
149
155
|
"funding": {
|
|
150
156
|
"url": "https://opencollective.com/libvips"
|
package/src/common.cc
CHANGED
|
@@ -54,13 +54,13 @@ namespace sharp {
|
|
|
54
54
|
bool AttrAsBool(Napi::Object obj, std::string attr) {
|
|
55
55
|
return obj.Get(attr).As<Napi::Boolean>().Value();
|
|
56
56
|
}
|
|
57
|
-
std::vector<double>
|
|
58
|
-
Napi::Array
|
|
59
|
-
std::vector<double>
|
|
60
|
-
for (unsigned int i = 0; i <
|
|
61
|
-
|
|
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);
|
|
62
62
|
}
|
|
63
|
-
return
|
|
63
|
+
return vectorOfDouble;
|
|
64
64
|
}
|
|
65
65
|
std::vector<int32_t> AttrAsInt32Vector(Napi::Object obj, std::string attr) {
|
|
66
66
|
Napi::Array array = obj.Get(attr).As<Napi::Array>();
|
|
@@ -109,7 +109,13 @@ namespace sharp {
|
|
|
109
109
|
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
|
110
110
|
descriptor->createWidth = AttrAsUint32(input, "createWidth");
|
|
111
111
|
descriptor->createHeight = AttrAsUint32(input, "createHeight");
|
|
112
|
-
|
|
112
|
+
if (HasAttr(input, "createNoiseType")) {
|
|
113
|
+
descriptor->createNoiseType = AttrAsStr(input, "createNoiseType");
|
|
114
|
+
descriptor->createNoiseMean = AttrAsDouble(input, "createNoiseMean");
|
|
115
|
+
descriptor->createNoiseSigma = AttrAsDouble(input, "createNoiseSigma");
|
|
116
|
+
} else {
|
|
117
|
+
descriptor->createBackground = AttrAsVectorOfDouble(input, "createBackground");
|
|
118
|
+
}
|
|
113
119
|
}
|
|
114
120
|
// Limit input images to a given number of pixels, where pixels = width * height
|
|
115
121
|
descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
|
|
@@ -189,31 +195,38 @@ namespace sharp {
|
|
|
189
195
|
return id;
|
|
190
196
|
}
|
|
191
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Regenerate this table with something like:
|
|
200
|
+
*
|
|
201
|
+
* $ vips -l foreign | grep -i load | awk '{ print $2, $1; }'
|
|
202
|
+
*
|
|
203
|
+
* Plus a bit of editing.
|
|
204
|
+
*/
|
|
192
205
|
std::map<std::string, ImageType> loaderToType = {
|
|
193
|
-
{ "
|
|
194
|
-
{ "
|
|
195
|
-
{ "
|
|
196
|
-
{ "
|
|
197
|
-
{ "
|
|
198
|
-
{ "
|
|
199
|
-
{ "
|
|
200
|
-
{ "
|
|
201
|
-
{ "
|
|
202
|
-
{ "
|
|
203
|
-
{ "
|
|
204
|
-
{ "
|
|
205
|
-
{ "
|
|
206
|
-
{ "
|
|
207
|
-
{ "
|
|
208
|
-
{ "
|
|
209
|
-
{ "
|
|
210
|
-
{ "
|
|
211
|
-
{ "
|
|
212
|
-
{ "
|
|
213
|
-
{ "
|
|
214
|
-
{ "
|
|
215
|
-
{ "
|
|
216
|
-
{ "
|
|
206
|
+
{ "VipsForeignLoadJpegFile", ImageType::JPEG },
|
|
207
|
+
{ "VipsForeignLoadJpegBuffer", ImageType::JPEG },
|
|
208
|
+
{ "VipsForeignLoadPngFile", ImageType::PNG },
|
|
209
|
+
{ "VipsForeignLoadPngBuffer", ImageType::PNG },
|
|
210
|
+
{ "VipsForeignLoadWebpFile", ImageType::WEBP },
|
|
211
|
+
{ "VipsForeignLoadWebpBuffer", ImageType::WEBP },
|
|
212
|
+
{ "VipsForeignLoadTiffFile", ImageType::TIFF },
|
|
213
|
+
{ "VipsForeignLoadTiffBuffer", ImageType::TIFF },
|
|
214
|
+
{ "VipsForeignLoadGifFile", ImageType::GIF },
|
|
215
|
+
{ "VipsForeignLoadGifBuffer", ImageType::GIF },
|
|
216
|
+
{ "VipsForeignLoadSvgFile", ImageType::SVG },
|
|
217
|
+
{ "VipsForeignLoadSvgBuffer", ImageType::SVG },
|
|
218
|
+
{ "VipsForeignLoadHeifFile", ImageType::HEIF },
|
|
219
|
+
{ "VipsForeignLoadHeifBuffer", ImageType::HEIF },
|
|
220
|
+
{ "VipsForeignLoadPdfFile", ImageType::PDF },
|
|
221
|
+
{ "VipsForeignLoadPdfBuffer", ImageType::PDF },
|
|
222
|
+
{ "VipsForeignLoadMagickFile", ImageType::MAGICK },
|
|
223
|
+
{ "VipsForeignLoadMagickBuffer", ImageType::MAGICK },
|
|
224
|
+
{ "VipsForeignLoadOpenslide", ImageType::OPENSLIDE },
|
|
225
|
+
{ "VipsForeignLoadPpmFile", ImageType::PPM },
|
|
226
|
+
{ "VipsForeignLoadFits", ImageType::FITS },
|
|
227
|
+
{ "VipsForeignLoadOpenexr", ImageType::EXR },
|
|
228
|
+
{ "VipsForeignLoadVips", ImageType::VIPS },
|
|
229
|
+
{ "VipsForeignLoadRaw", ImageType::RAW }
|
|
217
230
|
};
|
|
218
231
|
|
|
219
232
|
/*
|
|
@@ -223,7 +236,7 @@ namespace sharp {
|
|
|
223
236
|
ImageType imageType = ImageType::UNKNOWN;
|
|
224
237
|
char const *load = vips_foreign_find_load_buffer(buffer, length);
|
|
225
238
|
if (load != nullptr) {
|
|
226
|
-
auto it = loaderToType.find(
|
|
239
|
+
auto it = loaderToType.find(load);
|
|
227
240
|
if (it != loaderToType.end()) {
|
|
228
241
|
imageType = it->second;
|
|
229
242
|
}
|
|
@@ -238,7 +251,7 @@ namespace sharp {
|
|
|
238
251
|
ImageType imageType = ImageType::UNKNOWN;
|
|
239
252
|
char const *load = vips_foreign_find_load(file);
|
|
240
253
|
if (load != nullptr) {
|
|
241
|
-
auto it = loaderToType.find(
|
|
254
|
+
auto it = loaderToType.find(load);
|
|
242
255
|
if (it != loaderToType.end()) {
|
|
243
256
|
imageType = it->second;
|
|
244
257
|
}
|
|
@@ -318,15 +331,35 @@ namespace sharp {
|
|
|
318
331
|
} else {
|
|
319
332
|
if (descriptor->createChannels > 0) {
|
|
320
333
|
// Create new image
|
|
321
|
-
|
|
322
|
-
descriptor->
|
|
323
|
-
descriptor->
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
334
|
+
if (descriptor->createNoiseType == "gaussian") {
|
|
335
|
+
int const channels = descriptor->createChannels;
|
|
336
|
+
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight);
|
|
337
|
+
std::vector<VImage> bands = {};
|
|
338
|
+
bands.reserve(channels);
|
|
339
|
+
for (int _band = 0; _band < channels; _band++) {
|
|
340
|
+
bands.push_back(image.gaussnoise(
|
|
341
|
+
descriptor->createWidth,
|
|
342
|
+
descriptor->createHeight,
|
|
343
|
+
VImage::option()->set("mean", descriptor->createNoiseMean)->set("sigma", descriptor->createNoiseSigma)));
|
|
344
|
+
}
|
|
345
|
+
image = image.bandjoin(bands);
|
|
346
|
+
image = image.cast(VipsBandFormat::VIPS_FORMAT_UCHAR);
|
|
347
|
+
if (channels < 3) {
|
|
348
|
+
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
|
349
|
+
} else {
|
|
350
|
+
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
std::vector<double> background = {
|
|
354
|
+
descriptor->createBackground[0],
|
|
355
|
+
descriptor->createBackground[1],
|
|
356
|
+
descriptor->createBackground[2]
|
|
357
|
+
};
|
|
358
|
+
if (descriptor->createChannels == 4) {
|
|
359
|
+
background.push_back(descriptor->createBackground[3]);
|
|
360
|
+
}
|
|
361
|
+
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
|
328
362
|
}
|
|
329
|
-
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
|
330
363
|
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
|
|
331
364
|
imageType = ImageType::RAW;
|
|
332
365
|
} else {
|
|
@@ -388,12 +421,7 @@ namespace sharp {
|
|
|
388
421
|
Uses colour space interpretation with number of channels to guess this.
|
|
389
422
|
*/
|
|
390
423
|
bool HasAlpha(VImage image) {
|
|
391
|
-
|
|
392
|
-
VipsInterpretation const interpretation = image.interpretation();
|
|
393
|
-
return (
|
|
394
|
-
(bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) ||
|
|
395
|
-
(bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) ||
|
|
396
|
-
(bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK));
|
|
424
|
+
return image.has_alpha();
|
|
397
425
|
}
|
|
398
426
|
|
|
399
427
|
/*
|
|
@@ -658,26 +686,18 @@ namespace sharp {
|
|
|
658
686
|
int top = 0;
|
|
659
687
|
|
|
660
688
|
// assign only if valid
|
|
661
|
-
if (x
|
|
689
|
+
if (x < (inWidth - outWidth)) {
|
|
662
690
|
left = x;
|
|
663
691
|
} else if (x >= (inWidth - outWidth)) {
|
|
664
692
|
left = inWidth - outWidth;
|
|
665
693
|
}
|
|
666
694
|
|
|
667
|
-
if (y
|
|
695
|
+
if (y < (inHeight - outHeight)) {
|
|
668
696
|
top = y;
|
|
669
697
|
} else if (y >= (inHeight - outHeight)) {
|
|
670
698
|
top = inHeight - outHeight;
|
|
671
699
|
}
|
|
672
700
|
|
|
673
|
-
// the resulting left and top could have been outside the image after calculation from bottom/right edges
|
|
674
|
-
if (left < 0) {
|
|
675
|
-
left = 0;
|
|
676
|
-
}
|
|
677
|
-
if (top < 0) {
|
|
678
|
-
top = 0;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
701
|
return std::make_tuple(left, top);
|
|
682
702
|
}
|
|
683
703
|
|
package/src/common.h
CHANGED
|
@@ -24,8 +24,10 @@
|
|
|
24
24
|
|
|
25
25
|
// Verify platform and compiler compatibility
|
|
26
26
|
|
|
27
|
-
#if (VIPS_MAJOR_VERSION < 8 ||
|
|
28
|
-
|
|
27
|
+
#if (VIPS_MAJOR_VERSION < 8) || \
|
|
28
|
+
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10) || \
|
|
29
|
+
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 10 && VIPS_MICRO_VERSION < 5)
|
|
30
|
+
#error "libvips version 8.10.5+ is required - please see https://sharp.pixelplumbing.com/install"
|
|
29
31
|
#endif
|
|
30
32
|
|
|
31
33
|
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
|
@@ -62,6 +64,9 @@ namespace sharp {
|
|
|
62
64
|
int createWidth;
|
|
63
65
|
int createHeight;
|
|
64
66
|
std::vector<double> createBackground;
|
|
67
|
+
std::string createNoiseType;
|
|
68
|
+
double createNoiseMean;
|
|
69
|
+
double createNoiseSigma;
|
|
65
70
|
|
|
66
71
|
InputDescriptor():
|
|
67
72
|
buffer(nullptr),
|
|
@@ -80,7 +85,9 @@ namespace sharp {
|
|
|
80
85
|
createChannels(0),
|
|
81
86
|
createWidth(0),
|
|
82
87
|
createHeight(0),
|
|
83
|
-
createBackground{ 0.0, 0.0, 0.0, 255.0 }
|
|
88
|
+
createBackground{ 0.0, 0.0, 0.0, 255.0 },
|
|
89
|
+
createNoiseMean(0.0),
|
|
90
|
+
createNoiseSigma(0.0) {}
|
|
84
91
|
};
|
|
85
92
|
|
|
86
93
|
// Convenience methods to access the attributes of a Napi::Object
|
|
@@ -92,7 +99,7 @@ namespace sharp {
|
|
|
92
99
|
double AttrAsDouble(Napi::Object obj, std::string attr);
|
|
93
100
|
double AttrAsDouble(Napi::Object obj, unsigned int const attr);
|
|
94
101
|
bool AttrAsBool(Napi::Object obj, std::string attr);
|
|
95
|
-
std::vector<double>
|
|
102
|
+
std::vector<double> AttrAsVectorOfDouble(Napi::Object obj, std::string attr);
|
|
96
103
|
std::vector<int32_t> AttrAsInt32Vector(Napi::Object obj, std::string attr);
|
|
97
104
|
|
|
98
105
|
// Create an InputDescriptor instance from a Napi::Object describing an input image
|
package/src/metadata.cc
CHANGED
|
@@ -74,6 +74,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|
|
74
74
|
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
|
|
75
75
|
baton->pagePrimary = image.get_int("heif-primary");
|
|
76
76
|
}
|
|
77
|
+
if (image.get_typeof("heif-compression") == VIPS_TYPE_REF_STRING) {
|
|
78
|
+
baton->compression = image.get_string("heif-compression");
|
|
79
|
+
}
|
|
77
80
|
if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
|
|
78
81
|
int const levels = std::stoi(image.get_string("openslide.level-count"));
|
|
79
82
|
for (int l = 0; l < levels; l++) {
|
|
@@ -186,6 +189,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|
|
186
189
|
if (baton->pagePrimary > -1) {
|
|
187
190
|
info.Set("pagePrimary", baton->pagePrimary);
|
|
188
191
|
}
|
|
192
|
+
if (!baton->compression.empty()) {
|
|
193
|
+
info.Set("compression", baton->compression);
|
|
194
|
+
}
|
|
189
195
|
if (!baton->levels.empty()) {
|
|
190
196
|
int i = 0;
|
|
191
197
|
Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
|
package/src/metadata.h
CHANGED
package/src/operations.cc
CHANGED
|
@@ -149,8 +149,8 @@ namespace sharp {
|
|
|
149
149
|
*/
|
|
150
150
|
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix) {
|
|
151
151
|
double *m = matrix.get();
|
|
152
|
+
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
152
153
|
return image
|
|
153
|
-
.colourspace(VIPS_INTERPRETATION_sRGB)
|
|
154
154
|
.recomb(image.bands() == 3
|
|
155
155
|
? VImage::new_from_memory(
|
|
156
156
|
m, 9 * sizeof(double), 3, 3, 1, VIPS_FORMAT_DOUBLE
|