sharp 0.26.1 → 0.27.1
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 +11 -4
- package/lib/channel.js +1 -1
- package/lib/colour.js +7 -0
- package/lib/composite.js +10 -7
- package/lib/constructor.js +58 -15
- package/lib/input.js +41 -10
- package/lib/is.js +10 -0
- package/lib/libvips.js +3 -2
- package/lib/operation.js +99 -0
- package/lib/output.js +71 -19
- package/lib/resize.js +2 -1
- package/lib/utility.js +21 -0
- package/package.json +25 -19
- package/src/common.cc +77 -50
- package/src/common.h +12 -4
- package/src/metadata.cc +6 -0
- package/src/metadata.h +1 -0
- package/src/pipeline.cc +52 -17
- package/src/pipeline.h +25 -4
- package/src/sharp.cc +1 -0
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
|
|
@@ -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
|
-
"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.1",
|
|
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
|
|
78
|
-
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
|
|
82
|
+
"test": "semistandard && cpplint && npm run test-unit && npm run test-licensing",
|
|
83
|
+
"test-unit": "nyc --reporter=lcov --branches=99 mocha --parallel --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.0",
|
|
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.2.1",
|
|
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");
|
|
@@ -180,6 +186,7 @@ namespace sharp {
|
|
|
180
186
|
case ImageType::OPENSLIDE: id = "openslide"; break;
|
|
181
187
|
case ImageType::PPM: id = "ppm"; break;
|
|
182
188
|
case ImageType::FITS: id = "fits"; break;
|
|
189
|
+
case ImageType::EXR: id = "exr"; break;
|
|
183
190
|
case ImageType::VIPS: id = "vips"; break;
|
|
184
191
|
case ImageType::RAW: id = "raw"; break;
|
|
185
192
|
case ImageType::UNKNOWN: id = "unknown"; break;
|
|
@@ -188,30 +195,38 @@ namespace sharp {
|
|
|
188
195
|
return id;
|
|
189
196
|
}
|
|
190
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
|
+
*/
|
|
191
205
|
std::map<std::string, ImageType> loaderToType = {
|
|
192
|
-
{ "
|
|
193
|
-
{ "
|
|
194
|
-
{ "
|
|
195
|
-
{ "
|
|
196
|
-
{ "
|
|
197
|
-
{ "
|
|
198
|
-
{ "
|
|
199
|
-
{ "
|
|
200
|
-
{ "
|
|
201
|
-
{ "
|
|
202
|
-
{ "
|
|
203
|
-
{ "
|
|
204
|
-
{ "
|
|
205
|
-
{ "
|
|
206
|
-
{ "
|
|
207
|
-
{ "
|
|
208
|
-
{ "
|
|
209
|
-
{ "
|
|
210
|
-
{ "
|
|
211
|
-
{ "
|
|
212
|
-
{ "
|
|
213
|
-
{ "
|
|
214
|
-
{ "
|
|
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 }
|
|
215
230
|
};
|
|
216
231
|
|
|
217
232
|
/*
|
|
@@ -221,7 +236,7 @@ namespace sharp {
|
|
|
221
236
|
ImageType imageType = ImageType::UNKNOWN;
|
|
222
237
|
char const *load = vips_foreign_find_load_buffer(buffer, length);
|
|
223
238
|
if (load != nullptr) {
|
|
224
|
-
auto it = loaderToType.find(
|
|
239
|
+
auto it = loaderToType.find(load);
|
|
225
240
|
if (it != loaderToType.end()) {
|
|
226
241
|
imageType = it->second;
|
|
227
242
|
}
|
|
@@ -236,7 +251,7 @@ namespace sharp {
|
|
|
236
251
|
ImageType imageType = ImageType::UNKNOWN;
|
|
237
252
|
char const *load = vips_foreign_find_load(file);
|
|
238
253
|
if (load != nullptr) {
|
|
239
|
-
auto it = loaderToType.find(
|
|
254
|
+
auto it = loaderToType.find(load);
|
|
240
255
|
if (it != loaderToType.end()) {
|
|
241
256
|
imageType = it->second;
|
|
242
257
|
}
|
|
@@ -316,15 +331,35 @@ namespace sharp {
|
|
|
316
331
|
} else {
|
|
317
332
|
if (descriptor->createChannels > 0) {
|
|
318
333
|
// Create new image
|
|
319
|
-
|
|
320
|
-
descriptor->
|
|
321
|
-
descriptor->
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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);
|
|
326
362
|
}
|
|
327
|
-
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
|
328
363
|
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
|
|
329
364
|
imageType = ImageType::RAW;
|
|
330
365
|
} else {
|
|
@@ -656,26 +691,18 @@ namespace sharp {
|
|
|
656
691
|
int top = 0;
|
|
657
692
|
|
|
658
693
|
// assign only if valid
|
|
659
|
-
if (x
|
|
694
|
+
if (x < (inWidth - outWidth)) {
|
|
660
695
|
left = x;
|
|
661
696
|
} else if (x >= (inWidth - outWidth)) {
|
|
662
697
|
left = inWidth - outWidth;
|
|
663
698
|
}
|
|
664
699
|
|
|
665
|
-
if (y
|
|
700
|
+
if (y < (inHeight - outHeight)) {
|
|
666
701
|
top = y;
|
|
667
702
|
} else if (y >= (inHeight - outHeight)) {
|
|
668
703
|
top = inHeight - outHeight;
|
|
669
704
|
}
|
|
670
705
|
|
|
671
|
-
// the resulting left and top could have been outside the image after calculation from bottom/right edges
|
|
672
|
-
if (left < 0) {
|
|
673
|
-
left = 0;
|
|
674
|
-
}
|
|
675
|
-
if (top < 0) {
|
|
676
|
-
top = 0;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
706
|
return std::make_tuple(left, top);
|
|
680
707
|
}
|
|
681
708
|
|
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
|
|
@@ -111,6 +118,7 @@ namespace sharp {
|
|
|
111
118
|
OPENSLIDE,
|
|
112
119
|
PPM,
|
|
113
120
|
FITS,
|
|
121
|
+
EXR,
|
|
114
122
|
VIPS,
|
|
115
123
|
RAW,
|
|
116
124
|
UNKNOWN,
|
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/pipeline.cc
CHANGED
|
@@ -485,6 +485,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
485
485
|
baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost);
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
+
// Affine transform
|
|
489
|
+
if (baton->affineMatrix.size() > 0) {
|
|
490
|
+
std::vector<double> background;
|
|
491
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground);
|
|
492
|
+
image = image.affine(baton->affineMatrix, VImage::option()->set("background", background)
|
|
493
|
+
->set("idx", baton->affineIdx)
|
|
494
|
+
->set("idy", baton->affineIdy)
|
|
495
|
+
->set("odx", baton->affineOdx)
|
|
496
|
+
->set("ody", baton->affineOdy)
|
|
497
|
+
->set("interpolate", baton->affineInterpolator));
|
|
498
|
+
}
|
|
499
|
+
|
|
488
500
|
// Extend edges
|
|
489
501
|
if (baton->extendTop > 0 || baton->extendBottom > 0 || baton->extendLeft > 0 || baton->extendRight > 0) {
|
|
490
502
|
std::vector<double> background;
|
|
@@ -558,7 +570,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
558
570
|
int left;
|
|
559
571
|
int top;
|
|
560
572
|
compositeImage = compositeImage.replicate(across, down);
|
|
561
|
-
if (composite->
|
|
573
|
+
if (composite->hasOffset) {
|
|
562
574
|
std::tie(left, top) = sharp::CalculateCrop(
|
|
563
575
|
compositeImage.width(), compositeImage.height(), image.width(), image.height(),
|
|
564
576
|
composite->left, composite->top);
|
|
@@ -580,7 +592,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
580
592
|
// Calculate position
|
|
581
593
|
int left;
|
|
582
594
|
int top;
|
|
583
|
-
if (composite->
|
|
595
|
+
if (composite->hasOffset) {
|
|
584
596
|
// Composite image at given offsets
|
|
585
597
|
std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
|
|
586
598
|
compositeImage.width(), compositeImage.height(), composite->left, composite->top);
|
|
@@ -718,7 +730,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
718
730
|
if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == sharp::ImageType::JPEG)) {
|
|
719
731
|
// Write JPEG to buffer
|
|
720
732
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
721
|
-
VipsArea *area =
|
|
733
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.jpegsave_buffer(VImage::option()
|
|
722
734
|
->set("strip", !baton->withMetadata)
|
|
723
735
|
->set("Q", baton->jpegQuality)
|
|
724
736
|
->set("interlace", baton->jpegProgressive)
|
|
@@ -745,7 +757,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
745
757
|
inputImageType == sharp::ImageType::SVG))) {
|
|
746
758
|
// Write PNG to buffer
|
|
747
759
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
748
|
-
VipsArea *area =
|
|
760
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.pngsave_buffer(VImage::option()
|
|
749
761
|
->set("strip", !baton->withMetadata)
|
|
750
762
|
->set("interlace", baton->pngProgressive)
|
|
751
763
|
->set("compression", baton->pngCompressionLevel)
|
|
@@ -763,7 +775,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
763
775
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::WEBP)) {
|
|
764
776
|
// Write WEBP to buffer
|
|
765
777
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::WEBP);
|
|
766
|
-
VipsArea *area =
|
|
778
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.webpsave_buffer(VImage::option()
|
|
767
779
|
->set("strip", !baton->withMetadata)
|
|
768
780
|
->set("Q", baton->webpQuality)
|
|
769
781
|
->set("lossless", baton->webpLossless)
|
|
@@ -780,7 +792,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
780
792
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF && supportsGifOutput)) {
|
|
781
793
|
// Write GIF to buffer
|
|
782
794
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
783
|
-
VipsArea *area =
|
|
795
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.magicksave_buffer(VImage::option()
|
|
784
796
|
->set("strip", !baton->withMetadata)
|
|
785
797
|
->set("optimize_gif_frames", TRUE)
|
|
786
798
|
->set("optimize_gif_transparency", TRUE)
|
|
@@ -801,7 +813,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
801
813
|
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
|
|
802
814
|
image = image.cast(VIPS_FORMAT_FLOAT);
|
|
803
815
|
}
|
|
804
|
-
VipsArea *area =
|
|
816
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.tiffsave_buffer(VImage::option()
|
|
805
817
|
->set("strip", !baton->withMetadata)
|
|
806
818
|
->set("Q", baton->tiffQuality)
|
|
807
819
|
->set("bitdepth", baton->tiffBitdepth)
|
|
@@ -821,10 +833,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
821
833
|
} else if (baton->formatOut == "heif" ||
|
|
822
834
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
|
|
823
835
|
// Write HEIF to buffer
|
|
824
|
-
VipsArea *area =
|
|
836
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
|
825
837
|
->set("strip", !baton->withMetadata)
|
|
826
838
|
->set("compression", baton->heifCompression)
|
|
827
839
|
->set("Q", baton->heifQuality)
|
|
840
|
+
->set("speed", baton->heifSpeed)
|
|
841
|
+
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
842
|
+
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
843
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
844
|
+
#endif
|
|
828
845
|
->set("lossless", baton->heifLossless)));
|
|
829
846
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
830
847
|
baton->bufferOutLength = area->length;
|
|
@@ -873,7 +890,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
873
890
|
bool const isV = sharp::IsV(baton->fileOut);
|
|
874
891
|
bool const mightMatchInput = baton->formatOut == "input";
|
|
875
892
|
bool const willMatchInput = mightMatchInput &&
|
|
876
|
-
!(isJpeg || isPng || isWebp || isGif || isTiff || isDz || isDzZip || isV);
|
|
893
|
+
!(isJpeg || isPng || isWebp || isGif || isTiff || isHeif || isDz || isDzZip || isV);
|
|
877
894
|
|
|
878
895
|
if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
|
|
879
896
|
(willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
|
|
@@ -938,6 +955,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
938
955
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
939
956
|
baton->channels = std::min(baton->channels, 3);
|
|
940
957
|
}
|
|
958
|
+
// Cast pixel values to float, if required
|
|
959
|
+
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
|
|
960
|
+
image = image.cast(VIPS_FORMAT_FLOAT);
|
|
961
|
+
}
|
|
941
962
|
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
942
963
|
->set("strip", !baton->withMetadata)
|
|
943
964
|
->set("Q", baton->tiffQuality)
|
|
@@ -954,13 +975,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
954
975
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
|
955
976
|
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
|
956
977
|
// Write HEIF to file
|
|
957
|
-
if (sharp::IsAvif(baton->fileOut)) {
|
|
958
|
-
baton->heifCompression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
|
|
959
|
-
}
|
|
960
978
|
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
961
979
|
->set("strip", !baton->withMetadata)
|
|
962
980
|
->set("Q", baton->heifQuality)
|
|
963
981
|
->set("compression", baton->heifCompression)
|
|
982
|
+
->set("speed", baton->heifSpeed)
|
|
983
|
+
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
984
|
+
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
985
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
986
|
+
#endif
|
|
964
987
|
->set("lossless", baton->heifLossless));
|
|
965
988
|
baton->formatOut = "heif";
|
|
966
989
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
|
@@ -1014,6 +1037,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1014
1037
|
->set("suffix", const_cast<char*>(suffix.data()))
|
|
1015
1038
|
->set("angle", CalculateAngleRotation(baton->tileAngle))
|
|
1016
1039
|
->set("background", baton->tileBackground)
|
|
1040
|
+
->set("centre", baton->tileCentre)
|
|
1017
1041
|
->set("skip_blanks", baton->tileSkipBlanks);
|
|
1018
1042
|
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
|
|
1019
1043
|
// not passing anything - libvips will handle choice
|
|
@@ -1241,6 +1265,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1241
1265
|
composite->gravity = sharp::AttrAsUint32(compositeObject, "gravity");
|
|
1242
1266
|
composite->left = sharp::AttrAsInt32(compositeObject, "left");
|
|
1243
1267
|
composite->top = sharp::AttrAsInt32(compositeObject, "top");
|
|
1268
|
+
composite->hasOffset = sharp::AttrAsBool(compositeObject, "hasOffset");
|
|
1244
1269
|
composite->tile = sharp::AttrAsBool(compositeObject, "tile");
|
|
1245
1270
|
composite->premultiplied = sharp::AttrAsBool(compositeObject, "premultiplied");
|
|
1246
1271
|
baton->composite.push_back(composite);
|
|
@@ -1248,7 +1273,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1248
1273
|
// Resize options
|
|
1249
1274
|
baton->withoutEnlargement = sharp::AttrAsBool(options, "withoutEnlargement");
|
|
1250
1275
|
baton->position = sharp::AttrAsInt32(options, "position");
|
|
1251
|
-
baton->resizeBackground = sharp::
|
|
1276
|
+
baton->resizeBackground = sharp::AttrAsVectorOfDouble(options, "resizeBackground");
|
|
1252
1277
|
baton->kernel = sharp::AttrAsStr(options, "kernel");
|
|
1253
1278
|
baton->fastShrinkOnLoad = sharp::AttrAsBool(options, "fastShrinkOnLoad");
|
|
1254
1279
|
// Join Channel Options
|
|
@@ -1261,7 +1286,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1261
1286
|
}
|
|
1262
1287
|
// Operators
|
|
1263
1288
|
baton->flatten = sharp::AttrAsBool(options, "flatten");
|
|
1264
|
-
baton->flattenBackground = sharp::
|
|
1289
|
+
baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
|
|
1265
1290
|
baton->negate = sharp::AttrAsBool(options, "negate");
|
|
1266
1291
|
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
|
|
1267
1292
|
baton->brightness = sharp::AttrAsDouble(options, "brightness");
|
|
@@ -1283,7 +1308,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1283
1308
|
baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
|
|
1284
1309
|
baton->angle = sharp::AttrAsInt32(options, "angle");
|
|
1285
1310
|
baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
|
|
1286
|
-
baton->rotationBackground = sharp::
|
|
1311
|
+
baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
|
|
1287
1312
|
baton->rotateBeforePreExtract = sharp::AttrAsBool(options, "rotateBeforePreExtract");
|
|
1288
1313
|
baton->flip = sharp::AttrAsBool(options, "flip");
|
|
1289
1314
|
baton->flop = sharp::AttrAsBool(options, "flop");
|
|
@@ -1291,8 +1316,15 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1291
1316
|
baton->extendBottom = sharp::AttrAsInt32(options, "extendBottom");
|
|
1292
1317
|
baton->extendLeft = sharp::AttrAsInt32(options, "extendLeft");
|
|
1293
1318
|
baton->extendRight = sharp::AttrAsInt32(options, "extendRight");
|
|
1294
|
-
baton->extendBackground = sharp::
|
|
1319
|
+
baton->extendBackground = sharp::AttrAsVectorOfDouble(options, "extendBackground");
|
|
1295
1320
|
baton->extractChannel = sharp::AttrAsInt32(options, "extractChannel");
|
|
1321
|
+
baton->affineMatrix = sharp::AttrAsVectorOfDouble(options, "affineMatrix");
|
|
1322
|
+
baton->affineBackground = sharp::AttrAsVectorOfDouble(options, "affineBackground");
|
|
1323
|
+
baton->affineIdx = sharp::AttrAsDouble(options, "affineIdx");
|
|
1324
|
+
baton->affineIdy = sharp::AttrAsDouble(options, "affineIdy");
|
|
1325
|
+
baton->affineOdx = sharp::AttrAsDouble(options, "affineOdx");
|
|
1326
|
+
baton->affineOdy = sharp::AttrAsDouble(options, "affineOdy");
|
|
1327
|
+
baton->affineInterpolator = vips::VInterpolate::new_from_name(sharp::AttrAsStr(options, "affineInterpolator").data());
|
|
1296
1328
|
|
|
1297
1329
|
baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
|
|
1298
1330
|
baton->ensureAlpha = sharp::AttrAsBool(options, "ensureAlpha");
|
|
@@ -1375,6 +1407,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1375
1407
|
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
|
1376
1408
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
|
1377
1409
|
sharp::AttrAsStr(options, "heifCompression").data()));
|
|
1410
|
+
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
|
|
1411
|
+
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
|
1378
1412
|
|
|
1379
1413
|
// Animated output
|
|
1380
1414
|
if (sharp::HasAttr(options, "pageHeight")) {
|
|
@@ -1391,7 +1425,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1391
1425
|
baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
|
|
1392
1426
|
baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
|
|
1393
1427
|
baton->tileAngle = sharp::AttrAsInt32(options, "tileAngle");
|
|
1394
|
-
baton->tileBackground = sharp::
|
|
1428
|
+
baton->tileBackground = sharp::AttrAsVectorOfDouble(options, "tileBackground");
|
|
1395
1429
|
baton->tileSkipBlanks = sharp::AttrAsInt32(options, "tileSkipBlanks");
|
|
1396
1430
|
baton->tileContainer = static_cast<VipsForeignDzContainer>(
|
|
1397
1431
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_CONTAINER,
|
|
@@ -1403,6 +1437,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1403
1437
|
baton->tileDepth = static_cast<VipsForeignDzDepth>(
|
|
1404
1438
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH,
|
|
1405
1439
|
sharp::AttrAsStr(options, "tileDepth").data()));
|
|
1440
|
+
baton->tileCentre = sharp::AttrAsBool(options, "tileCentre");
|
|
1406
1441
|
|
|
1407
1442
|
// Force random access for certain operations
|
|
1408
1443
|
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
|