sharp 0.26.2 → 0.26.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
16
16
  As well as image resizing, operations such as
17
17
  rotation, extraction, compositing and gamma correction are available.
18
18
 
19
- Most modern macOS, Windows and Linux systems running Node.js v10.16.0+
19
+ Most modern macOS, Windows and Linux systems running Node.js v10+
20
20
  do not require any additional install or runtime dependencies.
21
21
 
22
22
  ## Examples
@@ -25,6 +25,7 @@ const minimumGlibcVersionByArch = {
25
25
  const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
26
26
  const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
27
27
  const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
28
+ const supportsBrotli = ('BrotliDecompress' in zlib);
28
29
 
29
30
  const fail = function (err) {
30
31
  npmLog.error('sharp', err.message);
@@ -43,7 +44,7 @@ const extractTarball = function (tarPath) {
43
44
  libvips.mkdirSync(versionedVendorPath);
44
45
  stream.pipeline(
45
46
  fs.createReadStream(tarPath),
46
- new zlib.BrotliDecompress(),
47
+ supportsBrotli ? new zlib.BrotliDecompress() : new zlib.Gunzip(),
47
48
  tarFs.extract(versionedVendorPath),
48
49
  function (err) {
49
50
  if (err) {
@@ -58,6 +59,7 @@ const extractTarball = function (tarPath) {
58
59
 
59
60
  try {
60
61
  const useGlobalLibvips = libvips.useGlobalLibvips();
62
+
61
63
  if (useGlobalLibvips) {
62
64
  const globalLibvipsVersion = libvips.globalLibvipsVersion();
63
65
  npmLog.info('sharp', `Detected globally-installed libvips v${globalLibvipsVersion}`);
@@ -86,8 +88,10 @@ try {
86
88
  throw new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`);
87
89
  }
88
90
 
91
+ const extension = supportsBrotli ? 'br' : 'gz';
92
+
89
93
  // Download to per-process temporary file
90
- const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.br';
94
+ const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.' + extension;
91
95
  const tarPathCache = path.join(libvips.cachePath(), tarFilename);
92
96
  if (fs.existsSync(tarPathCache)) {
93
97
  npmLog.info('sharp', `Using cached ${tarPathCache}`);
@@ -155,6 +155,13 @@ const Sharp = function (input, options) {
155
155
  extendRight: 0,
156
156
  extendBackground: [0, 0, 0, 255],
157
157
  withoutEnlargement: false,
158
+ affineMatrix: [],
159
+ affineBackground: [0, 0, 0, 255],
160
+ affineIdx: 0,
161
+ affineIdy: 0,
162
+ affineOdx: 0,
163
+ affineOdy: 0,
164
+ affineInterpolator: this.constructor.interpolators.bilinear,
158
165
  kernel: 'lanczos3',
159
166
  fastShrinkOnLoad: true,
160
167
  // operations
package/lib/operation.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const { flatten: flattenArray } = require('array-flatten');
3
4
  const color = require('color');
4
5
  const is = require('./is');
5
6
 
@@ -82,6 +83,103 @@ function flop (flop) {
82
83
  return this;
83
84
  }
84
85
 
86
+ /**
87
+ * Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.
88
+ *
89
+ * You must provide an array of length 4 or a 2x2 affine transformation matrix.
90
+ * By default, new pixels are filled with a black background. You can provide a background color with the `background` option.
91
+ * A particular interpolator may also be specified. Set the `interpolator` option to an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.nohalo`.
92
+ *
93
+ * In the case of a 2x2 matrix, the transform is:
94
+ * - X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
95
+ * - Y = `matrix[1, 0]` \* (x + `idx`) + `matrix[1, 1]` \* (y + `idy`) + `ody`
96
+ *
97
+ * where:
98
+ * - x and y are the coordinates in input image.
99
+ * - X and Y are the coordinates in output image.
100
+ * - (0,0) is the upper left corner.
101
+ *
102
+ * @since 0.27.0
103
+ *
104
+ * @example
105
+ * const pipeline = sharp()
106
+ * .affine([[1, 0.3], [0.1, 0.7]], {
107
+ * background: 'white',
108
+ * interpolate: sharp.interpolators.nohalo
109
+ * })
110
+ * .toBuffer((err, outputBuffer, info) => {
111
+ * // outputBuffer contains the transformed image
112
+ * // info.width and info.height contain the new dimensions
113
+ * });
114
+ *
115
+ * inputStream
116
+ * .pipe(pipeline);
117
+ *
118
+ * @param {Array<Array<number>>|Array<number>} matrix - affine transformation matrix
119
+ * @param {Object} [options] - if present, is an Object with optional attributes.
120
+ * @param {String|Object} [options.background="#000000"] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
121
+ * @param {Number} [options.idx=0] - input horizontal offset
122
+ * @param {Number} [options.idy=0] - input vertical offset
123
+ * @param {Number} [options.odx=0] - output horizontal offset
124
+ * @param {Number} [options.ody=0] - output vertical offset
125
+ * @param {String} [options.interpolator=sharp.interpolators.bicubic] - interpolator
126
+ * @returns {Sharp}
127
+ * @throws {Error} Invalid parameters
128
+ */
129
+ function affine (matrix, options) {
130
+ const flatMatrix = flattenArray(matrix);
131
+ if (flatMatrix.length === 4 && flatMatrix.every(is.number)) {
132
+ this.options.affineMatrix = flatMatrix;
133
+ } else {
134
+ throw is.invalidParameterError('matrix', '1x4 or 2x2 array', matrix);
135
+ }
136
+
137
+ if (is.defined(options)) {
138
+ if (is.object(options)) {
139
+ this._setBackgroundColourOption('affineBackground', options.background);
140
+ if (is.defined(options.idx)) {
141
+ if (is.number(options.idx)) {
142
+ this.options.affineIdx = options.idx;
143
+ } else {
144
+ throw is.invalidParameterError('options.idx', 'number', options.idx);
145
+ }
146
+ }
147
+ if (is.defined(options.idy)) {
148
+ if (is.number(options.idy)) {
149
+ this.options.affineIdy = options.idy;
150
+ } else {
151
+ throw is.invalidParameterError('options.idy', 'number', options.idy);
152
+ }
153
+ }
154
+ if (is.defined(options.odx)) {
155
+ if (is.number(options.odx)) {
156
+ this.options.affineOdx = options.odx;
157
+ } else {
158
+ throw is.invalidParameterError('options.odx', 'number', options.odx);
159
+ }
160
+ }
161
+ if (is.defined(options.ody)) {
162
+ if (is.number(options.ody)) {
163
+ this.options.affineOdy = options.ody;
164
+ } else {
165
+ throw is.invalidParameterError('options.ody', 'number', options.ody);
166
+ }
167
+ }
168
+ if (is.defined(options.interpolator)) {
169
+ if (is.inArray(options.interpolator, Object.values(this.constructor.interpolators))) {
170
+ this.options.affineInterpolator = options.interpolator;
171
+ } else {
172
+ throw is.invalidParameterError('options.interpolator', 'valid interpolator name', options.interpolator);
173
+ }
174
+ }
175
+ } else {
176
+ throw is.invalidParameterError('options', 'object', options);
177
+ }
178
+ }
179
+
180
+ return this;
181
+ }
182
+
85
183
  /**
86
184
  * Sharpen the image.
87
185
  * When used without parameters, performs a fast, mild sharpen of the output image.
@@ -482,6 +580,7 @@ module.exports = function (Sharp) {
482
580
  rotate,
483
581
  flip,
484
582
  flop,
583
+ affine,
485
584
  sharpen,
486
585
  median,
487
586
  blur,
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
3
  "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
4
- "version": "0.26.2",
4
+ "version": "0.26.3",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -69,7 +69,8 @@
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>"
73
74
  ],
74
75
  "scripts": {
75
76
  "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
@@ -112,30 +113,31 @@
112
113
  "vips"
113
114
  ],
114
115
  "dependencies": {
115
- "color": "^3.1.2",
116
+ "array-flatten": "^3.0.0",
117
+ "color": "^3.1.3",
116
118
  "detect-libc": "^1.0.3",
117
119
  "node-addon-api": "^3.0.2",
118
120
  "npmlog": "^4.1.2",
119
- "prebuild-install": "^5.3.5",
121
+ "prebuild-install": "^6.0.0",
120
122
  "semver": "^7.3.2",
121
123
  "simple-get": "^4.0.0",
122
- "tar-fs": "^2.1.0",
124
+ "tar-fs": "^2.1.1",
123
125
  "tunnel-agent": "^0.6.0"
124
126
  },
125
127
  "devDependencies": {
126
128
  "async": "^3.2.0",
127
129
  "cc": "^2.0.1",
128
130
  "decompress-zip": "^0.3.2",
129
- "documentation": "^13.0.2",
131
+ "documentation": "^13.1.0",
130
132
  "exif-reader": "^1.0.3",
131
133
  "icc": "^2.0.0",
132
134
  "license-checker": "^25.0.1",
133
- "mocha": "^8.1.1",
135
+ "mocha": "^8.2.1",
134
136
  "mock-fs": "^4.13.0",
135
137
  "nyc": "^15.1.0",
136
138
  "prebuild": "^10.0.1",
137
139
  "rimraf": "^3.0.2",
138
- "semistandard": "^14.2.3"
140
+ "semistandard": "^16.0.0"
139
141
  },
140
142
  "license": "Apache-2.0",
141
143
  "config": {
@@ -144,7 +146,7 @@
144
146
  "target": 3
145
147
  },
146
148
  "engines": {
147
- "node": ">=10.16.0"
149
+ "node": ">=10"
148
150
  },
149
151
  "funding": {
150
152
  "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,7 @@ 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
+ descriptor->createBackground = AttrAsVectorOfDouble(input, "createBackground");
113
113
  }
114
114
  // Limit input images to a given number of pixels, where pixels = width * height
115
115
  descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
package/src/common.h CHANGED
@@ -92,7 +92,7 @@ namespace sharp {
92
92
  double AttrAsDouble(Napi::Object obj, std::string attr);
93
93
  double AttrAsDouble(Napi::Object obj, unsigned int const attr);
94
94
  bool AttrAsBool(Napi::Object obj, std::string attr);
95
- std::vector<double> AttrAsRgba(Napi::Object obj, std::string attr);
95
+ std::vector<double> AttrAsVectorOfDouble(Napi::Object obj, std::string attr);
96
96
  std::vector<int32_t> AttrAsInt32Vector(Napi::Object obj, std::string attr);
97
97
 
98
98
  // Create an InputDescriptor instance from a Napi::Object describing an input image
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;
@@ -1249,7 +1261,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1249
1261
  // Resize options
1250
1262
  baton->withoutEnlargement = sharp::AttrAsBool(options, "withoutEnlargement");
1251
1263
  baton->position = sharp::AttrAsInt32(options, "position");
1252
- baton->resizeBackground = sharp::AttrAsRgba(options, "resizeBackground");
1264
+ baton->resizeBackground = sharp::AttrAsVectorOfDouble(options, "resizeBackground");
1253
1265
  baton->kernel = sharp::AttrAsStr(options, "kernel");
1254
1266
  baton->fastShrinkOnLoad = sharp::AttrAsBool(options, "fastShrinkOnLoad");
1255
1267
  // Join Channel Options
@@ -1262,7 +1274,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1262
1274
  }
1263
1275
  // Operators
1264
1276
  baton->flatten = sharp::AttrAsBool(options, "flatten");
1265
- baton->flattenBackground = sharp::AttrAsRgba(options, "flattenBackground");
1277
+ baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
1266
1278
  baton->negate = sharp::AttrAsBool(options, "negate");
1267
1279
  baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
1268
1280
  baton->brightness = sharp::AttrAsDouble(options, "brightness");
@@ -1284,7 +1296,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1284
1296
  baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
1285
1297
  baton->angle = sharp::AttrAsInt32(options, "angle");
1286
1298
  baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
1287
- baton->rotationBackground = sharp::AttrAsRgba(options, "rotationBackground");
1299
+ baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
1288
1300
  baton->rotateBeforePreExtract = sharp::AttrAsBool(options, "rotateBeforePreExtract");
1289
1301
  baton->flip = sharp::AttrAsBool(options, "flip");
1290
1302
  baton->flop = sharp::AttrAsBool(options, "flop");
@@ -1292,8 +1304,15 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1292
1304
  baton->extendBottom = sharp::AttrAsInt32(options, "extendBottom");
1293
1305
  baton->extendLeft = sharp::AttrAsInt32(options, "extendLeft");
1294
1306
  baton->extendRight = sharp::AttrAsInt32(options, "extendRight");
1295
- baton->extendBackground = sharp::AttrAsRgba(options, "extendBackground");
1307
+ baton->extendBackground = sharp::AttrAsVectorOfDouble(options, "extendBackground");
1296
1308
  baton->extractChannel = sharp::AttrAsInt32(options, "extractChannel");
1309
+ baton->affineMatrix = sharp::AttrAsVectorOfDouble(options, "affineMatrix");
1310
+ baton->affineBackground = sharp::AttrAsVectorOfDouble(options, "affineBackground");
1311
+ baton->affineIdx = sharp::AttrAsDouble(options, "affineIdx");
1312
+ baton->affineIdy = sharp::AttrAsDouble(options, "affineIdy");
1313
+ baton->affineOdx = sharp::AttrAsDouble(options, "affineOdx");
1314
+ baton->affineOdy = sharp::AttrAsDouble(options, "affineOdy");
1315
+ baton->affineInterpolator = vips::VInterpolate::new_from_name(sharp::AttrAsStr(options, "affineInterpolator").data());
1297
1316
 
1298
1317
  baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
1299
1318
  baton->ensureAlpha = sharp::AttrAsBool(options, "ensureAlpha");
@@ -1392,7 +1411,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1392
1411
  baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
1393
1412
  baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
1394
1413
  baton->tileAngle = sharp::AttrAsInt32(options, "tileAngle");
1395
- baton->tileBackground = sharp::AttrAsRgba(options, "tileBackground");
1414
+ baton->tileBackground = sharp::AttrAsVectorOfDouble(options, "tileBackground");
1396
1415
  baton->tileSkipBlanks = sharp::AttrAsInt32(options, "tileSkipBlanks");
1397
1416
  baton->tileContainer = static_cast<VipsForeignDzContainer>(
1398
1417
  vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_CONTAINER,
package/src/pipeline.h CHANGED
@@ -119,6 +119,13 @@ struct PipelineBaton {
119
119
  int extendRight;
120
120
  std::vector<double> extendBackground;
121
121
  bool withoutEnlargement;
122
+ std::vector<double> affineMatrix;
123
+ std::vector<double> affineBackground;
124
+ double affineIdx;
125
+ double affineIdy;
126
+ double affineOdx;
127
+ double affineOdy;
128
+ vips::VInterpolate affineInterpolator;
122
129
  int jpegQuality;
123
130
  bool jpegProgressive;
124
131
  std::string jpegChromaSubsampling;
@@ -231,6 +238,13 @@ struct PipelineBaton {
231
238
  extendRight(0),
232
239
  extendBackground{ 0.0, 0.0, 0.0, 255.0 },
233
240
  withoutEnlargement(false),
241
+ affineMatrix{ 1.0, 0.0, 0.0, 1.0 },
242
+ affineBackground{ 0.0, 0.0, 0.0, 255.0 },
243
+ affineIdx(0),
244
+ affineIdy(0),
245
+ affineOdx(0),
246
+ affineOdy(0),
247
+ affineInterpolator(vips::VInterpolate::new_from_name("bicubic")),
234
248
  jpegQuality(80),
235
249
  jpegProgressive(false),
236
250
  jpegChromaSubsampling("4:2:0"),