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/lib/libvips.js CHANGED
@@ -44,10 +44,16 @@ const globalLibvipsVersion = function () {
44
44
 
45
45
  const hasVendoredLibvips = function () {
46
46
  const currentPlatformId = platform();
47
+ const vendorPath = path.join(__dirname, '..', 'vendor');
48
+ let vendorVersionId;
47
49
  let vendorPlatformId;
48
50
  try {
49
- vendorPlatformId = require(path.join(__dirname, '..', 'vendor', 'platform.json'));
51
+ vendorVersionId = require(path.join(vendorPath, 'versions.json')).vips;
52
+ vendorPlatformId = require(path.join(vendorPath, 'platform.json'));
50
53
  } catch (err) {}
54
+ if (vendorVersionId && vendorVersionId !== minimumLibvipsVersion) {
55
+ throw new Error(`Found vendored libvips v${vendorVersionId} but require v${minimumLibvipsVersion}. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
56
+ }
51
57
  if (vendorPlatformId) {
52
58
  if (currentPlatformId === vendorPlatformId) {
53
59
  return true;
package/lib/operation.js CHANGED
@@ -1,14 +1,18 @@
1
1
  'use strict';
2
2
 
3
+ const color = require('color');
3
4
  const is = require('./is');
4
5
 
5
6
  /**
6
7
  * Rotate the output image by either an explicit angle
7
8
  * or auto-orient based on the EXIF `Orientation` tag.
8
9
  *
9
- * If an angle is provided, it is converted to a valid 90/180/270deg rotation.
10
+ * If an angle is provided, it is converted to a valid positive degree rotation.
10
11
  * For example, `-450` will produce a 270deg rotation.
11
12
  *
13
+ * When rotating by an angle other than a multiple of 90,
14
+ * the background colour can be provided with the `background` option.
15
+ *
12
16
  * If no angle is provided, it is determined from the EXIF data.
13
17
  * Mirroring is supported and may infer the use of a flip operation.
14
18
  *
@@ -28,64 +32,30 @@ const is = require('./is');
28
32
  * });
29
33
  * readableStream.pipe(pipeline);
30
34
  *
31
- * @param {Number} [angle=auto] angle of rotation, must be a multiple of 90.
35
+ * @param {Number} [angle=auto] angle of rotation.
36
+ * @param {Object} [options] - if present, is an Object with optional attributes.
37
+ * @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.
32
38
  * @returns {Sharp}
33
39
  * @throws {Error} Invalid parameters
34
40
  */
35
- function rotate (angle) {
41
+ function rotate (angle, options) {
36
42
  if (!is.defined(angle)) {
37
43
  this.options.useExifOrientation = true;
38
44
  } else if (is.integer(angle) && !(angle % 90)) {
39
45
  this.options.angle = angle;
40
- } else {
41
- throw new Error('Unsupported angle: angle must be a positive/negative multiple of 90 ' + angle);
42
- }
43
- return this;
44
- }
45
-
46
- /**
47
- * Extract a region of the image.
48
- *
49
- * - Use `extract` before `resize` for pre-resize extraction.
50
- * - Use `extract` after `resize` for post-resize extraction.
51
- * - Use `extract` before and after for both.
52
- *
53
- * @example
54
- * sharp(input)
55
- * .extract({ left: left, top: top, width: width, height: height })
56
- * .toFile(output, function(err) {
57
- * // Extract a region of the input image, saving in the same format.
58
- * });
59
- * @example
60
- * sharp(input)
61
- * .extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
62
- * .resize(width, height)
63
- * .extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
64
- * .toFile(output, function(err) {
65
- * // Extract a region, resize, then extract from the resized image
66
- * });
67
- *
68
- * @param {Object} options
69
- * @param {Number} options.left - zero-indexed offset from left edge
70
- * @param {Number} options.top - zero-indexed offset from top edge
71
- * @param {Number} options.width - dimension of extracted image
72
- * @param {Number} options.height - dimension of extracted image
73
- * @returns {Sharp}
74
- * @throws {Error} Invalid parameters
75
- */
76
- function extract (options) {
77
- const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
78
- ['left', 'top', 'width', 'height'].forEach(function (name) {
79
- const value = options[name];
80
- if (is.integer(value) && value >= 0) {
81
- this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
82
- } else {
83
- throw new Error('Non-integer value for ' + name + ' of ' + value);
46
+ } else if (is.number(angle)) {
47
+ this.options.rotationAngle = angle;
48
+ if (is.object(options) && options.background) {
49
+ const backgroundColour = color(options.background);
50
+ this.options.rotationBackground = [
51
+ backgroundColour.red(),
52
+ backgroundColour.green(),
53
+ backgroundColour.blue(),
54
+ Math.round(backgroundColour.alpha() * 255)
55
+ ];
84
56
  }
85
- }, this);
86
- // Ensure existing rotation occurs before pre-resize extraction
87
- if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true)) {
88
- this.options.rotateBeforePreExtract = true;
57
+ } else {
58
+ throw new Error('Unsupported angle: must be a number.');
89
59
  }
90
60
  return this;
91
61
  }
@@ -201,72 +171,15 @@ function blur (sigma) {
201
171
  }
202
172
 
203
173
  /**
204
- * Extends/pads the edges of the image with the colour provided to the `background` method.
205
- * This operation will always occur after resizing and extraction, if any.
206
- *
207
- * @example
208
- * // Resize to 140 pixels wide, then add 10 transparent pixels
209
- * // to the top, left and right edges and 20 to the bottom edge
210
- * sharp(input)
211
- * .resize(140)
212
- * .background({r: 0, g: 0, b: 0, alpha: 0})
213
- * .extend({top: 10, bottom: 20, left: 10, right: 10})
214
- * ...
215
- *
216
- * @param {(Number|Object)} extend - single pixel count to add to all edges or an Object with per-edge counts
217
- * @param {Number} [extend.top]
218
- * @param {Number} [extend.left]
219
- * @param {Number} [extend.bottom]
220
- * @param {Number} [extend.right]
221
- * @returns {Sharp}
222
- * @throws {Error} Invalid parameters
223
- */
224
- function extend (extend) {
225
- if (is.integer(extend) && extend > 0) {
226
- this.options.extendTop = extend;
227
- this.options.extendBottom = extend;
228
- this.options.extendLeft = extend;
229
- this.options.extendRight = extend;
230
- } else if (
231
- is.object(extend) &&
232
- is.integer(extend.top) && extend.top >= 0 &&
233
- is.integer(extend.bottom) && extend.bottom >= 0 &&
234
- is.integer(extend.left) && extend.left >= 0 &&
235
- is.integer(extend.right) && extend.right >= 0
236
- ) {
237
- this.options.extendTop = extend.top;
238
- this.options.extendBottom = extend.bottom;
239
- this.options.extendLeft = extend.left;
240
- this.options.extendRight = extend.right;
241
- } else {
242
- throw new Error('Invalid edge extension ' + extend);
243
- }
244
- return this;
245
- }
246
-
247
- /**
248
- * Merge alpha transparency channel, if any, with `background`.
249
- * @param {Boolean} [flatten=true]
250
- * @returns {Sharp}
251
- */
252
- function flatten (flatten) {
253
- this.options.flatten = is.bool(flatten) ? flatten : true;
254
- return this;
255
- }
256
-
257
- /**
258
- * Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel.
259
- * @param {Number} [tolerance=10] value between 1 and 99 representing the percentage similarity.
174
+ * Merge alpha transparency channel, if any, with a background.
175
+ * @param {Object} [options]
176
+ * @param {String|Object} [options.background={r: 0, g: 0, b: 0}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black.
260
177
  * @returns {Sharp}
261
- * @throws {Error} Invalid parameters
262
178
  */
263
- function trim (tolerance) {
264
- if (!is.defined(tolerance)) {
265
- this.options.trimTolerance = 10;
266
- } else if (is.integer(tolerance) && is.inRange(tolerance, 1, 99)) {
267
- this.options.trimTolerance = tolerance;
268
- } else {
269
- throw new Error('Invalid trim tolerance (1 to 99) ' + tolerance);
179
+ function flatten (options) {
180
+ this.options.flatten = is.bool(options) ? options : true;
181
+ if (is.object(options)) {
182
+ this._setColourOption('flattenBackground', options.background);
270
183
  }
271
184
  return this;
272
185
  }
@@ -277,11 +190,15 @@ function trim (tolerance) {
277
190
  * This can improve the perceived brightness of a resized image in non-linear colour spaces.
278
191
  * JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation
279
192
  * when applying a gamma correction.
193
+ *
194
+ * Supply a second argument to use a different output gamma value, otherwise the first value is used in both cases.
195
+ *
280
196
  * @param {Number} [gamma=2.2] value between 1.0 and 3.0.
197
+ * @param {Number} [gammaOut] value between 1.0 and 3.0. (optional, defaults to same as `gamma`)
281
198
  * @returns {Sharp}
282
199
  * @throws {Error} Invalid parameters
283
200
  */
284
- function gamma (gamma) {
201
+ function gamma (gamma, gammaOut) {
285
202
  if (!is.defined(gamma)) {
286
203
  // Default gamma correction of 2.2 (sRGB)
287
204
  this.options.gamma = 2.2;
@@ -290,6 +207,14 @@ function gamma (gamma) {
290
207
  } else {
291
208
  throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma);
292
209
  }
210
+ if (!is.defined(gammaOut)) {
211
+ // Default gamma correction for output is same as input
212
+ this.options.gammaOut = this.options.gamma;
213
+ } else if (is.number(gammaOut) && is.inRange(gammaOut, 1, 3)) {
214
+ this.options.gammaOut = gammaOut;
215
+ } else {
216
+ throw new Error('Invalid output gamma correction (1.0 to 3.0) ' + gammaOut);
217
+ }
293
218
  return this;
294
219
  }
295
220
 
@@ -453,22 +378,56 @@ function linear (a, b) {
453
378
  return this;
454
379
  }
455
380
 
381
+ /**
382
+ * Recomb the image with the specified matrix.
383
+ *
384
+ * @example
385
+ * sharp(input)
386
+ * .recomb([
387
+ * [0.3588, 0.7044, 0.1368],
388
+ * [0.2990, 0.5870, 0.1140],
389
+ * [0.2392, 0.4696, 0.0912],
390
+ * ])
391
+ * .raw()
392
+ * .toBuffer(function(err, data, info) {
393
+ * // data contains the raw pixel data after applying the recomb
394
+ * // With this example input, a sepia filter has been applied
395
+ * });
396
+ *
397
+ * @param {Array<Array<Number>>} 3x3 Recombination matrix
398
+ * @returns {Sharp}
399
+ * @throws {Error} Invalid parameters
400
+ */
401
+ function recomb (inputMatrix) {
402
+ if (!Array.isArray(inputMatrix) || inputMatrix.length !== 3 ||
403
+ inputMatrix[0].length !== 3 ||
404
+ inputMatrix[1].length !== 3 ||
405
+ inputMatrix[2].length !== 3
406
+ ) {
407
+ // must pass in a kernel
408
+ throw new Error('Invalid Recomb Matrix');
409
+ }
410
+ this.options.recombMatrix = [
411
+ inputMatrix[0][0], inputMatrix[0][1], inputMatrix[0][2],
412
+ inputMatrix[1][0], inputMatrix[1][1], inputMatrix[1][2],
413
+ inputMatrix[2][0], inputMatrix[2][1], inputMatrix[2][2]
414
+ ].map(Number);
415
+ return this;
416
+ }
417
+
456
418
  /**
457
419
  * Decorate the Sharp prototype with operation-related functions.
458
420
  * @private
459
421
  */
460
422
  module.exports = function (Sharp) {
461
- [
423
+ Object.assign(Sharp.prototype, {
462
424
  rotate,
463
- extract,
464
425
  flip,
465
426
  flop,
466
427
  sharpen,
467
428
  median,
468
429
  blur,
469
- extend,
470
430
  flatten,
471
- trim,
472
431
  gamma,
473
432
  negate,
474
433
  normalise,
@@ -476,8 +435,7 @@ module.exports = function (Sharp) {
476
435
  convolve,
477
436
  threshold,
478
437
  boolean,
479
- linear
480
- ].forEach(function (f) {
481
- Sharp.prototype[f.name] = f;
438
+ linear,
439
+ recomb
482
440
  });
483
441
  };
package/lib/output.js CHANGED
@@ -32,7 +32,7 @@ const sharp = require('../build/Release/sharp.node');
32
32
  */
33
33
  function toFile (fileOut, callback) {
34
34
  if (!fileOut || fileOut.length === 0) {
35
- const errOutputInvalid = new Error('Invalid output');
35
+ const errOutputInvalid = new Error('Missing output file path');
36
36
  if (is.fn(callback)) {
37
37
  callback(errOutputInvalid);
38
38
  } else {
@@ -175,30 +175,30 @@ function jpeg (options) {
175
175
  throw new Error('Invalid chromaSubsampling (4:2:0, 4:4:4) ' + options.chromaSubsampling);
176
176
  }
177
177
  }
178
- options.trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
179
- if (is.defined(options.trellisQuantisation)) {
180
- this._setBooleanOption('jpegTrellisQuantisation', options.trellisQuantisation);
178
+ const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
179
+ if (is.defined(trellisQuantisation)) {
180
+ this._setBooleanOption('jpegTrellisQuantisation', trellisQuantisation);
181
181
  }
182
182
  if (is.defined(options.overshootDeringing)) {
183
183
  this._setBooleanOption('jpegOvershootDeringing', options.overshootDeringing);
184
184
  }
185
- options.optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
186
- if (is.defined(options.optimiseScans)) {
187
- this._setBooleanOption('jpegOptimiseScans', options.optimiseScans);
188
- if (options.optimiseScans) {
185
+ const optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
186
+ if (is.defined(optimiseScans)) {
187
+ this._setBooleanOption('jpegOptimiseScans', optimiseScans);
188
+ if (optimiseScans) {
189
189
  this.options.jpegProgressive = true;
190
190
  }
191
191
  }
192
- options.optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
193
- if (is.defined(options.optimiseCoding)) {
194
- this._setBooleanOption('jpegOptimiseCoding', options.optimiseCoding);
192
+ const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
193
+ if (is.defined(optimiseCoding)) {
194
+ this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
195
195
  }
196
- options.quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
197
- if (is.defined(options.quantisationTable)) {
198
- if (is.integer(options.quantisationTable) && is.inRange(options.quantisationTable, 0, 8)) {
199
- this.options.jpegQuantisationTable = options.quantisationTable;
196
+ const quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
197
+ if (is.defined(quantisationTable)) {
198
+ if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
199
+ this.options.jpegQuantisationTable = quantisationTable;
200
200
  } else {
201
- throw new Error('Invalid quantisation table (integer, 0-8) ' + options.quantisationTable);
201
+ throw new Error('Invalid quantisation table (integer, 0-8) ' + quantisationTable);
202
202
  }
203
203
  }
204
204
  }
@@ -221,6 +221,11 @@ function jpeg (options) {
221
221
  * @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
222
222
  * @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9
223
223
  * @param {Boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
224
+ * @param {Boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libimagequant
225
+ * @param {Number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, requires libimagequant
226
+ * @param {Number} [options.colours=256] - maximum number of palette entries, requires libimagequant
227
+ * @param {Number} [options.colors=256] - alternative spelling of `options.colours`, requires libimagequant
228
+ * @param {Number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, requires libimagequant
224
229
  * @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
225
230
  * @returns {Sharp}
226
231
  * @throws {Error} Invalid options
@@ -240,6 +245,33 @@ function png (options) {
240
245
  if (is.defined(options.adaptiveFiltering)) {
241
246
  this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
242
247
  }
248
+ if (is.defined(options.palette)) {
249
+ this._setBooleanOption('pngPalette', options.palette);
250
+ if (this.options.pngPalette) {
251
+ if (is.defined(options.quality)) {
252
+ if (is.integer(options.quality) && is.inRange(options.quality, 0, 100)) {
253
+ this.options.pngQuality = options.quality;
254
+ } else {
255
+ throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
256
+ }
257
+ }
258
+ const colours = options.colours || options.colors;
259
+ if (is.defined(colours)) {
260
+ if (is.integer(colours) && is.inRange(colours, 2, 256)) {
261
+ this.options.pngColours = colours;
262
+ } else {
263
+ throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
264
+ }
265
+ }
266
+ if (is.defined(options.dither)) {
267
+ if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
268
+ this.options.pngDither = options.dither;
269
+ } else {
270
+ throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
271
+ }
272
+ }
273
+ }
274
+ }
243
275
  }
244
276
  return this._updateFormatOut('png', options);
245
277
  }
@@ -304,6 +336,10 @@ function webp (options) {
304
336
  * @param {Boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
305
337
  * @param {Boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
306
338
  * @param {Boolean} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
339
+ * @param {Boolean} [options.pyramid=false] - write an image pyramid
340
+ * @param {Boolean} [options.tile=false] - write a tiled tiff
341
+ * @param {Boolean} [options.tileWidth=256] - horizontal tile size
342
+ * @param {Boolean} [options.tileHeight=256] - vertical tile size
307
343
  * @param {Number} [options.xres=1.0] - horizontal resolution in pixels/mm
308
344
  * @param {Number} [options.yres=1.0] - vertical resolution in pixels/mm
309
345
  * @param {Boolean} [options.squash=false] - squash 8-bit images down to 1 bit
@@ -311,51 +347,83 @@ function webp (options) {
311
347
  * @throws {Error} Invalid options
312
348
  */
313
349
  function tiff (options) {
314
- if (is.object(options) && is.defined(options.quality)) {
315
- if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
316
- this.options.tiffQuality = options.quality;
317
- } else {
318
- throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
350
+ if (is.object(options)) {
351
+ if (is.defined(options.quality)) {
352
+ if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
353
+ this.options.tiffQuality = options.quality;
354
+ } else {
355
+ throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
356
+ }
319
357
  }
320
- }
321
- if (is.object(options) && is.defined(options.squash)) {
322
- if (is.bool(options.squash)) {
323
- this.options.tiffSquash = options.squash;
324
- } else {
325
- throw new Error('Invalid Value for squash ' + options.squash + ' Only Boolean Values allowed for options.squash.');
358
+ if (is.defined(options.squash)) {
359
+ if (is.bool(options.squash)) {
360
+ this.options.tiffSquash = options.squash;
361
+ } else {
362
+ throw new Error('Invalid Value for squash ' + options.squash + ' Only Boolean Values allowed for options.squash.');
363
+ }
326
364
  }
327
- }
328
- // resolution
329
- if (is.object(options) && is.defined(options.xres)) {
330
- if (is.number(options.xres)) {
331
- this.options.tiffXres = options.xres;
332
- } else {
333
- throw new Error('Invalid Value for xres ' + options.xres + ' Only numeric values allowed for options.xres');
365
+ // tiling
366
+ if (is.defined(options.tile)) {
367
+ if (is.bool(options.tile)) {
368
+ this.options.tiffTile = options.tile;
369
+ } else {
370
+ throw new Error('Invalid Value for tile ' + options.tile + ' Only Boolean values allowed for options.tile');
371
+ }
334
372
  }
335
- }
336
- if (is.object(options) && is.defined(options.yres)) {
337
- if (is.number(options.yres)) {
338
- this.options.tiffYres = options.yres;
339
- } else {
340
- throw new Error('Invalid Value for yres ' + options.yres + ' Only numeric values allowed for options.yres');
373
+ if (is.defined(options.tileWidth)) {
374
+ if (is.number(options.tileWidth) && options.tileWidth > 0) {
375
+ this.options.tiffTileWidth = options.tileWidth;
376
+ } else {
377
+ throw new Error('Invalid Value for tileWidth ' + options.tileWidth + ' Only positive numeric values allowed for options.tileWidth');
378
+ }
341
379
  }
342
- }
343
- // compression
344
- if (is.defined(options) && is.defined(options.compression)) {
345
- if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
346
- this.options.tiffCompression = options.compression;
347
- } else {
348
- const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, ccittfax4, none`;
349
- throw new Error(message);
380
+ if (is.defined(options.tileHeight)) {
381
+ if (is.number(options.tileHeight) && options.tileHeight > 0) {
382
+ this.options.tiffTileHeight = options.tileHeight;
383
+ } else {
384
+ throw new Error('Invalid Value for tileHeight ' + options.tileHeight + ' Only positive numeric values allowed for options.tileHeight');
385
+ }
350
386
  }
351
- }
352
- // predictor
353
- if (is.defined(options) && is.defined(options.predictor)) {
354
- if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
355
- this.options.tiffPredictor = options.predictor;
356
- } else {
357
- const message = `Invalid predictor option "${options.predictor}". Should be one of: none, horizontal, float`;
358
- throw new Error(message);
387
+ // pyramid
388
+ if (is.defined(options.pyramid)) {
389
+ if (is.bool(options.pyramid)) {
390
+ this.options.tiffPyramid = options.pyramid;
391
+ } else {
392
+ throw new Error('Invalid Value for pyramid ' + options.pyramid + ' Only Boolean values allowed for options.pyramid');
393
+ }
394
+ }
395
+ // resolution
396
+ if (is.defined(options.xres)) {
397
+ if (is.number(options.xres)) {
398
+ this.options.tiffXres = options.xres;
399
+ } else {
400
+ throw new Error('Invalid Value for xres ' + options.xres + ' Only numeric values allowed for options.xres');
401
+ }
402
+ }
403
+ if (is.defined(options.yres)) {
404
+ if (is.number(options.yres)) {
405
+ this.options.tiffYres = options.yres;
406
+ } else {
407
+ throw new Error('Invalid Value for yres ' + options.yres + ' Only numeric values allowed for options.yres');
408
+ }
409
+ }
410
+ // compression
411
+ if (is.defined(options.compression)) {
412
+ if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
413
+ this.options.tiffCompression = options.compression;
414
+ } else {
415
+ const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, ccittfax4, none`;
416
+ throw new Error(message);
417
+ }
418
+ }
419
+ // predictor
420
+ if (is.defined(options.predictor)) {
421
+ if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
422
+ this.options.tiffPredictor = options.predictor;
423
+ } else {
424
+ const message = `Invalid predictor option "${options.predictor}". Should be one of: none, horizontal, float`;
425
+ throw new Error(message);
426
+ }
359
427
  }
360
428
  }
361
429
  return this._updateFormatOut('tiff', options);
@@ -505,7 +573,9 @@ function tile (tile) {
505
573
  * @returns {Sharp}
506
574
  */
507
575
  function _updateFormatOut (formatOut, options) {
508
- this.options.formatOut = (is.object(options) && options.force === false) ? 'input' : formatOut;
576
+ if (!(is.object(options) && options.force === false)) {
577
+ this.options.formatOut = formatOut;
578
+ }
509
579
  return this;
510
580
  }
511
581
 
@@ -641,7 +711,7 @@ function _pipeline (callback) {
641
711
  * @private
642
712
  */
643
713
  module.exports = function (Sharp) {
644
- [
714
+ Object.assign(Sharp.prototype, {
645
715
  // Public
646
716
  toFile,
647
717
  toBuffer,
@@ -658,7 +728,5 @@ module.exports = function (Sharp) {
658
728
  _setBooleanOption,
659
729
  _read,
660
730
  _pipeline
661
- ].forEach(function (f) {
662
- Sharp.prototype[f.name] = f;
663
731
  });
664
732
  };
package/lib/platform.js CHANGED
@@ -1,10 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ const detectLibc = require('detect-libc');
4
+
3
5
  module.exports = function () {
4
6
  const arch = process.env.npm_config_arch || process.arch;
5
7
  const platform = process.env.npm_config_platform || process.platform;
8
+ const libc = (platform === 'linux' && detectLibc.isNonGlibcLinux) ? detectLibc.family : '';
6
9
 
7
- const platformId = [platform];
10
+ const platformId = [`${platform}${libc}`];
8
11
  if (arch === 'arm' || arch === 'armhf' || arch === 'arm64') {
9
12
  const armVersion = (arch === 'arm64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6';
10
13
  platformId.push(`armv${armVersion}`);