sharp 0.17.2 → 0.18.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/input.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const util = require('util');
3
+ const color = require('color');
4
4
  const is = require('./is');
5
5
  const sharp = require('../build/Release/sharp.node');
6
6
 
@@ -8,7 +8,7 @@ const sharp = require('../build/Release/sharp.node');
8
8
  * Create Object containing input and input-related options.
9
9
  * @private
10
10
  */
11
- const _createInputDescriptor = function _createInputDescriptor (input, inputOptions, containerOptions) {
11
+ function _createInputDescriptor (input, inputOptions, containerOptions) {
12
12
  const inputDescriptor = {};
13
13
  if (is.string(input)) {
14
14
  // filesystem
@@ -16,6 +16,9 @@ const _createInputDescriptor = function _createInputDescriptor (input, inputOpti
16
16
  } else if (is.buffer(input)) {
17
17
  // Buffer
18
18
  inputDescriptor.buffer = input;
19
+ } else if (is.plainObject(input) && !is.defined(inputOptions)) {
20
+ // Plain Object descriptor, e.g. create
21
+ inputOptions = input;
19
22
  } else if (!is.defined(input) && is.object(containerOptions) && containerOptions.allowStream) {
20
23
  // Stream
21
24
  inputDescriptor.buffer = [];
@@ -35,8 +38,8 @@ const _createInputDescriptor = function _createInputDescriptor (input, inputOpti
35
38
  if (is.defined(inputOptions.raw)) {
36
39
  if (
37
40
  is.object(inputOptions.raw) &&
38
- is.integer(inputOptions.raw.width) && is.inRange(inputOptions.raw.width, 1, this.constructor.maximum.width) &&
39
- is.integer(inputOptions.raw.height) && is.inRange(inputOptions.raw.height, 1, this.constructor.maximum.height) &&
41
+ is.integer(inputOptions.raw.width) && inputOptions.raw.width > 0 &&
42
+ is.integer(inputOptions.raw.height) && inputOptions.raw.height > 0 &&
40
43
  is.integer(inputOptions.raw.channels) && is.inRange(inputOptions.raw.channels, 1, 4)
41
44
  ) {
42
45
  inputDescriptor.rawWidth = inputOptions.raw.width;
@@ -46,11 +49,35 @@ const _createInputDescriptor = function _createInputDescriptor (input, inputOpti
46
49
  throw new Error('Expected width, height and channels for raw pixel input');
47
50
  }
48
51
  }
52
+ // Create new image
53
+ if (is.defined(inputOptions.create)) {
54
+ if (
55
+ is.object(inputOptions.create) &&
56
+ is.integer(inputOptions.create.width) && inputOptions.create.width > 0 &&
57
+ is.integer(inputOptions.create.height) && inputOptions.create.height > 0 &&
58
+ is.integer(inputOptions.create.channels) && is.inRange(inputOptions.create.channels, 3, 4) &&
59
+ is.defined(inputOptions.create.background)
60
+ ) {
61
+ inputDescriptor.createWidth = inputOptions.create.width;
62
+ inputDescriptor.createHeight = inputOptions.create.height;
63
+ inputDescriptor.createChannels = inputOptions.create.channels;
64
+ const background = color(inputOptions.create.background);
65
+ inputDescriptor.createBackground = [
66
+ background.red(),
67
+ background.green(),
68
+ background.blue(),
69
+ Math.round(background.alpha() * 255)
70
+ ];
71
+ delete inputDescriptor.buffer;
72
+ } else {
73
+ throw new Error('Expected width, height, channels and background to create a new input image');
74
+ }
75
+ }
49
76
  } else if (is.defined(inputOptions)) {
50
77
  throw new Error('Invalid input options ' + inputOptions);
51
78
  }
52
79
  return inputDescriptor;
53
- };
80
+ }
54
81
 
55
82
  /**
56
83
  * Handle incoming Buffer chunk on Writable Stream.
@@ -59,7 +86,7 @@ const _createInputDescriptor = function _createInputDescriptor (input, inputOpti
59
86
  * @param {String} encoding - unused
60
87
  * @param {Function} callback
61
88
  */
62
- const _write = function _write (chunk, encoding, callback) {
89
+ function _write (chunk, encoding, callback) {
63
90
  /* istanbul ignore else */
64
91
  if (Array.isArray(this.options.input.buffer)) {
65
92
  /* istanbul ignore else */
@@ -78,26 +105,26 @@ const _write = function _write (chunk, encoding, callback) {
78
105
  } else {
79
106
  callback(new Error('Unexpected data on Writable Stream'));
80
107
  }
81
- };
108
+ }
82
109
 
83
110
  /**
84
111
  * Flattens the array of chunks accumulated in input.buffer.
85
112
  * @private
86
113
  */
87
- const _flattenBufferIn = function _flattenBufferIn () {
114
+ function _flattenBufferIn () {
88
115
  if (this._isStreamInput()) {
89
116
  this.options.input.buffer = Buffer.concat(this.options.input.buffer);
90
117
  }
91
- };
118
+ }
92
119
 
93
120
  /**
94
121
  * Are we expecting Stream-based input?
95
122
  * @private
96
123
  * @returns {Boolean}
97
124
  */
98
- const _isStreamInput = function _isStreamInput () {
125
+ function _isStreamInput () {
99
126
  return Array.isArray(this.options.input.buffer);
100
- };
127
+ }
101
128
 
102
129
  /**
103
130
  * Take a "snapshot" of the Sharp instance, returning a new instance.
@@ -114,11 +141,11 @@ const _isStreamInput = function _isStreamInput () {
114
141
  *
115
142
  * @returns {Sharp}
116
143
  */
117
- const clone = function clone () {
144
+ function clone () {
118
145
  const that = this;
119
146
  // Clone existing options
120
147
  const clone = this.constructor.call();
121
- util._extend(clone.options, this.options);
148
+ clone.options = Object.assign({}, this.options);
122
149
  // Pass 'finish' event to clone for Stream-based input
123
150
  this.on('finish', function () {
124
151
  // Clone inherits input data
@@ -127,17 +154,18 @@ const clone = function clone () {
127
154
  clone.emit('finish');
128
155
  });
129
156
  return clone;
130
- };
157
+ }
131
158
 
132
159
  /**
133
- * Fast access to image metadata without decoding any compressed image data.
160
+ * Fast access to (uncached) image metadata without decoding any compressed image data.
134
161
  * A Promises/A+ promise is returned when `callback` is not provided.
135
162
  *
136
163
  * - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
137
164
  * - `width`: Number of pixels wide
138
165
  * - `height`: Number of pixels high
139
- * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
166
+ * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636)
140
167
  * - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
168
+ * - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
141
169
  * - `density`: Number of pixels per inch (DPI), if present
142
170
  * - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
143
171
  * - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
@@ -162,7 +190,7 @@ const clone = function clone () {
162
190
  * @param {Function} [callback] - called with the arguments `(err, metadata)`
163
191
  * @returns {Promise<Object>|Sharp}
164
192
  */
165
- const metadata = function metadata (callback) {
193
+ function metadata (callback) {
166
194
  const that = this;
167
195
  if (is.fn(callback)) {
168
196
  if (this._isStreamInput()) {
@@ -200,7 +228,7 @@ const metadata = function metadata (callback) {
200
228
  });
201
229
  }
202
230
  }
203
- };
231
+ }
204
232
 
205
233
  /**
206
234
  * Do not process input images where the number of pixels (width * height) exceeds this limit.
@@ -210,20 +238,20 @@ const metadata = function metadata (callback) {
210
238
  * @returns {Sharp}
211
239
  * @throws {Error} Invalid limit
212
240
  */
213
- const limitInputPixels = function limitInputPixels (limit) {
241
+ function limitInputPixels (limit) {
214
242
  // if we pass in false we represent the integer as 0 to disable
215
243
  if (limit === false) {
216
244
  limit = 0;
217
245
  } else if (limit === true) {
218
- limit = this.constructor.maximum.pixels;
246
+ limit = Math.pow(0x3FFF, 2);
219
247
  }
220
248
  if (is.integer(limit) && limit >= 0) {
221
249
  this.options.limitInputPixels = limit;
222
250
  } else {
223
- throw new Error('Invalid pixel limit (0 to ' + this.constructor.maximum.pixels + ') ' + limit);
251
+ throw is.invalidParameterError('limitInputPixels', 'integer', limit);
224
252
  }
225
253
  return this;
226
- };
254
+ }
227
255
 
228
256
  /**
229
257
  * An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`.
@@ -231,10 +259,10 @@ const limitInputPixels = function limitInputPixels (limit) {
231
259
  * @param {Boolean} [sequentialRead=true]
232
260
  * @returns {Sharp}
233
261
  */
234
- const sequentialRead = function sequentialRead (sequentialRead) {
262
+ function sequentialRead (sequentialRead) {
235
263
  this.options.sequentialRead = is.bool(sequentialRead) ? sequentialRead : true;
236
264
  return this;
237
- };
265
+ }
238
266
 
239
267
  /**
240
268
  * Decorate the Sharp prototype with input-related functions.
package/lib/is.js CHANGED
@@ -16,6 +16,14 @@ const object = function (val) {
16
16
  return typeof val === 'object';
17
17
  };
18
18
 
19
+ /**
20
+ * Is this value a plain object?
21
+ * @private
22
+ */
23
+ const plainObject = function (val) {
24
+ return object(val) && Object.prototype.toString.call(val) === '[object Object]';
25
+ };
26
+
19
27
  /**
20
28
  * Is this value a function?
21
29
  * @private
@@ -98,6 +106,7 @@ const invalidParameterError = function (name, expected, actual) {
98
106
  module.exports = {
99
107
  defined: defined,
100
108
  object: object,
109
+ plainObject: plainObject,
101
110
  fn: fn,
102
111
  bool: bool,
103
112
  buffer: buffer,
package/lib/operation.js CHANGED
@@ -6,7 +6,10 @@ const is = require('./is');
6
6
  * Rotate the output image by either an explicit angle
7
7
  * or auto-orient based on the EXIF `Orientation` tag.
8
8
  *
9
- * Use this method without angle to determine the angle from EXIF data.
9
+ * If an angle is provided, it is converted to a valid 90/180/270deg rotation.
10
+ * For example, `-450` will produce a 270deg rotation.
11
+ *
12
+ * If no angle is provided, it is determined from the EXIF data.
10
13
  * Mirroring is supported and may infer the use of a flip operation.
11
14
  *
12
15
  * The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
@@ -25,20 +28,20 @@ const is = require('./is');
25
28
  * });
26
29
  * readableStream.pipe(pipeline);
27
30
  *
28
- * @param {Number} [angle=auto] 0, 90, 180 or 270.
31
+ * @param {Number} [angle=auto] angle of rotation, must be a multiple of 90.
29
32
  * @returns {Sharp}
30
33
  * @throws {Error} Invalid parameters
31
34
  */
32
- const rotate = function rotate (angle) {
35
+ function rotate (angle) {
33
36
  if (!is.defined(angle)) {
34
- this.options.angle = -1;
35
- } else if (is.integer(angle) && is.inArray(angle, [0, 90, 180, 270])) {
37
+ this.options.useExifOrientation = true;
38
+ } else if (is.integer(angle) && !(angle % 90)) {
36
39
  this.options.angle = angle;
37
40
  } else {
38
- throw new Error('Unsupported angle (0, 90, 180, 270) ' + angle);
41
+ throw new Error('Unsupported angle: angle must be a positive/negative multiple of 90 ' + angle);
39
42
  }
40
43
  return this;
41
- };
44
+ }
42
45
 
43
46
  /**
44
47
  * Extract a region of the image.
@@ -70,7 +73,7 @@ const rotate = function rotate (angle) {
70
73
  * @returns {Sharp}
71
74
  * @throws {Error} Invalid parameters
72
75
  */
73
- const extract = function extract (options) {
76
+ function extract (options) {
74
77
  const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
75
78
  ['left', 'top', 'width', 'height'].forEach(function (name) {
76
79
  const value = options[name];
@@ -81,11 +84,11 @@ const extract = function extract (options) {
81
84
  }
82
85
  }, this);
83
86
  // Ensure existing rotation occurs before pre-resize extraction
84
- if (suffix === 'Pre' && this.options.angle !== 0) {
87
+ if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true)) {
85
88
  this.options.rotateBeforePreExtract = true;
86
89
  }
87
90
  return this;
88
- };
91
+ }
89
92
 
90
93
  /**
91
94
  * Flip the image about the vertical Y axis. This always occurs after rotation, if any.
@@ -93,10 +96,10 @@ const extract = function extract (options) {
93
96
  * @param {Boolean} [flip=true]
94
97
  * @returns {Sharp}
95
98
  */
96
- const flip = function flip (flip) {
99
+ function flip (flip) {
97
100
  this.options.flip = is.bool(flip) ? flip : true;
98
101
  return this;
99
- };
102
+ }
100
103
 
101
104
  /**
102
105
  * Flop the image about the horizontal X axis. This always occurs after rotation, if any.
@@ -104,10 +107,10 @@ const flip = function flip (flip) {
104
107
  * @param {Boolean} [flop=true]
105
108
  * @returns {Sharp}
106
109
  */
107
- const flop = function flop (flop) {
110
+ function flop (flop) {
108
111
  this.options.flop = is.bool(flop) ? flop : true;
109
112
  return this;
110
- };
113
+ }
111
114
 
112
115
  /**
113
116
  * Sharpen the image.
@@ -121,7 +124,7 @@ const flop = function flop (flop) {
121
124
  * @returns {Sharp}
122
125
  * @throws {Error} Invalid parameters
123
126
  */
124
- const sharpen = function sharpen (sigma, flat, jagged) {
127
+ function sharpen (sigma, flat, jagged) {
125
128
  if (!is.defined(sigma)) {
126
129
  // No arguments: default to mild sharpen
127
130
  this.options.sharpenSigma = -1;
@@ -151,7 +154,7 @@ const sharpen = function sharpen (sigma, flat, jagged) {
151
154
  throw new Error('Invalid sharpen sigma (0.01 - 10000) ' + sigma);
152
155
  }
153
156
  return this;
154
- };
157
+ }
155
158
 
156
159
  /**
157
160
  * Blur the image.
@@ -161,7 +164,7 @@ const sharpen = function sharpen (sigma, flat, jagged) {
161
164
  * @returns {Sharp}
162
165
  * @throws {Error} Invalid parameters
163
166
  */
164
- const blur = function blur (sigma) {
167
+ function blur (sigma) {
165
168
  if (!is.defined(sigma)) {
166
169
  // No arguments: default to mild blur
167
170
  this.options.blurSigma = -1;
@@ -175,7 +178,7 @@ const blur = function blur (sigma) {
175
178
  throw new Error('Invalid blur sigma (0.3 - 1000.0) ' + sigma);
176
179
  }
177
180
  return this;
178
- };
181
+ }
179
182
 
180
183
  /**
181
184
  * Extends/pads the edges of the image with the colour provided to the `background` method.
@@ -198,7 +201,7 @@ const blur = function blur (sigma) {
198
201
  * @returns {Sharp}
199
202
  * @throws {Error} Invalid parameters
200
203
  */
201
- const extend = function extend (extend) {
204
+ function extend (extend) {
202
205
  if (is.integer(extend) && extend > 0) {
203
206
  this.options.extendTop = extend;
204
207
  this.options.extendBottom = extend;
@@ -219,17 +222,17 @@ const extend = function extend (extend) {
219
222
  throw new Error('Invalid edge extension ' + extend);
220
223
  }
221
224
  return this;
222
- };
225
+ }
223
226
 
224
227
  /**
225
228
  * Merge alpha transparency channel, if any, with `background`.
226
229
  * @param {Boolean} [flatten=true]
227
230
  * @returns {Sharp}
228
231
  */
229
- const flatten = function flatten (flatten) {
232
+ function flatten (flatten) {
230
233
  this.options.flatten = is.bool(flatten) ? flatten : true;
231
234
  return this;
232
- };
235
+ }
233
236
 
234
237
  /**
235
238
  * Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel.
@@ -237,7 +240,7 @@ const flatten = function flatten (flatten) {
237
240
  * @returns {Sharp}
238
241
  * @throws {Error} Invalid parameters
239
242
  */
240
- const trim = function trim (tolerance) {
243
+ function trim (tolerance) {
241
244
  if (!is.defined(tolerance)) {
242
245
  this.options.trimTolerance = 10;
243
246
  } else if (is.integer(tolerance) && is.inRange(tolerance, 1, 99)) {
@@ -246,7 +249,7 @@ const trim = function trim (tolerance) {
246
249
  throw new Error('Invalid trim tolerance (1 to 99) ' + tolerance);
247
250
  }
248
251
  return this;
249
- };
252
+ }
250
253
 
251
254
  /**
252
255
  * Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
@@ -258,7 +261,7 @@ const trim = function trim (tolerance) {
258
261
  * @returns {Sharp}
259
262
  * @throws {Error} Invalid parameters
260
263
  */
261
- const gamma = function gamma (gamma) {
264
+ function gamma (gamma) {
262
265
  if (!is.defined(gamma)) {
263
266
  // Default gamma correction of 2.2 (sRGB)
264
267
  this.options.gamma = 2.2;
@@ -268,36 +271,36 @@ const gamma = function gamma (gamma) {
268
271
  throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma);
269
272
  }
270
273
  return this;
271
- };
274
+ }
272
275
 
273
276
  /**
274
277
  * Produce the "negative" of the image.
275
278
  * @param {Boolean} [negate=true]
276
279
  * @returns {Sharp}
277
280
  */
278
- const negate = function negate (negate) {
281
+ function negate (negate) {
279
282
  this.options.negate = is.bool(negate) ? negate : true;
280
283
  return this;
281
- };
284
+ }
282
285
 
283
286
  /**
284
287
  * Enhance output image contrast by stretching its luminance to cover the full dynamic range.
285
288
  * @param {Boolean} [normalise=true]
286
289
  * @returns {Sharp}
287
290
  */
288
- const normalise = function normalise (normalise) {
291
+ function normalise (normalise) {
289
292
  this.options.normalise = is.bool(normalise) ? normalise : true;
290
293
  return this;
291
- };
294
+ }
292
295
 
293
296
  /**
294
297
  * Alternative spelling of normalise.
295
298
  * @param {Boolean} [normalize=true]
296
299
  * @returns {Sharp}
297
300
  */
298
- const normalize = function normalize (normalize) {
301
+ function normalize (normalize) {
299
302
  return this.normalise(normalize);
300
- };
303
+ }
301
304
 
302
305
  /**
303
306
  * Convolve the image with the specified kernel.
@@ -324,7 +327,7 @@ const normalize = function normalize (normalize) {
324
327
  * @returns {Sharp}
325
328
  * @throws {Error} Invalid parameters
326
329
  */
327
- const convolve = function convolve (kernel) {
330
+ function convolve (kernel) {
328
331
  if (!is.object(kernel) || !Array.isArray(kernel.kernel) ||
329
332
  !is.integer(kernel.width) || !is.integer(kernel.height) ||
330
333
  !is.inRange(kernel.width, 3, 1001) || !is.inRange(kernel.height, 3, 1001) ||
@@ -348,7 +351,7 @@ const convolve = function convolve (kernel) {
348
351
  }
349
352
  this.options.convKernel = kernel;
350
353
  return this;
351
- };
354
+ }
352
355
 
353
356
  /**
354
357
  * Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
@@ -359,7 +362,7 @@ const convolve = function convolve (kernel) {
359
362
  * @returns {Sharp}
360
363
  * @throws {Error} Invalid parameters
361
364
  */
362
- const threshold = function threshold (threshold, options) {
365
+ function threshold (threshold, options) {
363
366
  if (!is.defined(threshold)) {
364
367
  this.options.threshold = 128;
365
368
  } else if (is.bool(threshold)) {
@@ -375,7 +378,7 @@ const threshold = function threshold (threshold, options) {
375
378
  this.options.thresholdGrayscale = false;
376
379
  }
377
380
  return this;
378
- };
381
+ }
379
382
 
380
383
  /**
381
384
  * Perform a bitwise boolean operation with operand image.
@@ -393,7 +396,7 @@ const threshold = function threshold (threshold, options) {
393
396
  * @returns {Sharp}
394
397
  * @throws {Error} Invalid parameters
395
398
  */
396
- const boolean = function boolean (operand, operator, options) {
399
+ function boolean (operand, operator, options) {
397
400
  this.options.boolean = this._createInputDescriptor(operand, options);
398
401
  if (is.string(operator) && is.inArray(operator, ['and', 'or', 'eor'])) {
399
402
  this.options.booleanOp = operator;
@@ -401,7 +404,7 @@ const boolean = function boolean (operand, operator, options) {
401
404
  throw new Error('Invalid boolean operator ' + operator);
402
405
  }
403
406
  return this;
404
- };
407
+ }
405
408
 
406
409
  /**
407
410
  * Decorate the Sharp prototype with operation-related functions.