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/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
- if (!fileOut || fileOut.length === 0) {
50
- const errOutputInvalid = new Error('Missing output file path');
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(errOutputInvalid);
62
+ callback(err);
53
63
  } else {
54
- return Promise.reject(errOutputInvalid);
64
+ return Promise.reject(err);
55
65
  }
56
66
  } else {
57
- if (this.options.input.file === fileOut) {
58
- const errOutputIsInput = new Error('Cannot use same file for input and output');
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 RAW output are supported.
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 new Error('The gif operation requires libvips to have been installed with support for ImageMagick');
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 {boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
475
- * @param {boolean} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
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 {boolean} [options.tileWidth=256] - horizontal tile size
479
- * @param {boolean} [options.tileHeight=256] - vertical tile size
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 {boolean} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
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 HEIF options for output image.
575
+ * Use these AVIF options for output image.
561
576
  *
562
- * Support for HEIF (HEIC/AVIF) is experimental.
563
- * Do not use this in production systems.
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
- * Requires a custom, globally-installed libvips compiled with support for libheif.
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
- * Most versions of libheif support only the patent-encumbered HEVC compression format.
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=80] - quality, integer 1-100
573
- * @param {boolean} [options.compression='hevc'] - compression format: hevc, avc, jpeg, av1
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, ['hevc', 'avc', 'jpeg', 'av1'])) {
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: hevc, avc, jpeg, av1', options.compression);
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
- cache,
142
- concurrency,
143
- counters,
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.26.2",
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 && node install/prebuild-ci",
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 && for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done && node docs/search-index/build",
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
- "color": "^3.1.2",
120
+ "array-flatten": "^3.0.0",
121
+ "color": "^3.1.3",
116
122
  "detect-libc": "^1.0.3",
117
- "node-addon-api": "^3.0.2",
123
+ "node-addon-api": "^3.1.0",
118
124
  "npmlog": "^4.1.2",
119
- "prebuild-install": "^5.3.5",
120
- "semver": "^7.3.2",
125
+ "prebuild-install": "^6.0.1",
126
+ "semver": "^7.3.4",
121
127
  "simple-get": "^4.0.0",
122
- "tar-fs": "^2.1.0",
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": "^2.0.1",
128
- "decompress-zip": "^0.3.2",
129
- "documentation": "^13.0.2",
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.1.1",
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": "^14.2.3"
144
+ "semistandard": "^16.0.0"
139
145
  },
140
146
  "license": "Apache-2.0",
141
147
  "config": {
142
- "libvips": "8.10.0",
148
+ "libvips": "8.10.5",
143
149
  "runtime": "napi",
144
150
  "target": 3
145
151
  },
146
152
  "engines": {
147
- "node": ">=10.16.0"
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> AttrAsRgba(Napi::Object obj, std::string attr) {
58
- Napi::Array background = obj.Get(attr).As<Napi::Array>();
59
- std::vector<double> rgba(background.Length());
60
- for (unsigned int i = 0; i < background.Length(); i++) {
61
- rgba[i] = AttrAsDouble(background, i);
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 rgba;
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
- descriptor->createBackground = AttrAsRgba(input, "createBackground");
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
- { "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 }
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(vips_nickname_find(g_type_from_name(load)));
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(vips_nickname_find(g_type_from_name(load)));
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
- std::vector<double> background = {
322
- descriptor->createBackground[0],
323
- descriptor->createBackground[1],
324
- descriptor->createBackground[2]
325
- };
326
- if (descriptor->createChannels == 4) {
327
- background.push_back(descriptor->createBackground[3]);
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
- int const bands = image.bands();
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 >= 0 && x < (inWidth - outWidth)) {
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 >= 0 && y < (inHeight - outHeight)) {
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 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10))
28
- #error "libvips version 8.10.0+ is required - please see https://sharp.pixelplumbing.com/install"
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> AttrAsRgba(Napi::Object obj, std::string attr);
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
@@ -39,6 +39,7 @@ struct MetadataBaton {
39
39
  int loop;
40
40
  std::vector<int> delay;
41
41
  int pagePrimary;
42
+ std::string compression;
42
43
  std::vector<std::pair<int, int>> levels;
43
44
  bool hasProfile;
44
45
  bool hasAlpha;
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