sharp 0.20.8 → 0.21.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 +13 -11
- package/binding.gyp +16 -1
- package/docs/api-channel.md +16 -0
- package/docs/api-colour.md +1 -19
- package/docs/api-composite.md +2 -3
- package/docs/api-constructor.md +2 -3
- package/docs/api-input.md +7 -2
- package/docs/api-operation.md +73 -115
- package/docs/api-output.md +9 -0
- package/docs/api-resize.md +160 -108
- package/docs/api-utility.md +4 -7
- package/docs/changelog.md +102 -0
- package/docs/image/sharp-logo.svg +5 -0
- package/docs/index.md +12 -6
- package/docs/install.md +41 -42
- package/docs/performance.md +14 -21
- package/install/libvips.js +28 -24
- package/lib/channel.js +20 -4
- package/lib/colour.js +35 -18
- package/lib/composite.js +2 -3
- package/lib/constructor.js +27 -42
- package/lib/input.js +9 -6
- package/lib/libvips.js +7 -1
- package/lib/operation.js +81 -123
- package/lib/output.js +128 -60
- package/lib/platform.js +4 -1
- package/lib/resize.js +310 -107
- package/lib/utility.js +5 -8
- package/package.json +30 -21
- package/src/common.cc +59 -14
- package/src/common.h +15 -12
- package/src/libvips/cplusplus/VImage.cpp +95 -95
- package/src/libvips/cplusplus/vips-operators.cpp +268 -185
- package/src/metadata.cc +34 -1
- package/src/metadata.h +10 -1
- package/src/operations.cc +47 -49
- package/src/operations.h +13 -2
- package/src/pipeline.cc +101 -95
- package/src/pipeline.h +40 -14
- package/src/sharp.cc +1 -1
- package/src/stats.cc +1 -1
- package/src/stats.h +1 -1
- package/src/utilities.cc +2 -2
- package/src/utilities.h +1 -1
package/lib/resize.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const deprecate = require('util').deprecate;
|
|
3
4
|
const is = require('./is');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* Weighting to apply
|
|
7
|
+
* Weighting to apply when using contain/cover fit.
|
|
7
8
|
* @member
|
|
8
9
|
* @private
|
|
9
10
|
*/
|
|
@@ -21,7 +22,23 @@ const gravity = {
|
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
+
* Position to apply when using contain/cover fit.
|
|
26
|
+
* @member
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
const position = {
|
|
30
|
+
top: 1,
|
|
31
|
+
right: 2,
|
|
32
|
+
bottom: 3,
|
|
33
|
+
left: 4,
|
|
34
|
+
'right top': 5,
|
|
35
|
+
'right bottom': 6,
|
|
36
|
+
'left bottom': 7,
|
|
37
|
+
'left top': 8
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Strategies for automagic cover behaviour.
|
|
25
42
|
* @member
|
|
26
43
|
* @private
|
|
27
44
|
*/
|
|
@@ -38,45 +55,144 @@ const strategy = {
|
|
|
38
55
|
const kernel = {
|
|
39
56
|
nearest: 'nearest',
|
|
40
57
|
cubic: 'cubic',
|
|
58
|
+
mitchell: 'mitchell',
|
|
41
59
|
lanczos2: 'lanczos2',
|
|
42
60
|
lanczos3: 'lanczos3'
|
|
43
61
|
};
|
|
44
62
|
|
|
45
63
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
64
|
+
* Methods by which an image can be resized to fit the provided dimensions.
|
|
65
|
+
* @member
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
const fit = {
|
|
69
|
+
contain: 'contain',
|
|
70
|
+
cover: 'cover',
|
|
71
|
+
fill: 'fill',
|
|
72
|
+
inside: 'inside',
|
|
73
|
+
outside: 'outside'
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Map external fit property to internal canvas property.
|
|
78
|
+
* @member
|
|
79
|
+
* @private
|
|
80
|
+
*/
|
|
81
|
+
const mapFitToCanvas = {
|
|
82
|
+
contain: 'embed',
|
|
83
|
+
cover: 'crop',
|
|
84
|
+
fill: 'ignore_aspect',
|
|
85
|
+
inside: 'max',
|
|
86
|
+
outside: 'min'
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resize image to `width`, `height` or `width x height`.
|
|
91
|
+
*
|
|
92
|
+
* When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
|
|
93
|
+
* - `cover`: Crop to cover both provided dimensions (the default).
|
|
94
|
+
* - `contain`: Embed within both provided dimensions.
|
|
95
|
+
* - `fill`: Ignore the aspect ratio of the input and stretch to both provided dimensions.
|
|
96
|
+
* - `inside`: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
|
|
97
|
+
* - `outside`: Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified.
|
|
98
|
+
* Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
|
|
99
|
+
*
|
|
100
|
+
* When using a `fit` of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
|
101
|
+
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
|
102
|
+
* - `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
|
|
103
|
+
* - `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
|
|
104
|
+
* Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property.
|
|
105
|
+
*
|
|
106
|
+
* The experimental strategy-based approach resizes so one dimension is at its target length
|
|
107
|
+
* then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
|
|
108
|
+
* - `entropy`: focus on the region with the highest [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29).
|
|
109
|
+
* - `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
|
|
48
110
|
*
|
|
49
|
-
* Possible kernels are:
|
|
111
|
+
* Possible interpolation kernels are:
|
|
50
112
|
* - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
|
|
51
113
|
* - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
|
|
114
|
+
* - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
|
|
52
115
|
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
|
|
53
116
|
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
|
54
117
|
*
|
|
55
118
|
* @example
|
|
56
|
-
* sharp(
|
|
119
|
+
* sharp(input)
|
|
120
|
+
* .resize({ width: 100 })
|
|
121
|
+
* .toBuffer()
|
|
122
|
+
* .then(data => {
|
|
123
|
+
* // 100 pixels wide, auto-scaled height
|
|
124
|
+
* });
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* sharp(input)
|
|
128
|
+
* .resize({ height: 100 })
|
|
129
|
+
* .toBuffer()
|
|
130
|
+
* .then(data => {
|
|
131
|
+
* // 100 pixels high, auto-scaled width
|
|
132
|
+
* });
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* sharp(input)
|
|
57
136
|
* .resize(200, 300, {
|
|
58
|
-
* kernel: sharp.kernel.nearest
|
|
137
|
+
* kernel: sharp.kernel.nearest,
|
|
138
|
+
* fit: 'contain',
|
|
139
|
+
* position: 'right top',
|
|
140
|
+
* background: { r: 255, g: 255, b: 255, alpha: 0.5 }
|
|
141
|
+
* })
|
|
142
|
+
* .toFile('output.png')
|
|
143
|
+
* .then(() => {
|
|
144
|
+
* // output.png is a 200 pixels wide and 300 pixels high image
|
|
145
|
+
* // containing a nearest-neighbour scaled version
|
|
146
|
+
* // contained within the north-east corner of a semi-transparent white canvas
|
|
147
|
+
* });
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* const transformer = sharp()
|
|
151
|
+
* .resize({
|
|
152
|
+
* width: 200,
|
|
153
|
+
* height: 200,
|
|
154
|
+
* fit: sharp.fit.cover,
|
|
155
|
+
* position: sharp.strategy.entropy
|
|
156
|
+
* });
|
|
157
|
+
* // Read image data from readableStream
|
|
158
|
+
* // Write 200px square auto-cropped image data to writableStream
|
|
159
|
+
* readableStream
|
|
160
|
+
* .pipe(transformer)
|
|
161
|
+
* .pipe(writableStream);
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* sharp(input)
|
|
165
|
+
* .resize(200, 200, {
|
|
166
|
+
* fit: sharp.fit.inside,
|
|
167
|
+
* withoutEnlargement: true
|
|
59
168
|
* })
|
|
60
|
-
* .
|
|
61
|
-
* .
|
|
62
|
-
* .
|
|
63
|
-
*
|
|
64
|
-
* //
|
|
65
|
-
* //
|
|
66
|
-
* // of the image data in inputBuffer
|
|
169
|
+
* .toFormat('jpeg')
|
|
170
|
+
* .toBuffer()
|
|
171
|
+
* .then(function(outputBuffer) {
|
|
172
|
+
* // outputBuffer contains JPEG image data
|
|
173
|
+
* // no wider and no higher than 200 pixels
|
|
174
|
+
* // and no larger than the input image
|
|
67
175
|
* });
|
|
68
176
|
*
|
|
69
177
|
* @param {Number} [width] - pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
|
|
70
178
|
* @param {Number} [height] - pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
|
|
71
179
|
* @param {Object} [options]
|
|
180
|
+
* @param {String} [options.width] - alternative means of specifying `width`. If both are present this take priority.
|
|
181
|
+
* @param {String} [options.height] - alternative means of specifying `height`. If both are present this take priority.
|
|
182
|
+
* @param {String} [options.fit='cover'] - how the image should be resized to fit both provided dimensions, one of `cover`, `contain`, `fill`, `inside` or `outside`.
|
|
183
|
+
* @param {String} [options.position='centre'] - position, gravity or strategy to use when `fit` is `cover` or `contain`.
|
|
184
|
+
* @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when using a `fit` of `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
|
|
72
185
|
* @param {String} [options.kernel='lanczos3'] - the kernel to use for image reduction.
|
|
186
|
+
* @param {Boolean} [options.withoutEnlargement=false] - do not enlarge if the width *or* height are already less than the specified dimensions, equivalent to GraphicsMagick's `>` geometry option.
|
|
73
187
|
* @param {Boolean} [options.fastShrinkOnLoad=true] - take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern on some images.
|
|
74
188
|
* @returns {Sharp}
|
|
75
189
|
* @throws {Error} Invalid parameters
|
|
76
190
|
*/
|
|
77
191
|
function resize (width, height, options) {
|
|
78
192
|
if (is.defined(width)) {
|
|
79
|
-
if (is.
|
|
193
|
+
if (is.object(width) && !is.defined(options)) {
|
|
194
|
+
options = width;
|
|
195
|
+
} else if (is.integer(width) && width > 0) {
|
|
80
196
|
this.options.width = width;
|
|
81
197
|
} else {
|
|
82
198
|
throw is.invalidParameterError('width', 'positive integer', width);
|
|
@@ -94,6 +210,38 @@ function resize (width, height, options) {
|
|
|
94
210
|
this.options.height = -1;
|
|
95
211
|
}
|
|
96
212
|
if (is.object(options)) {
|
|
213
|
+
// Width
|
|
214
|
+
if (is.integer(options.width) && options.width > 0) {
|
|
215
|
+
this.options.width = options.width;
|
|
216
|
+
}
|
|
217
|
+
// Height
|
|
218
|
+
if (is.integer(options.height) && options.height > 0) {
|
|
219
|
+
this.options.height = options.height;
|
|
220
|
+
}
|
|
221
|
+
// Fit
|
|
222
|
+
if (is.defined(options.fit)) {
|
|
223
|
+
const canvas = mapFitToCanvas[options.fit];
|
|
224
|
+
if (is.string(canvas)) {
|
|
225
|
+
this.options.canvas = canvas;
|
|
226
|
+
} else {
|
|
227
|
+
throw is.invalidParameterError('fit', 'valid fit', options.fit);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Position
|
|
231
|
+
if (is.defined(options.position)) {
|
|
232
|
+
const pos = is.integer(options.position)
|
|
233
|
+
? options.position
|
|
234
|
+
: strategy[options.position] || position[options.position] || gravity[options.position];
|
|
235
|
+
if (is.integer(pos) && (is.inRange(pos, 0, 8) || is.inRange(pos, 16, 17))) {
|
|
236
|
+
this.options.position = pos;
|
|
237
|
+
} else {
|
|
238
|
+
throw is.invalidParameterError('position', 'valid position/gravity/strategy', options.position);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Background
|
|
242
|
+
if (is.defined(options.background)) {
|
|
243
|
+
this._setColourOption('resizeBackground', options.background);
|
|
244
|
+
}
|
|
97
245
|
// Kernel
|
|
98
246
|
if (is.defined(options.kernel)) {
|
|
99
247
|
if (is.string(kernel[options.kernel])) {
|
|
@@ -102,6 +250,10 @@ function resize (width, height, options) {
|
|
|
102
250
|
throw is.invalidParameterError('kernel', 'valid kernel name', options.kernel);
|
|
103
251
|
}
|
|
104
252
|
}
|
|
253
|
+
// Without enlargement
|
|
254
|
+
if (is.defined(options.withoutEnlargement)) {
|
|
255
|
+
this._setBooleanOption('withoutEnlargement', options.withoutEnlargement);
|
|
256
|
+
}
|
|
105
257
|
// Shrink on load
|
|
106
258
|
if (is.defined(options.fastShrinkOnLoad)) {
|
|
107
259
|
this._setBooleanOption('fastShrinkOnLoad', options.fastShrinkOnLoad);
|
|
@@ -111,48 +263,144 @@ function resize (width, height, options) {
|
|
|
111
263
|
}
|
|
112
264
|
|
|
113
265
|
/**
|
|
114
|
-
*
|
|
266
|
+
* Extends/pads the edges of the image with the provided background colour.
|
|
267
|
+
* This operation will always occur after resizing and extraction, if any.
|
|
115
268
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
269
|
+
* @example
|
|
270
|
+
* // Resize to 140 pixels wide, then add 10 transparent pixels
|
|
271
|
+
* // to the top, left and right edges and 20 to the bottom edge
|
|
272
|
+
* sharp(input)
|
|
273
|
+
* .resize(140)
|
|
274
|
+
* .extend({
|
|
275
|
+
* top: 10,
|
|
276
|
+
* bottom: 20,
|
|
277
|
+
* left: 10,
|
|
278
|
+
* right: 10
|
|
279
|
+
* background: { r: 0, g: 0, b: 0, alpha: 0 }
|
|
280
|
+
* })
|
|
281
|
+
* ...
|
|
118
282
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
283
|
+
* @param {(Number|Object)} extend - single pixel count to add to all edges or an Object with per-edge counts
|
|
284
|
+
* @param {Number} [extend.top]
|
|
285
|
+
* @param {Number} [extend.left]
|
|
286
|
+
* @param {Number} [extend.bottom]
|
|
287
|
+
* @param {Number} [extend.right]
|
|
288
|
+
* @param {String|Object} [extend.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
|
|
289
|
+
* @returns {Sharp}
|
|
290
|
+
* @throws {Error} Invalid parameters
|
|
291
|
+
*/
|
|
292
|
+
function extend (extend) {
|
|
293
|
+
if (is.integer(extend) && extend > 0) {
|
|
294
|
+
this.options.extendTop = extend;
|
|
295
|
+
this.options.extendBottom = extend;
|
|
296
|
+
this.options.extendLeft = extend;
|
|
297
|
+
this.options.extendRight = extend;
|
|
298
|
+
} else if (
|
|
299
|
+
is.object(extend) &&
|
|
300
|
+
is.integer(extend.top) && extend.top >= 0 &&
|
|
301
|
+
is.integer(extend.bottom) && extend.bottom >= 0 &&
|
|
302
|
+
is.integer(extend.left) && extend.left >= 0 &&
|
|
303
|
+
is.integer(extend.right) && extend.right >= 0
|
|
304
|
+
) {
|
|
305
|
+
this.options.extendTop = extend.top;
|
|
306
|
+
this.options.extendBottom = extend.bottom;
|
|
307
|
+
this.options.extendLeft = extend.left;
|
|
308
|
+
this.options.extendRight = extend.right;
|
|
309
|
+
this._setColourOption('extendBackground', extend.background);
|
|
310
|
+
} else {
|
|
311
|
+
throw new Error('Invalid edge extension ' + extend);
|
|
312
|
+
}
|
|
313
|
+
return this;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Extract a region of the image.
|
|
318
|
+
*
|
|
319
|
+
* - Use `extract` before `resize` for pre-resize extraction.
|
|
320
|
+
* - Use `extract` after `resize` for post-resize extraction.
|
|
321
|
+
* - Use `extract` before and after for both.
|
|
123
322
|
*
|
|
124
323
|
* @example
|
|
125
|
-
*
|
|
126
|
-
* .
|
|
127
|
-
* .
|
|
128
|
-
*
|
|
129
|
-
*
|
|
324
|
+
* sharp(input)
|
|
325
|
+
* .extract({ left: left, top: top, width: width, height: height })
|
|
326
|
+
* .toFile(output, function(err) {
|
|
327
|
+
* // Extract a region of the input image, saving in the same format.
|
|
328
|
+
* });
|
|
329
|
+
* @example
|
|
330
|
+
* sharp(input)
|
|
331
|
+
* .extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
|
|
332
|
+
* .resize(width, height)
|
|
333
|
+
* .extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
|
|
334
|
+
* .toFile(output, function(err) {
|
|
335
|
+
* // Extract a region, resize, then extract from the resized image
|
|
130
336
|
* });
|
|
131
|
-
* // Read image data from readableStream
|
|
132
|
-
* // Write 200px square auto-cropped image data to writableStream
|
|
133
|
-
* readableStream.pipe(transformer).pipe(writableStream);
|
|
134
337
|
*
|
|
135
|
-
* @param {
|
|
338
|
+
* @param {Object} options
|
|
339
|
+
* @param {Number} options.left - zero-indexed offset from left edge
|
|
340
|
+
* @param {Number} options.top - zero-indexed offset from top edge
|
|
341
|
+
* @param {Number} options.width - dimension of extracted image
|
|
342
|
+
* @param {Number} options.height - dimension of extracted image
|
|
136
343
|
* @returns {Sharp}
|
|
137
344
|
* @throws {Error} Invalid parameters
|
|
138
345
|
*/
|
|
346
|
+
function extract (options) {
|
|
347
|
+
const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
|
|
348
|
+
['left', 'top', 'width', 'height'].forEach(function (name) {
|
|
349
|
+
const value = options[name];
|
|
350
|
+
if (is.integer(value) && value >= 0) {
|
|
351
|
+
this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
|
|
352
|
+
} else {
|
|
353
|
+
throw new Error('Non-integer value for ' + name + ' of ' + value);
|
|
354
|
+
}
|
|
355
|
+
}, this);
|
|
356
|
+
// Ensure existing rotation occurs before pre-resize extraction
|
|
357
|
+
if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true)) {
|
|
358
|
+
this.options.rotateBeforePreExtract = true;
|
|
359
|
+
}
|
|
360
|
+
return this;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
|
|
365
|
+
* The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
|
366
|
+
* @param {Number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero.
|
|
367
|
+
* @returns {Sharp}
|
|
368
|
+
* @throws {Error} Invalid parameters
|
|
369
|
+
*/
|
|
370
|
+
function trim (threshold) {
|
|
371
|
+
if (!is.defined(threshold)) {
|
|
372
|
+
this.options.trimThreshold = 10;
|
|
373
|
+
} else if (is.number(threshold) && threshold > 0) {
|
|
374
|
+
this.options.trimThreshold = threshold;
|
|
375
|
+
} else {
|
|
376
|
+
throw is.invalidParameterError('threshold', 'number greater than zero', threshold);
|
|
377
|
+
}
|
|
378
|
+
return this;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Deprecated functions
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* @deprecated
|
|
385
|
+
* @private
|
|
386
|
+
*/
|
|
139
387
|
function crop (crop) {
|
|
140
388
|
this.options.canvas = 'crop';
|
|
141
389
|
if (!is.defined(crop)) {
|
|
142
390
|
// Default
|
|
143
|
-
this.options.
|
|
391
|
+
this.options.position = gravity.center;
|
|
144
392
|
} else if (is.integer(crop) && is.inRange(crop, 0, 8)) {
|
|
145
393
|
// Gravity (numeric)
|
|
146
|
-
this.options.
|
|
394
|
+
this.options.position = crop;
|
|
147
395
|
} else if (is.string(crop) && is.integer(gravity[crop])) {
|
|
148
396
|
// Gravity (string)
|
|
149
|
-
this.options.
|
|
397
|
+
this.options.position = gravity[crop];
|
|
150
398
|
} else if (is.integer(crop) && crop >= strategy.entropy) {
|
|
151
399
|
// Strategy
|
|
152
|
-
this.options.
|
|
400
|
+
this.options.position = crop;
|
|
153
401
|
} else if (is.string(crop) && is.integer(strategy[crop])) {
|
|
154
402
|
// Strategy (string)
|
|
155
|
-
this.options.
|
|
403
|
+
this.options.position = strategy[crop];
|
|
156
404
|
} else {
|
|
157
405
|
throw is.invalidParameterError('crop', 'valid crop id/name/strategy', crop);
|
|
158
406
|
}
|
|
@@ -160,66 +408,29 @@ function crop (crop) {
|
|
|
160
408
|
}
|
|
161
409
|
|
|
162
410
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* If the background contains an alpha value then WebP and PNG format output images will
|
|
167
|
-
* contain an alpha channel, even when the input image does not.
|
|
168
|
-
*
|
|
169
|
-
* @example
|
|
170
|
-
* sharp('input.gif')
|
|
171
|
-
* .resize(200, 300)
|
|
172
|
-
* .background({r: 0, g: 0, b: 0, alpha: 0})
|
|
173
|
-
* .embed()
|
|
174
|
-
* .toFormat(sharp.format.webp)
|
|
175
|
-
* .toBuffer(function(err, outputBuffer) {
|
|
176
|
-
* if (err) {
|
|
177
|
-
* throw err;
|
|
178
|
-
* }
|
|
179
|
-
* // outputBuffer contains WebP image data of a 200 pixels wide and 300 pixels high
|
|
180
|
-
* // containing a scaled version, embedded on a transparent canvas, of input.gif
|
|
181
|
-
* });
|
|
182
|
-
* @param {String} [embed='centre'] - A member of `sharp.gravity` to embed to an edge/corner.
|
|
183
|
-
* @returns {Sharp}
|
|
184
|
-
* @throws {Error} Invalid parameters
|
|
411
|
+
* @deprecated
|
|
412
|
+
* @private
|
|
185
413
|
*/
|
|
186
414
|
function embed (embed) {
|
|
187
415
|
this.options.canvas = 'embed';
|
|
188
|
-
|
|
189
416
|
if (!is.defined(embed)) {
|
|
190
417
|
// Default
|
|
191
|
-
this.options.
|
|
418
|
+
this.options.position = gravity.center;
|
|
192
419
|
} else if (is.integer(embed) && is.inRange(embed, 0, 8)) {
|
|
193
420
|
// Gravity (numeric)
|
|
194
|
-
this.options.
|
|
421
|
+
this.options.position = embed;
|
|
195
422
|
} else if (is.string(embed) && is.integer(gravity[embed])) {
|
|
196
423
|
// Gravity (string)
|
|
197
|
-
this.options.
|
|
424
|
+
this.options.position = gravity[embed];
|
|
198
425
|
} else {
|
|
199
426
|
throw is.invalidParameterError('embed', 'valid embed id/name', embed);
|
|
200
427
|
}
|
|
201
|
-
|
|
202
428
|
return this;
|
|
203
429
|
}
|
|
204
430
|
|
|
205
431
|
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* sharp(inputBuffer)
|
|
213
|
-
* .resize(200, 200)
|
|
214
|
-
* .max()
|
|
215
|
-
* .toFormat('jpeg')
|
|
216
|
-
* .toBuffer()
|
|
217
|
-
* .then(function(outputBuffer) {
|
|
218
|
-
* // outputBuffer contains JPEG image data no wider than 200 pixels and no higher
|
|
219
|
-
* // than 200 pixels regardless of the inputBuffer image dimensions
|
|
220
|
-
* });
|
|
221
|
-
*
|
|
222
|
-
* @returns {Sharp}
|
|
432
|
+
* @deprecated
|
|
433
|
+
* @private
|
|
223
434
|
*/
|
|
224
435
|
function max () {
|
|
225
436
|
this.options.canvas = 'max';
|
|
@@ -227,12 +438,8 @@ function max () {
|
|
|
227
438
|
}
|
|
228
439
|
|
|
229
440
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
|
|
234
|
-
*
|
|
235
|
-
* @returns {Sharp}
|
|
441
|
+
* @deprecated
|
|
442
|
+
* @private
|
|
236
443
|
*/
|
|
237
444
|
function min () {
|
|
238
445
|
this.options.canvas = 'min';
|
|
@@ -240,9 +447,8 @@ function min () {
|
|
|
240
447
|
}
|
|
241
448
|
|
|
242
449
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
* @returns {Sharp}
|
|
450
|
+
* @deprecated
|
|
451
|
+
* @private
|
|
246
452
|
*/
|
|
247
453
|
function ignoreAspectRatio () {
|
|
248
454
|
this.options.canvas = 'ignore_aspect';
|
|
@@ -250,15 +456,8 @@ function ignoreAspectRatio () {
|
|
|
250
456
|
}
|
|
251
457
|
|
|
252
458
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
* "*change the dimensions of the image only if its width or height exceeds the geometry specification*".
|
|
256
|
-
* Use with `max()` to preserve the image's aspect ratio.
|
|
257
|
-
*
|
|
258
|
-
* The default behaviour *before* function call is `false`, meaning the image will be enlarged.
|
|
259
|
-
*
|
|
260
|
-
* @param {Boolean} [withoutEnlargement=true]
|
|
261
|
-
* @returns {Sharp}
|
|
459
|
+
* @deprecated
|
|
460
|
+
* @private
|
|
262
461
|
*/
|
|
263
462
|
function withoutEnlargement (withoutEnlargement) {
|
|
264
463
|
this.options.withoutEnlargement = is.bool(withoutEnlargement) ? withoutEnlargement : true;
|
|
@@ -270,19 +469,23 @@ function withoutEnlargement (withoutEnlargement) {
|
|
|
270
469
|
* @private
|
|
271
470
|
*/
|
|
272
471
|
module.exports = function (Sharp) {
|
|
273
|
-
|
|
472
|
+
Object.assign(Sharp.prototype, {
|
|
274
473
|
resize,
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
min,
|
|
279
|
-
ignoreAspectRatio,
|
|
280
|
-
withoutEnlargement
|
|
281
|
-
].forEach(function (f) {
|
|
282
|
-
Sharp.prototype[f.name] = f;
|
|
474
|
+
extend,
|
|
475
|
+
extract,
|
|
476
|
+
trim
|
|
283
477
|
});
|
|
284
478
|
// Class attributes
|
|
285
479
|
Sharp.gravity = gravity;
|
|
286
480
|
Sharp.strategy = strategy;
|
|
287
481
|
Sharp.kernel = kernel;
|
|
482
|
+
Sharp.fit = fit;
|
|
483
|
+
Sharp.position = position;
|
|
484
|
+
// Deprecated functions, to be removed in v0.22.0
|
|
485
|
+
Sharp.prototype.crop = deprecate(crop, 'crop(position) is deprecated, use resize({ fit: "cover", position }) instead');
|
|
486
|
+
Sharp.prototype.embed = deprecate(embed, 'embed(position) is deprecated, use resize({ fit: "contain", position }) instead');
|
|
487
|
+
Sharp.prototype.max = deprecate(max, 'max() is deprecated, use resize({ fit: "inside" }) instead');
|
|
488
|
+
Sharp.prototype.min = deprecate(min, 'min() is deprecated, use resize({ fit: "outside" }) instead');
|
|
489
|
+
Sharp.prototype.ignoreAspectRatio = deprecate(ignoreAspectRatio, 'ignoreAspectRatio() is deprecated, use resize({ fit: "fill" }) instead');
|
|
490
|
+
Sharp.prototype.withoutEnlargement = deprecate(withoutEnlargement, 'withoutEnlargement() is deprecated, use resize({ withoutEnlargement: true }) instead');
|
|
288
491
|
};
|
package/lib/utility.js
CHANGED
|
@@ -82,23 +82,20 @@ function counters () {
|
|
|
82
82
|
* Improves the performance of `resize`, `blur` and `sharpen` operations
|
|
83
83
|
* by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM NEON.
|
|
84
84
|
*
|
|
85
|
-
* This feature is currently off by default but future versions may reverse this.
|
|
86
|
-
* Versions of liborc prior to 0.4.25 are known to segfault under heavy load.
|
|
87
|
-
*
|
|
88
85
|
* @example
|
|
89
86
|
* const simd = sharp.simd();
|
|
90
|
-
* // simd is `true` if
|
|
87
|
+
* // simd is `true` if the runtime use of liborc is currently enabled
|
|
91
88
|
* @example
|
|
92
|
-
* const simd = sharp.simd(
|
|
93
|
-
* //
|
|
89
|
+
* const simd = sharp.simd(false);
|
|
90
|
+
* // prevent libvips from using liborc at runtime
|
|
94
91
|
*
|
|
95
|
-
* @param {Boolean} [simd=
|
|
92
|
+
* @param {Boolean} [simd=true]
|
|
96
93
|
* @returns {Boolean}
|
|
97
94
|
*/
|
|
98
95
|
function simd (simd) {
|
|
99
96
|
return sharp.simd(is.bool(simd) ? simd : null);
|
|
100
97
|
}
|
|
101
|
-
simd(
|
|
98
|
+
simd(true);
|
|
102
99
|
|
|
103
100
|
/**
|
|
104
101
|
* Decorate the Sharp class with utility-related functions.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sharp",
|
|
3
3
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.21.3",
|
|
5
5
|
"author": "Lovell Fuller <npm@lovell.info>",
|
|
6
6
|
"homepage": "https://github.com/lovell/sharp",
|
|
7
7
|
"contributors": [
|
|
@@ -54,13 +54,20 @@
|
|
|
54
54
|
"Sylvain Dumont <sylvain.dumont35@gmail.com>",
|
|
55
55
|
"Alun Davies <alun.owain.davies@googlemail.com>",
|
|
56
56
|
"Aidan Hoolachan <ajhoolachan21@gmail.com>",
|
|
57
|
-
"Axel Eirola <axel.eirola@iki.fi>"
|
|
57
|
+
"Axel Eirola <axel.eirola@iki.fi>",
|
|
58
|
+
"Freezy <freezy@xbmc.org>",
|
|
59
|
+
"Daiz <taneli.vatanen@gmail.com>",
|
|
60
|
+
"Julian Aubourg <j@ubourg.net>",
|
|
61
|
+
"Keith Belovay <keith@picthrive.com>",
|
|
62
|
+
"Michael B. Klein <mbklein@gmail.com>"
|
|
58
63
|
],
|
|
59
64
|
"scripts": {
|
|
60
65
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
|
61
|
-
"clean": "rm -rf node_modules/ build/ vendor/ coverage/ test/fixtures/output.*",
|
|
62
|
-
"test": "semistandard && cc &&
|
|
63
|
-
"
|
|
66
|
+
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
|
|
67
|
+
"test": "semistandard && cc && npm run test-unit && npm run test-licensing && prebuild-ci",
|
|
68
|
+
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
|
|
69
|
+
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
|
|
70
|
+
"test-coverage": "./test/coverage/report.sh",
|
|
64
71
|
"test-leak": "./test/leak/leak.sh",
|
|
65
72
|
"docs": "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"
|
|
66
73
|
},
|
|
@@ -86,38 +93,40 @@
|
|
|
86
93
|
"vips"
|
|
87
94
|
],
|
|
88
95
|
"dependencies": {
|
|
89
|
-
"
|
|
96
|
+
"bindings": "^1.3.1",
|
|
97
|
+
"color": "^3.1.0",
|
|
90
98
|
"detect-libc": "^1.0.3",
|
|
91
|
-
"nan": "^2.11.0",
|
|
92
99
|
"fs-copy-file-sync": "^1.1.1",
|
|
100
|
+
"nan": "^2.12.1",
|
|
93
101
|
"npmlog": "^4.1.2",
|
|
94
|
-
"prebuild-install": "^
|
|
95
|
-
"semver": "^5.
|
|
96
|
-
"simple-get": "^
|
|
97
|
-
"tar": "^4.4.
|
|
102
|
+
"prebuild-install": "^5.2.2",
|
|
103
|
+
"semver": "^5.6.0",
|
|
104
|
+
"simple-get": "^3.0.3",
|
|
105
|
+
"tar": "^4.4.8",
|
|
98
106
|
"tunnel-agent": "^0.6.0"
|
|
99
107
|
},
|
|
100
108
|
"devDependencies": {
|
|
101
109
|
"async": "^2.6.1",
|
|
102
110
|
"cc": "^1.0.2",
|
|
103
|
-
"decompress-zip": "^0.3.
|
|
104
|
-
"documentation": "^
|
|
111
|
+
"decompress-zip": "^0.3.2",
|
|
112
|
+
"documentation": "^9.1.1",
|
|
105
113
|
"exif-reader": "^1.0.2",
|
|
106
114
|
"icc": "^1.0.0",
|
|
115
|
+
"license-checker": "^25.0.1",
|
|
107
116
|
"mocha": "^5.2.0",
|
|
108
|
-
"mock-fs": "^4.
|
|
109
|
-
"nyc": "^
|
|
110
|
-
"prebuild": "^
|
|
111
|
-
"prebuild-ci": "^2.
|
|
112
|
-
"rimraf": "^2.6.
|
|
113
|
-
"semistandard": "^
|
|
117
|
+
"mock-fs": "^4.7.0",
|
|
118
|
+
"nyc": "^13.1.0",
|
|
119
|
+
"prebuild": "^8.1.2",
|
|
120
|
+
"prebuild-ci": "^2.3.0",
|
|
121
|
+
"rimraf": "^2.6.3",
|
|
122
|
+
"semistandard": "^13.0.1"
|
|
114
123
|
},
|
|
115
124
|
"license": "Apache-2.0",
|
|
116
125
|
"config": {
|
|
117
|
-
"libvips": "8.
|
|
126
|
+
"libvips": "8.7.0"
|
|
118
127
|
},
|
|
119
128
|
"engines": {
|
|
120
|
-
"node": ">=
|
|
129
|
+
"node": ">=6"
|
|
121
130
|
},
|
|
122
131
|
"semistandard": {
|
|
123
132
|
"env": [
|