Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db654de385 | ||
|
|
a6aeef612b | ||
|
|
7bf6cbd669 | ||
|
|
04c31b35a7 | ||
|
|
ee9cdb6598 | ||
|
|
8960eb8309 | ||
|
|
54d9dc46f5 | ||
|
|
51b4a7c564 | ||
|
|
5b03579e5c | ||
|
|
58c2af3251 | ||
|
|
ee948ac6fa | ||
|
|
66a3ce5e55 | ||
|
|
75e5afcd42 | ||
|
|
d396a4e65d | ||
|
|
ae1dbcdf4e | ||
|
|
4c29368b51 | ||
|
|
36e55969d2 | ||
|
|
985e881e7a | ||
|
|
0b116715aa | ||
|
|
9deac83322 | ||
|
|
5d36f5f699 | ||
|
|
926572b41e | ||
|
|
d0c8e95641 | ||
|
|
b0ca23c3e7 | ||
|
|
c3a0d5f5d0 | ||
|
|
1d36936954 | ||
|
|
b609df4b48 | ||
|
|
c567d3b9ab | ||
|
|
27d9fe2a4e | ||
|
|
ac883c5215 | ||
|
|
e8720c9374 | ||
|
|
42e45d842a | ||
|
|
72fd8abe2c | ||
|
|
ac18bbbc7c | ||
|
|
fcbe4e1e01 | ||
|
|
9280742385 | ||
|
|
7a1a1cf9e8 | ||
|
|
ea599ade10 | ||
|
|
1de49f3ed8 | ||
|
|
4ac65054bc | ||
|
|
23033e2050 | ||
|
|
dd3b78272a |
@@ -1,15 +1,15 @@
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-0-snap
|
||||
image_family: freebsd-14-0-snap
|
||||
|
||||
task:
|
||||
name: FreeBSD 13.0
|
||||
name: FreeBSD
|
||||
env:
|
||||
IGNORE_OSVERSION: yes
|
||||
skip_notifications: true
|
||||
prerequisites_script:
|
||||
- pkg update -f
|
||||
- pkg upgrade -y
|
||||
- pkg install -y pkgconf vips node npm
|
||||
- pkg install -y devel/pkgconf graphics/vips www/node16 www/npm
|
||||
install_script:
|
||||
- npm install --build-from-source --unsafe-perm
|
||||
test_script:
|
||||
|
||||
2
.github/CONTRIBUTING.md
vendored
@@ -16,7 +16,7 @@ If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exist
|
||||
it's probably fastest to add a comment to it about your requirement.
|
||||
|
||||
Implementation is usually straightforward if libvips
|
||||
[already supports](https://libvips.github.io/libvips/API/current/func-list.html)
|
||||
[already supports](https://www.libvips.org/API/current/func-list.html)
|
||||
the feature you need.
|
||||
|
||||
## Submit a Pull Request to fix a bug
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/installation.md
vendored
@@ -32,11 +32,11 @@ If you are using npm v7 or later, does the user running `npm install` own the di
|
||||
|
||||
If you are using the `ignore-scripts` feature of `npm`, have you tried with the `npm install --ignore-scripts=false` flag?
|
||||
|
||||
### What is the complete output of running `npm install --verbose sharp`?
|
||||
### What is the complete output of running `npm install --verbose --foreground-scripts sharp` in an empty directory?
|
||||
|
||||
<details>
|
||||
|
||||
<!-- Please provide output of `npm install --verbose sharp` here. -->
|
||||
<!-- Please provide output of `npm install --verbose --foreground-scripts sharp` in an empty directory here. -->
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -9,7 +9,13 @@ An alpha channel may be present and will be unchanged by the operation.
|
||||
|
||||
* `rgb` **([string][1] | [Object][2])** parsed by the [color][3] module to extract chroma values.
|
||||
|
||||
<!---->
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input)
|
||||
.tint({ r: 255, g: 240, b: 16 })
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
* Throws **[Error][4]** Invalid parameter
|
||||
|
||||
@@ -28,6 +34,12 @@ An alpha channel may be present, and will be unchanged by the operation.
|
||||
|
||||
* `greyscale` **[Boolean][5]** (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input).greyscale().toBuffer();
|
||||
```
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## grayscale
|
||||
|
||||
@@ -14,7 +14,7 @@ The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
|
||||
`hard-light`, `soft-light`, `difference`, `exclusion`.
|
||||
|
||||
More information about blend modes can be found at
|
||||
[https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode][1]
|
||||
[https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode][1]
|
||||
and [https://www.cairographics.org/operators/][2]
|
||||
|
||||
### Parameters
|
||||
@@ -41,11 +41,29 @@ and [https://www.cairographics.org/operators/][2]
|
||||
* `images[].raw.width` **[Number][7]?**
|
||||
* `images[].raw.height` **[Number][7]?**
|
||||
* `images[].raw.channels` **[Number][7]?**
|
||||
* `images[].failOnError` **[boolean][9]** @see [constructor parameters][10] (optional, default `true`)
|
||||
* `images[].animated` **[boolean][9]** Set to `true` to read all frames/pages of an animated image. (optional, default `false`)
|
||||
* `images[].failOn` **[string][6]** @see [constructor parameters][10] (optional, default `'warning'`)
|
||||
* `images[].limitInputPixels` **([number][7] | [boolean][9])** @see [constructor parameters][10] (optional, default `268402689`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
await sharp(background)
|
||||
.composite([
|
||||
{ input: layer1, gravity: 'northwest' },
|
||||
{ input: layer2, gravity: 'southeast' },
|
||||
])
|
||||
.toFile('combined.png');
|
||||
```
|
||||
|
||||
```javascript
|
||||
const output = await sharp('input.gif', { animated: true })
|
||||
.composite([
|
||||
{ input: 'overlay.png', tile: true, blend: 'saturate' }
|
||||
])
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
```javascript
|
||||
sharp('input.png')
|
||||
.rotate(180)
|
||||
@@ -71,7 +89,7 @@ Returns **Sharp**
|
||||
|
||||
* **since**: 0.22.0
|
||||
|
||||
[1]: https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
|
||||
[1]: https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
|
||||
|
||||
[2]: https://www.cairographics.org/operators/
|
||||
|
||||
|
||||
@@ -20,38 +20,37 @@ Implements the [stream.Duplex][1] class.
|
||||
JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||
* `options` **[Object][13]?** if present, is an Object with optional attributes.
|
||||
|
||||
* `options.failOnError` **[boolean][14]** by default halt processing and raise an error when loading invalid images.
|
||||
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
|
||||
* `options.limitInputPixels` **([number][15] | [boolean][14])** Do not process input images where the number of pixels
|
||||
* `options.failOn` **[string][12]** level of sensitivity to invalid images, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels. (optional, default `'warning'`)
|
||||
* `options.limitInputPixels` **([number][14] | [boolean][15])** Do not process input images where the number of pixels
|
||||
(width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
||||
An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default `268402689`)
|
||||
* `options.unlimited` **[boolean][14]** Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG). (optional, default `false`)
|
||||
* `options.sequentialRead` **[boolean][14]** Set this to `true` to use sequential rather than random access where possible.
|
||||
* `options.unlimited` **[boolean][15]** Set this to `true` to remove safety features that help prevent memory exhaustion (SVG, PNG). (optional, default `false`)
|
||||
* `options.sequentialRead` **[boolean][15]** Set this to `true` to use sequential rather than random access where possible.
|
||||
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
|
||||
* `options.density` **[number][15]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
|
||||
* `options.pages` **[number][15]** number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
||||
* `options.page` **[number][15]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
|
||||
* `options.subifd` **[number][15]** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default `-1`)
|
||||
* `options.level` **[number][15]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
||||
* `options.animated` **[boolean][14]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
|
||||
* `options.density` **[number][14]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
|
||||
* `options.pages` **[number][14]** number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
||||
* `options.page` **[number][14]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
|
||||
* `options.subifd` **[number][14]** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default `-1`)
|
||||
* `options.level` **[number][14]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
||||
* `options.animated` **[boolean][15]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
|
||||
* `options.raw` **[Object][13]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||
|
||||
* `options.raw.width` **[number][15]?** integral number of pixels wide.
|
||||
* `options.raw.height` **[number][15]?** integral number of pixels high.
|
||||
* `options.raw.channels` **[number][15]?** integral number of channels, between 1 and 4.
|
||||
* `options.raw.premultiplied` **[boolean][14]?** specifies that the raw input has already been premultiplied, set to `true`
|
||||
* `options.raw.width` **[number][14]?** integral number of pixels wide.
|
||||
* `options.raw.height` **[number][14]?** integral number of pixels high.
|
||||
* `options.raw.channels` **[number][14]?** integral number of channels, between 1 and 4.
|
||||
* `options.raw.premultiplied` **[boolean][15]?** specifies that the raw input has already been premultiplied, set to `true`
|
||||
to avoid sharp premultiplying the image. (optional, default `false`)
|
||||
* `options.create` **[Object][13]?** describes a new image to be created.
|
||||
|
||||
* `options.create.width` **[number][15]?** integral number of pixels wide.
|
||||
* `options.create.height` **[number][15]?** integral number of pixels high.
|
||||
* `options.create.channels` **[number][15]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
|
||||
* `options.create.width` **[number][14]?** integral number of pixels wide.
|
||||
* `options.create.height` **[number][14]?** integral number of pixels high.
|
||||
* `options.create.channels` **[number][14]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
|
||||
* `options.create.background` **([string][12] | [Object][13])?** parsed by the [color][16] module to extract values for red, green, blue and alpha.
|
||||
* `options.create.noise` **[Object][13]?** describes a noise to be created.
|
||||
|
||||
* `options.create.noise.type` **[string][12]?** type of generated noise, currently only `gaussian` is supported.
|
||||
* `options.create.noise.mean` **[number][15]?** mean of pixels in generated noise.
|
||||
* `options.create.noise.sigma` **[number][15]?** standard deviation of pixels in generated noise.
|
||||
* `options.create.noise.mean` **[number][14]?** mean of pixels in generated noise.
|
||||
* `options.create.noise.sigma` **[number][14]?** standard deviation of pixels in generated noise.
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -154,9 +153,7 @@ readableStream.pipe(pipeline);
|
||||
// Using Promises to know when the pipeline is complete
|
||||
const fs = require("fs");
|
||||
const got = require("got");
|
||||
const sharpStream = sharp({
|
||||
failOnError: false
|
||||
});
|
||||
const sharpStream = sharp({ failOn: 'none' });
|
||||
|
||||
const promises = [];
|
||||
|
||||
@@ -226,9 +223,9 @@ Returns **[Sharp][18]**
|
||||
|
||||
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||
|
||||
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
|
||||
[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
|
||||
[16]: https://www.npmjs.org/package/color
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ A `Promise` is returned when `callback` is not provided.
|
||||
|
||||
* `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
|
||||
* `size`: Total size of image in bytes, for Stream and Buffer input only
|
||||
* `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
|
||||
* `height`: Number of pixels high (EXIF orientation is not taken into consideration)
|
||||
* `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
|
||||
* `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
|
||||
* `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][1]
|
||||
* `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
||||
* `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...][2]
|
||||
@@ -63,6 +63,18 @@ image
|
||||
});
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Based on EXIF rotation metadata, get the right-side-up width and height:
|
||||
|
||||
const size = getNormalSize(await sharp(input).metadata());
|
||||
|
||||
function getNormalSize({ width, height, orientation }) {
|
||||
return orientation || 0 >= 5
|
||||
? { width: height, height: width }
|
||||
: { width, height };
|
||||
}
|
||||
```
|
||||
|
||||
Returns **([Promise][5]<[Object][6]> | Sharp)**
|
||||
|
||||
## stats
|
||||
@@ -82,9 +94,9 @@ A `Promise` is returned when `callback` is not provided.
|
||||
* `maxX` (x-coordinate of one of the pixel where the maximum lies)
|
||||
* `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
||||
* `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
|
||||
* `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
||||
* `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
|
||||
* `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
|
||||
* `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any.
|
||||
* `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any.
|
||||
* `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram.
|
||||
|
||||
**Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
|
||||
written to a buffer in order to run `stats` on the result (see third example).
|
||||
@@ -119,9 +131,9 @@ const stats = await sharp(part).stats();
|
||||
|
||||
Returns **[Promise][5]<[Object][6]>**
|
||||
|
||||
[1]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation
|
||||
[1]: https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation
|
||||
|
||||
[2]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat
|
||||
[2]: https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat
|
||||
|
||||
[3]: https://www.npmjs.com/package/icc
|
||||
|
||||
|
||||
@@ -53,6 +53,12 @@ The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
||||
|
||||
* `flip` **[Boolean][6]** (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input).flip().toBuffer();
|
||||
```
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## flop
|
||||
@@ -64,6 +70,12 @@ The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
||||
|
||||
* `flop` **[Boolean][6]** (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input).flop().toBuffer();
|
||||
```
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## affine
|
||||
@@ -129,13 +141,43 @@ When used without parameters, performs a fast, mild sharpen of the output image.
|
||||
When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
|
||||
Separate control over the level of sharpening in "flat" and "jagged" areas is available.
|
||||
|
||||
See [libvips sharpen][8] operation.
|
||||
|
||||
### Parameters
|
||||
|
||||
* `sigma` **[number][1]?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||
* `flat` **[number][1]** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
|
||||
* `jagged` **[number][1]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
|
||||
* `options` **([Object][2] | [number][1])?** if present, is an Object with attributes or (deprecated) a number for `options.sigma`.
|
||||
|
||||
<!---->
|
||||
* `options.sigma` **[number][1]?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||
* `options.m1` **[number][1]** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
|
||||
* `options.m2` **[number][1]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
|
||||
* `options.x1` **[number][1]** threshold between "flat" and "jagged" (optional, default `2.0`)
|
||||
* `options.y2` **[number][1]** maximum amount of brightening. (optional, default `10.0`)
|
||||
* `options.y3` **[number][1]** maximum amount of darkening. (optional, default `20.0`)
|
||||
* `flat` **[number][1]?** (deprecated) see `options.m1`.
|
||||
* `jagged` **[number][1]?** (deprecated) see `options.m2`.
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const data = await sharp(input).sharpen().toBuffer();
|
||||
```
|
||||
|
||||
```javascript
|
||||
const data = await sharp(input).sharpen({ sigma: 2 }).toBuffer();
|
||||
```
|
||||
|
||||
```javascript
|
||||
const data = await sharp(input)
|
||||
.sharpen({
|
||||
sigma: 2,
|
||||
m1: 0
|
||||
m2: 3,
|
||||
x1: 3,
|
||||
y2: 15,
|
||||
y3: 15,
|
||||
})
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
* Throws **[Error][5]** Invalid parameters
|
||||
|
||||
@@ -150,7 +192,15 @@ When used without parameters the default window is 3x3.
|
||||
|
||||
* `size` **[number][1]** square mask size: size x size (optional, default `3`)
|
||||
|
||||
<!---->
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input).median().toBuffer();
|
||||
```
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input).median(5).toBuffer();
|
||||
```
|
||||
|
||||
* Throws **[Error][5]** Invalid parameters
|
||||
|
||||
@@ -190,7 +240,7 @@ Returns **Sharp**
|
||||
|
||||
Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
||||
|
||||
See also [removeAlpha][8].
|
||||
See also [removeAlpha][9].
|
||||
|
||||
### Parameters
|
||||
|
||||
@@ -239,6 +289,20 @@ Produce the "negative" of the image.
|
||||
|
||||
* `options.alpha` **[Boolean][6]** Whether or not to negate any alpha channel (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input)
|
||||
.negate()
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input)
|
||||
.negate({ alpha: false })
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## normalise
|
||||
@@ -249,6 +313,12 @@ Enhance output image contrast by stretching its luminance to cover the full dyna
|
||||
|
||||
* `normalise` **[Boolean][6]** (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input).normalise().toBuffer();
|
||||
```
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## normalize
|
||||
@@ -259,12 +329,18 @@ Alternative spelling of normalise.
|
||||
|
||||
* `normalize` **[Boolean][6]** (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input).normalize().toBuffer();
|
||||
```
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## clahe
|
||||
|
||||
Perform contrast limiting adaptive histogram equalization
|
||||
[CLAHE][9].
|
||||
[CLAHE][10].
|
||||
|
||||
This will, in general, enhance the clarity of the image by bringing out darker details.
|
||||
|
||||
@@ -278,7 +354,16 @@ This will, in general, enhance the clarity of the image by bringing out darker d
|
||||
cumulative histogram. A value of 0 disables contrast limiting. Valid values
|
||||
are integers in the range 0-100 (inclusive) (optional, default `3`)
|
||||
|
||||
<!---->
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
const output = await sharp(input)
|
||||
.clahe({
|
||||
width: 3,
|
||||
height: 3,
|
||||
})
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
* Throws **[Error][5]** Invalid parameters
|
||||
|
||||
@@ -349,7 +434,7 @@ the selected bitwise boolean `operation` between the corresponding pixels of the
|
||||
|
||||
### Parameters
|
||||
|
||||
* `operand` **([Buffer][10] | [string][3])** Buffer containing image data or string containing the path to an image file.
|
||||
* `operand` **([Buffer][11] | [string][3])** Buffer containing image data or string containing the path to an image file.
|
||||
* `operator` **[string][3]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
|
||||
* `options` **[Object][2]?**
|
||||
|
||||
@@ -430,28 +515,41 @@ brightness is multiplicative whereas lightness is additive.
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
sharp(input)
|
||||
// increase brightness by a factor of 2
|
||||
const output = await sharp(input)
|
||||
.modulate({
|
||||
brightness: 2 // increase brightness by a factor of 2
|
||||
});
|
||||
brightness: 2
|
||||
})
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
sharp(input)
|
||||
```javascript
|
||||
// hue-rotate by 180 degrees
|
||||
const output = await sharp(input)
|
||||
.modulate({
|
||||
hue: 180 // hue-rotate by 180 degrees
|
||||
});
|
||||
hue: 180
|
||||
})
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
sharp(input)
|
||||
```javascript
|
||||
// increase lightness by +50
|
||||
const output = await sharp(input)
|
||||
.modulate({
|
||||
lightness: 50 // increase lightness by +50
|
||||
});
|
||||
lightness: 50
|
||||
})
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
```javascript
|
||||
// decreate brightness and saturation while also hue-rotating by 90 degrees
|
||||
sharp(input)
|
||||
const output = await sharp(input)
|
||||
.modulate({
|
||||
brightness: 0.5,
|
||||
saturation: 0.5,
|
||||
hue: 90
|
||||
});
|
||||
hue: 90,
|
||||
})
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
Returns **Sharp**
|
||||
@@ -474,8 +572,10 @@ Returns **Sharp**
|
||||
|
||||
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
||||
|
||||
[8]: /api-channel#removealpha
|
||||
[8]: https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen
|
||||
|
||||
[9]: https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE
|
||||
[9]: /api-channel#removealpha
|
||||
|
||||
[10]: https://nodejs.org/api/buffer.html
|
||||
[10]: https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE
|
||||
|
||||
[11]: https://nodejs.org/api/buffer.html
|
||||
|
||||
@@ -46,6 +46,8 @@ Returns **[Promise][5]<[Object][6]>** when no callback is provided
|
||||
Write output to a Buffer.
|
||||
JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
||||
|
||||
Use [toFormat][7] or one of the format-specific functions such as [jpeg][8], [png][9] etc. to set the output format.
|
||||
|
||||
If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
||||
|
||||
By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
@@ -66,7 +68,7 @@ A `Promise` is returned when `callback` is not provided.
|
||||
|
||||
* `options` **[Object][6]?**
|
||||
|
||||
* `options.resolveWithObject` **[boolean][7]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
||||
* `options.resolveWithObject` **[boolean][10]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
||||
* `callback` **[Function][3]?**
|
||||
|
||||
### Examples
|
||||
@@ -85,6 +87,7 @@ sharp(input)
|
||||
|
||||
```javascript
|
||||
sharp(input)
|
||||
.png()
|
||||
.toBuffer({ resolveWithObject: true })
|
||||
.then(({ data, info }) => { ... })
|
||||
.catch(err => { ... });
|
||||
@@ -107,7 +110,7 @@ await sharp(pixelArray, { raw: { width, height, channels } })
|
||||
.toFile('my-changed-image.jpg');
|
||||
```
|
||||
|
||||
Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
|
||||
Returns **[Promise][5]<[Buffer][11]>** when no callback is provided
|
||||
|
||||
## withMetadata
|
||||
|
||||
@@ -118,14 +121,16 @@ output profile is provided.
|
||||
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
||||
sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
||||
|
||||
EXIF metadata is unsupported for TIFF output.
|
||||
|
||||
### Parameters
|
||||
|
||||
* `options` **[Object][6]?**
|
||||
|
||||
* `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
||||
* `options.orientation` **[number][12]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
||||
* `options.icc` **[string][2]?** filesystem path to output ICC profile, defaults to sRGB.
|
||||
* `options.exif` **[Object][6]<[Object][6]>** Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data. (optional, default `{}`)
|
||||
* `options.density` **[number][9]?** Number of pixels per inch (DPI).
|
||||
* `options.density` **[number][12]?** Number of pixels per inch (DPI).
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -190,19 +195,19 @@ Use these JPEG options for output image.
|
||||
|
||||
* `options` **[Object][6]?** output options
|
||||
|
||||
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
||||
* `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
|
||||
* `options.quality` **[number][12]** quality, integer 1-100 (optional, default `80`)
|
||||
* `options.progressive` **[boolean][10]** use progressive (interlace) scan (optional, default `false`)
|
||||
* `options.chromaSubsampling` **[string][2]** set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling (optional, default `'4:2:0'`)
|
||||
* `options.optimiseCoding` **[boolean][7]** optimise Huffman coding tables (optional, default `true`)
|
||||
* `options.optimizeCoding` **[boolean][7]** alternative spelling of optimiseCoding (optional, default `true`)
|
||||
* `options.mozjpeg` **[boolean][7]** use mozjpeg defaults, equivalent to `{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }` (optional, default `false`)
|
||||
* `options.trellisQuantisation` **[boolean][7]** apply trellis quantisation (optional, default `false`)
|
||||
* `options.overshootDeringing` **[boolean][7]** apply overshoot deringing (optional, default `false`)
|
||||
* `options.optimiseScans` **[boolean][7]** optimise progressive scans, forces progressive (optional, default `false`)
|
||||
* `options.optimizeScans` **[boolean][7]** alternative spelling of optimiseScans (optional, default `false`)
|
||||
* `options.quantisationTable` **[number][9]** quantization table to use, integer 0-8 (optional, default `0`)
|
||||
* `options.quantizationTable` **[number][9]** alternative spelling of quantisationTable (optional, default `0`)
|
||||
* `options.force` **[boolean][7]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
|
||||
* `options.optimiseCoding` **[boolean][10]** optimise Huffman coding tables (optional, default `true`)
|
||||
* `options.optimizeCoding` **[boolean][10]** alternative spelling of optimiseCoding (optional, default `true`)
|
||||
* `options.mozjpeg` **[boolean][10]** use mozjpeg defaults, equivalent to `{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }` (optional, default `false`)
|
||||
* `options.trellisQuantisation` **[boolean][10]** apply trellis quantisation (optional, default `false`)
|
||||
* `options.overshootDeringing` **[boolean][10]** apply overshoot deringing (optional, default `false`)
|
||||
* `options.optimiseScans` **[boolean][10]** optimise progressive scans, forces progressive (optional, default `false`)
|
||||
* `options.optimizeScans` **[boolean][10]** alternative spelling of optimiseScans (optional, default `false`)
|
||||
* `options.quantisationTable` **[number][12]** quantization table to use, integer 0-8 (optional, default `0`)
|
||||
* `options.quantizationTable` **[number][12]** alternative spelling of quantisationTable (optional, default `0`)
|
||||
* `options.force` **[boolean][10]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -239,16 +244,16 @@ Set `palette` to `true` for slower, indexed PNG output.
|
||||
|
||||
* `options` **[Object][6]?**
|
||||
|
||||
* `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
|
||||
* `options.compressionLevel` **[number][9]** zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) (optional, default `6`)
|
||||
* `options.adaptiveFiltering` **[boolean][7]** use adaptive row filtering (optional, default `false`)
|
||||
* `options.palette` **[boolean][7]** quantise to a palette-based image with alpha transparency support (optional, default `false`)
|
||||
* `options.quality` **[number][9]** use the lowest number of colours needed to achieve given quality, sets `palette` to `true` (optional, default `100`)
|
||||
* `options.effort` **[number][9]** CPU effort, between 1 (fastest) and 10 (slowest), sets `palette` to `true` (optional, default `7`)
|
||||
* `options.colours` **[number][9]** maximum number of palette entries, sets `palette` to `true` (optional, default `256`)
|
||||
* `options.colors` **[number][9]** alternative spelling of `options.colours`, sets `palette` to `true` (optional, default `256`)
|
||||
* `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, sets `palette` to `true` (optional, default `1.0`)
|
||||
* `options.force` **[boolean][7]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
||||
* `options.progressive` **[boolean][10]** use progressive (interlace) scan (optional, default `false`)
|
||||
* `options.compressionLevel` **[number][12]** zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) (optional, default `6`)
|
||||
* `options.adaptiveFiltering` **[boolean][10]** use adaptive row filtering (optional, default `false`)
|
||||
* `options.palette` **[boolean][10]** quantise to a palette-based image with alpha transparency support (optional, default `false`)
|
||||
* `options.quality` **[number][12]** use the lowest number of colours needed to achieve given quality, sets `palette` to `true` (optional, default `100`)
|
||||
* `options.effort` **[number][12]** CPU effort, between 1 (fastest) and 10 (slowest), sets `palette` to `true` (optional, default `7`)
|
||||
* `options.colours` **[number][12]** maximum number of palette entries, sets `palette` to `true` (optional, default `256`)
|
||||
* `options.colors` **[number][12]** alternative spelling of `options.colours`, sets `palette` to `true` (optional, default `256`)
|
||||
* `options.dither` **[number][12]** level of Floyd-Steinberg error diffusion, sets `palette` to `true` (optional, default `1.0`)
|
||||
* `options.force` **[boolean][10]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -278,15 +283,15 @@ Use these WebP options for output image.
|
||||
|
||||
* `options` **[Object][6]?** output options
|
||||
|
||||
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
||||
* `options.alphaQuality` **[number][9]** quality of alpha layer, integer 0-100 (optional, default `100`)
|
||||
* `options.lossless` **[boolean][7]** use lossless compression mode (optional, default `false`)
|
||||
* `options.nearLossless` **[boolean][7]** use near_lossless compression mode (optional, default `false`)
|
||||
* `options.smartSubsample` **[boolean][7]** use high quality chroma subsampling (optional, default `false`)
|
||||
* `options.effort` **[number][9]** CPU effort, between 0 (fastest) and 6 (slowest) (optional, default `4`)
|
||||
* `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
|
||||
* `options.delay` **([number][9] | [Array][10]<[number][9]>)?** delay(s) between animation frames (in milliseconds)
|
||||
* `options.force` **[boolean][7]** force WebP output, otherwise attempt to use input format (optional, default `true`)
|
||||
* `options.quality` **[number][12]** quality, integer 1-100 (optional, default `80`)
|
||||
* `options.alphaQuality` **[number][12]** quality of alpha layer, integer 0-100 (optional, default `100`)
|
||||
* `options.lossless` **[boolean][10]** use lossless compression mode (optional, default `false`)
|
||||
* `options.nearLossless` **[boolean][10]** use near_lossless compression mode (optional, default `false`)
|
||||
* `options.smartSubsample` **[boolean][10]** use high quality chroma subsampling (optional, default `false`)
|
||||
* `options.effort` **[number][12]** CPU effort, between 0 (fastest) and 6 (slowest) (optional, default `4`)
|
||||
* `options.loop` **[number][12]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
|
||||
* `options.delay` **([number][12] | [Array][13]<[number][12]>)?** delay(s) between animation frames (in milliseconds)
|
||||
* `options.force` **[boolean][10]** force WebP output, otherwise attempt to use input format (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -318,13 +323,13 @@ The first entry in the palette is reserved for transparency.
|
||||
|
||||
* `options` **[Object][6]?** output options
|
||||
|
||||
* `options.colours` **[number][9]** maximum number of palette entries, including transparency, between 2 and 256 (optional, default `256`)
|
||||
* `options.colors` **[number][9]** alternative spelling of `options.colours` (optional, default `256`)
|
||||
* `options.effort` **[number][9]** CPU effort, between 1 (fastest) and 10 (slowest) (optional, default `7`)
|
||||
* `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) (optional, default `1.0`)
|
||||
* `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
|
||||
* `options.delay` **([number][9] | [Array][10]<[number][9]>)?** delay(s) between animation frames (in milliseconds)
|
||||
* `options.force` **[boolean][7]** force GIF output, otherwise attempt to use input format (optional, default `true`)
|
||||
* `options.colours` **[number][12]** maximum number of palette entries, including transparency, between 2 and 256 (optional, default `256`)
|
||||
* `options.colors` **[number][12]** alternative spelling of `options.colours` (optional, default `256`)
|
||||
* `options.effort` **[number][12]** CPU effort, between 1 (fastest) and 10 (slowest) (optional, default `7`)
|
||||
* `options.dither` **[number][12]** level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) (optional, default `1.0`)
|
||||
* `options.loop` **[number][12]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
|
||||
* `options.delay` **([number][12] | [Array][13]<[number][12]>)?** delay(s) between animation frames (in milliseconds)
|
||||
* `options.force` **[boolean][10]** force GIF output, otherwise attempt to use input format (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -332,7 +337,7 @@ The first entry in the palette is reserved for transparency.
|
||||
// Convert PNG to GIF
|
||||
await sharp(pngBuffer)
|
||||
.gif()
|
||||
.toBuffer());
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
```javascript
|
||||
@@ -363,16 +368,16 @@ Use these JP2 options for output image.
|
||||
|
||||
Requires libvips compiled with support for OpenJPEG.
|
||||
The prebuilt binaries do not include this - see
|
||||
[installing a custom libvips][11].
|
||||
[installing a custom libvips][14].
|
||||
|
||||
### Parameters
|
||||
|
||||
* `options` **[Object][6]?** output options
|
||||
|
||||
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
||||
* `options.lossless` **[boolean][7]** use lossless compression mode (optional, default `false`)
|
||||
* `options.tileWidth` **[number][9]** horizontal tile size (optional, default `512`)
|
||||
* `options.tileHeight` **[number][9]** vertical tile size (optional, default `512`)
|
||||
* `options.quality` **[number][12]** quality, integer 1-100 (optional, default `80`)
|
||||
* `options.lossless` **[boolean][10]** use lossless compression mode (optional, default `false`)
|
||||
* `options.tileWidth` **[number][12]** horizontal tile size (optional, default `512`)
|
||||
* `options.tileHeight` **[number][12]** vertical tile size (optional, default `512`)
|
||||
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
|
||||
|
||||
### Examples
|
||||
@@ -412,18 +417,18 @@ The `density` can be set in pixels/inch via [withMetadata][1] instead of providi
|
||||
|
||||
* `options` **[Object][6]?** output options
|
||||
|
||||
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
||||
* `options.force` **[boolean][7]** force TIFF output, otherwise attempt to use input format (optional, default `true`)
|
||||
* `options.quality` **[number][12]** quality, integer 1-100 (optional, default `80`)
|
||||
* `options.force` **[boolean][10]** force TIFF output, otherwise attempt to use input format (optional, default `true`)
|
||||
* `options.compression` **[string][2]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
|
||||
* `options.predictor` **[string][2]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
|
||||
* `options.pyramid` **[boolean][7]** write an image pyramid (optional, default `false`)
|
||||
* `options.tile` **[boolean][7]** write a tiled tiff (optional, default `false`)
|
||||
* `options.tileWidth` **[number][9]** horizontal tile size (optional, default `256`)
|
||||
* `options.tileHeight` **[number][9]** vertical tile size (optional, default `256`)
|
||||
* `options.xres` **[number][9]** horizontal resolution in pixels/mm (optional, default `1.0`)
|
||||
* `options.yres` **[number][9]** vertical resolution in pixels/mm (optional, default `1.0`)
|
||||
* `options.pyramid` **[boolean][10]** write an image pyramid (optional, default `false`)
|
||||
* `options.tile` **[boolean][10]** write a tiled tiff (optional, default `false`)
|
||||
* `options.tileWidth` **[number][12]** horizontal tile size (optional, default `256`)
|
||||
* `options.tileHeight` **[number][12]** vertical tile size (optional, default `256`)
|
||||
* `options.xres` **[number][12]** horizontal resolution in pixels/mm (optional, default `1.0`)
|
||||
* `options.yres` **[number][12]** vertical resolution in pixels/mm (optional, default `1.0`)
|
||||
* `options.resolutionUnit` **[string][2]** resolution unit options: inch, cm (optional, default `'inch'`)
|
||||
* `options.bitdepth` **[number][9]** reduce bitdepth to 1, 2 or 4 bit (optional, default `8`)
|
||||
* `options.bitdepth` **[number][12]** reduce bitdepth to 1, 2 or 4 bit (optional, default `8`)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -455,9 +460,9 @@ AVIF image sequences are not supported.
|
||||
|
||||
* `options` **[Object][6]?** output options
|
||||
|
||||
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
|
||||
* `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
|
||||
* `options.effort` **[number][9]** CPU effort, between 0 (fastest) and 9 (slowest) (optional, default `4`)
|
||||
* `options.quality` **[number][12]** quality, integer 1-100 (optional, default `50`)
|
||||
* `options.lossless` **[boolean][10]** use lossless compression (optional, default `false`)
|
||||
* `options.effort` **[number][12]** CPU effort, between 0 (fastest) and 9 (slowest) (optional, default `4`)
|
||||
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
|
||||
|
||||
<!---->
|
||||
@@ -481,10 +486,10 @@ globally-installed libvips compiled with support for libheif, libde265 and x265.
|
||||
|
||||
* `options` **[Object][6]?** output options
|
||||
|
||||
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
|
||||
* `options.quality` **[number][12]** quality, integer 1-100 (optional, default `50`)
|
||||
* `options.compression` **[string][2]** compression format: av1, hevc (optional, default `'av1'`)
|
||||
* `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
|
||||
* `options.effort` **[number][9]** CPU effort, between 0 (fastest) and 9 (slowest) (optional, default `4`)
|
||||
* `options.lossless` **[boolean][10]** use lossless compression (optional, default `false`)
|
||||
* `options.effort` **[number][12]** CPU effort, between 0 (fastest) and 9 (slowest) (optional, default `4`)
|
||||
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
|
||||
|
||||
<!---->
|
||||
@@ -540,16 +545,16 @@ Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed arc
|
||||
|
||||
* `options` **[Object][6]?**
|
||||
|
||||
* `options.size` **[number][9]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
|
||||
* `options.overlap` **[number][9]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
|
||||
* `options.angle` **[number][9]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
|
||||
* `options.background` **([string][2] | [Object][6])** background colour, parsed by the [color][12] module, defaults to white without transparency. (optional, default `{r:255,g:255,b:255,alpha:1}`)
|
||||
* `options.size` **[number][12]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
|
||||
* `options.overlap` **[number][12]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
|
||||
* `options.angle` **[number][12]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
|
||||
* `options.background` **([string][2] | [Object][6])** background colour, parsed by the [color][15] module, defaults to white without transparency. (optional, default `{r:255,g:255,b:255,alpha:1}`)
|
||||
* `options.depth` **[string][2]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
|
||||
* `options.skipBlanks` **[number][9]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`)
|
||||
* `options.skipBlanks` **[number][12]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`)
|
||||
* `options.container` **[string][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
|
||||
* `options.layout` **[string][2]** filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`. (optional, default `'dz'`)
|
||||
* `options.centre` **[boolean][7]** centre image in tile. (optional, default `false`)
|
||||
* `options.center` **[boolean][7]** alternative spelling of centre. (optional, default `false`)
|
||||
* `options.centre` **[boolean][10]** centre image in tile. (optional, default `false`)
|
||||
* `options.center` **[boolean][10]** alternative spelling of centre. (optional, default `false`)
|
||||
* `options.id` **[string][2]** when `layout` is `iiif`/`iiif3`, sets the `@id`/`id` attribute of `info.json` (optional, default `'https://example.com/iiif'`)
|
||||
|
||||
### Examples
|
||||
@@ -582,7 +587,7 @@ Time spent waiting for a libuv thread to become available is not included.
|
||||
|
||||
* `options` **[Object][6]**
|
||||
|
||||
* `options.seconds` **[number][9]** Number of seconds after which processing will be stopped
|
||||
* `options.seconds` **[number][12]** Number of seconds after which processing will be stopped
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
@@ -602,14 +607,20 @@ Returns **Sharp**
|
||||
|
||||
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||
|
||||
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
[7]: #toformat
|
||||
|
||||
[8]: https://nodejs.org/api/buffer.html
|
||||
[8]: #jpeg
|
||||
|
||||
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
[9]: #png
|
||||
|
||||
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
||||
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
|
||||
[11]: https://sharp.pixelplumbing.com/install#custom-libvips
|
||||
[11]: https://nodejs.org/api/buffer.html
|
||||
|
||||
[12]: https://www.npmjs.org/package/color
|
||||
[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
|
||||
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
||||
|
||||
[14]: https://sharp.pixelplumbing.com/install#custom-libvips
|
||||
|
||||
[15]: https://www.npmjs.org/package/color
|
||||
|
||||
@@ -95,7 +95,11 @@ Returns **[Object][1]**
|
||||
## concurrency
|
||||
|
||||
Gets or, when a concurrency is provided, sets
|
||||
the number of threads *libvips'* should create to process each image.
|
||||
the maximum number of threads *libvips* should use to process *each image*.
|
||||
These are from a thread pool managed by glib,
|
||||
which helps avoid the overhead of creating new threads.
|
||||
|
||||
This method always returns the current concurrency.
|
||||
|
||||
The default value is the number of CPU cores,
|
||||
except when using glibc-based Linux without jemalloc,
|
||||
@@ -103,10 +107,19 @@ where the default is `1` to help reduce memory fragmentation.
|
||||
|
||||
A value of `0` will reset this to the number of CPU cores.
|
||||
|
||||
The maximum number of images that can be processed in parallel
|
||||
is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
|
||||
Some image format libraries spawn additional threads,
|
||||
e.g. libaom manages its own 4 threads when encoding AVIF images,
|
||||
and these are independent of the value set here.
|
||||
|
||||
This method always returns the current concurrency.
|
||||
The maximum number of images that sharp can process in parallel
|
||||
is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
|
||||
which defaults to 4.
|
||||
|
||||
[https://nodejs.org/api/cli.html#uv_threadpool_sizesize][12]
|
||||
|
||||
For example, by default, a machine with 8 CPU cores will process
|
||||
4 images in parallel and use up to 8 threads per image,
|
||||
so there will be up to 32 concurrent threads.
|
||||
|
||||
### Parameters
|
||||
|
||||
@@ -199,3 +212,5 @@ Returns **[boolean][10]**
|
||||
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
|
||||
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
|
||||
[12]: https://nodejs.org/api/cli.html#uv_threadpool_sizesize
|
||||
|
||||
@@ -4,6 +4,48 @@
|
||||
|
||||
Requires libvips v8.12.2
|
||||
|
||||
### v0.30.5 - 23rd May 2022
|
||||
|
||||
* Install: pass `PKG_CONFIG_PATH` via env rather than substitution.
|
||||
[@dwisiswant0](https://github.com/dwisiswant0)
|
||||
|
||||
* Allow installation of prebuilt libvips binaries from filesystem.
|
||||
[#3196](https://github.com/lovell/sharp/pull/3196)
|
||||
[@ankurparihar](https://github.com/ankurparihar)
|
||||
|
||||
* Fix rotate-then-extract for EXIF orientation 2.
|
||||
[#3218](https://github.com/lovell/sharp/pull/3218)
|
||||
[@jakob0fischl](https://github.com/jakob0fischl)
|
||||
|
||||
### v0.30.4 - 18th April 2022
|
||||
|
||||
* Increase control over sensitivity to invalid images via `failOn`, deprecate `failOnError` (equivalent to `failOn: 'warning'`).
|
||||
|
||||
* Ensure `create` input image has correct bit depth and colour space.
|
||||
[#3139](https://github.com/lovell/sharp/issues/3139)
|
||||
|
||||
* Add support for `TypedArray` input with `byteOffset` and `length`.
|
||||
[#3146](https://github.com/lovell/sharp/pull/3146)
|
||||
[@codepage949](https://github.com/codepage949)
|
||||
|
||||
* Improve error message when attempting to render SVG input greater than 32767x32767.
|
||||
[#3167](https://github.com/lovell/sharp/issues/3167)
|
||||
|
||||
* Add missing file name to 'Input file is missing' error message.
|
||||
[#3178](https://github.com/lovell/sharp/pull/3178)
|
||||
[@Brodan](https://github.com/Brodan)
|
||||
|
||||
### v0.30.3 - 14th March 2022
|
||||
|
||||
* Allow `sharpen` options to be provided more consistently as an Object.
|
||||
[#2561](https://github.com/lovell/sharp/issues/2561)
|
||||
|
||||
* Expose `x1`, `y2` and `y3` parameters of `sharpen` operation.
|
||||
[#2935](https://github.com/lovell/sharp/issues/2935)
|
||||
|
||||
* Prevent double unpremultiply with some composite blend modes (regression in 0.30.2).
|
||||
[#3118](https://github.com/lovell/sharp/issues/3118)
|
||||
|
||||
### v0.30.2 - 2nd March 2022
|
||||
|
||||
* Improve performance and accuracy when compositing multiple images.
|
||||
|
||||
@@ -236,3 +236,12 @@ GitHub: https://github.com/gforge
|
||||
|
||||
Name: Chris Banks
|
||||
GitHub: https://github.com/christopherbradleybanks
|
||||
|
||||
Name: codepage949
|
||||
GitHub: https://github.com/codepage949
|
||||
|
||||
Name: Chris Hranj
|
||||
GitHub: https://github.com/Brodan
|
||||
|
||||
Name: Ankur Parihar
|
||||
GitHub: https://github.com/ankurparihar
|
||||
|
||||
BIN
docs/image/sharp-logo.png
Normal file
|
After Width: | Height: | Size: 661 B |
@@ -6,19 +6,13 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="Resize large images in common formats to smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; object-src 'none'; style-src 'unsafe-inline';
|
||||
img-src 'unsafe-inline' data: https://pixel.plumbing/px/ https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
||||
img-src 'unsafe-inline' data: https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
||||
connect-src 'self' https://www.google-analytics.com;
|
||||
script-src 'self' 'unsafe-inline' 'unsafe-eval'
|
||||
https://www.google-analytics.com/analytics.js;">
|
||||
<link rel="icon" type="image/png" href="https://pixel.plumbing/px/32x32/sharp-logo.svg">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="https://pixel.plumbing/px/152x152/sharp-logo.svg">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="https://pixel.plumbing/px/144x144/sharp-logo.svg">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="https://pixel.plumbing/px/120x120/sharp-logo.svg">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="https://pixel.plumbing/px/114x114/sharp-logo.svg">
|
||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/sharp-logo.svg">
|
||||
<link rel="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
|
||||
<link rel="icon" type="image/svg+xml" href="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.png">
|
||||
<link rel="author" href="/humans.txt" type="text/plain">
|
||||
<link rel="dns-prefetch" href="https://pixel.plumbing">
|
||||
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
@@ -124,7 +118,7 @@
|
||||
router: { mode: 'history' },
|
||||
logo: '<div style="display:flex;align-items:center">'
|
||||
+ '<strong>sharp</strong> '
|
||||
+ '<img src="https://pixel.plumbing/px/16x16/sharp-logo.svg" style="padding:8px" alt="#"> '
|
||||
+ '<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" style="padding:8px" width="32" height="32" alt="#"> '
|
||||
+ '<span style="opacity:0.8;white-space:pre" class="shorten-strapline">High performance </span> '
|
||||
+ '<span style="opacity:0.8">Node.js image processing</span> '
|
||||
+ '</div>',
|
||||
|
||||
@@ -57,7 +57,7 @@ When using npm v7 or later, the user running `npm install` must own the director
|
||||
|
||||
The `npm install --ignore-scripts=false` flag must be used when `npm` has been configured to ignore installation scripts.
|
||||
|
||||
Check the output of running `npm install --verbose sharp` for useful error messages.
|
||||
Check the output of running `npm install --verbose --foreground-scripts sharp` for useful error messages.
|
||||
|
||||
## Apple M1
|
||||
|
||||
@@ -74,20 +74,28 @@ The target platform and/or architecture can be manually selected using the follo
|
||||
npm install --platform=... --arch=... --arm-version=... sharp
|
||||
```
|
||||
|
||||
* `--platform`: one of `linux`, `linuxmusl`, `darwin` or `win32`.
|
||||
* `--platform`: one of `linux`, `darwin` or `win32`.
|
||||
* `--arch`: one of `x64`, `ia32`, `arm` or `arm64`.
|
||||
* `--arm-version`: one of `6`, `7` or `8` (`arm` defaults to `6`, `arm64` defaults to `8`).
|
||||
* `--libc`: one of `glibc` or `musl`. This option only works with platform `linux`, defaults to `glibc`
|
||||
* `--sharp-install-force`: skip version compatibility and Subresource Integrity checks.
|
||||
|
||||
These values can also be set via environment variables,
|
||||
`npm_config_platform`, `npm_config_arch`, `npm_config_arm_version`
|
||||
`npm_config_platform`, `npm_config_arch`, `npm_config_arm_version`, `npm_config_libc`
|
||||
and `SHARP_INSTALL_FORCE` respectively.
|
||||
|
||||
For example, if the target machine has a 64-bit ARM CPU and is running Alpine Linux,
|
||||
use the following flags:
|
||||
|
||||
```sh
|
||||
npm install --arch=arm64 --platform=linuxmusl sharp
|
||||
npm install --arch=arm64 --platform=linux --libc=musl sharp
|
||||
```
|
||||
|
||||
If the current machine is Alpine Linux and the target machine is Debian Linux on x64 cpu,
|
||||
use the following flags:
|
||||
|
||||
```sh
|
||||
npm install --arch=x64 --platform=linux --libc=glibc sharp
|
||||
```
|
||||
|
||||
## Custom libvips
|
||||
@@ -96,8 +104,8 @@ To use a custom, globally-installed version of libvips instead of the provided b
|
||||
make sure it is at least the version listed under `config.libvips` in the `package.json` file
|
||||
and that it can be located using `pkg-config --modversion vips-cpp`.
|
||||
|
||||
For help compiling libvips from source, please see
|
||||
[https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball](https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball).
|
||||
For help compiling libvips and its dependencies, please see
|
||||
[building libvips from source](https://www.libvips.org/install.html#building-libvips-from-source).
|
||||
|
||||
The use of a globally-installed libvips is unsupported on Windows.
|
||||
|
||||
@@ -118,6 +126,8 @@ Building from source requires:
|
||||
|
||||
This is an advanced approach that most people will not require.
|
||||
|
||||
### Prebuilt sharp binaries
|
||||
|
||||
To install the prebuilt sharp binaries from a custom URL,
|
||||
set the `sharp_binary_host` npm config option
|
||||
or the `npm_config_sharp_binary_host` environment variable.
|
||||
@@ -126,10 +136,16 @@ To install the prebuilt sharp binaries from a directory on the local filesystem,
|
||||
set the `sharp_local_prebuilds` npm config option
|
||||
or the `npm_config_sharp_local_prebuilds` environment variable.
|
||||
|
||||
### Prebuilt libvips binaries
|
||||
|
||||
To install the prebuilt libvips binaries from a custom URL,
|
||||
set the `sharp_libvips_binary_host` npm config option
|
||||
or the `npm_config_sharp_libvips_binary_host` environment variable.
|
||||
|
||||
To install the prebuilt libvips binaries from a directory on the local filesystem,
|
||||
set the `sharp_libvips_local_prebuilds` npm config option
|
||||
or the `npm_config_sharp_libvips_local_prebuilds` environment variable.
|
||||
|
||||
The version subpath and file name are appended to these.
|
||||
For example, if `sharp_libvips_binary_host` is set to `https://hostname/path`
|
||||
and the libvips version is `1.2.3` then the resultant URL will be
|
||||
@@ -207,15 +223,18 @@ run the following additional command after `npm install`:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux sharp
|
||||
rm -rf node_modules/sharp
|
||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux --libc=glibc sharp
|
||||
```
|
||||
|
||||
To get the best performance select the largest memory available.
|
||||
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
|
||||
|
||||
## Webpack
|
||||
## Bundlers
|
||||
|
||||
Ensure sharp is added to the
|
||||
### webpack
|
||||
|
||||
Ensure sharp is excluded from bundling via the
|
||||
[externals](https://webpack.js.org/configuration/externals/)
|
||||
configuration.
|
||||
|
||||
@@ -225,13 +244,38 @@ externals: {
|
||||
}
|
||||
```
|
||||
|
||||
### esbuild
|
||||
|
||||
Ensure sharp is excluded from bundling via the
|
||||
[external](https://esbuild.github.io/api/#external)
|
||||
configuration.
|
||||
|
||||
```js
|
||||
buildSync({
|
||||
entryPoints: ['app.js'],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
external: ['sharp'],
|
||||
})
|
||||
```
|
||||
|
||||
```sh
|
||||
esbuild app.js --bundle --platform=node --external:sharp
|
||||
```
|
||||
|
||||
## Worker threads
|
||||
|
||||
The main thread must call `require('sharp')`
|
||||
before worker threads are created
|
||||
to ensure shared libraries remain loaded in memory
|
||||
On some platforms, including glibc-based Linux,
|
||||
the main thread must call `require('sharp')`
|
||||
_before_ worker threads are created.
|
||||
This is to ensure shared libraries remain loaded in memory
|
||||
until after all threads are complete.
|
||||
|
||||
Without this, the following error may occur:
|
||||
```
|
||||
Module did not self-register
|
||||
```
|
||||
|
||||
## Known conflicts
|
||||
|
||||
### Canvas and Windows
|
||||
|
||||
@@ -9,7 +9,7 @@ const extractDescription = (str) =>
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/[^A-Za-z0-9_/\-,. ]/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.substr(0, 180)
|
||||
.substring(0, 200)
|
||||
.trim();
|
||||
|
||||
const extractParameters = (str) =>
|
||||
|
||||
@@ -21,6 +21,7 @@ module.exports = [
|
||||
'can',
|
||||
'containing',
|
||||
'contains',
|
||||
'created',
|
||||
'current',
|
||||
'date',
|
||||
'default',
|
||||
@@ -43,6 +44,8 @@ module.exports = [
|
||||
'how',
|
||||
'image',
|
||||
'implies',
|
||||
'include',
|
||||
'including',
|
||||
'involve',
|
||||
'its',
|
||||
'last',
|
||||
@@ -69,6 +72,7 @@ module.exports = [
|
||||
'provided',
|
||||
'ready',
|
||||
'requires',
|
||||
'requiresharp',
|
||||
'returned',
|
||||
'same',
|
||||
'see',
|
||||
@@ -77,6 +81,7 @@ module.exports = [
|
||||
'should',
|
||||
'since',
|
||||
'site',
|
||||
'some',
|
||||
'specified',
|
||||
'spelling',
|
||||
'such',
|
||||
|
||||
@@ -35,6 +35,7 @@ const hasSharpPrebuild = [
|
||||
];
|
||||
|
||||
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
||||
const localLibvipsDir = process.env.npm_config_sharp_libvips_local_prebuilds || '';
|
||||
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
||||
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
||||
const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
|
||||
@@ -42,7 +43,9 @@ const installationForced = !!(process.env.npm_config_sharp_install_force || proc
|
||||
const fail = function (err) {
|
||||
libvips.log(err);
|
||||
if (err.code === 'EACCES') {
|
||||
libvips.log('Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
|
||||
libvips.log('Are you trying to install as a root or sudo user?');
|
||||
libvips.log('- For npm <= v6, try again with the "--unsafe-perm" flag');
|
||||
libvips.log('- For npm >= v8, the user must own the directory "npm install" is run in');
|
||||
}
|
||||
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
|
||||
process.exit(1);
|
||||
@@ -156,6 +159,11 @@ try {
|
||||
if (fs.existsSync(tarPathCache)) {
|
||||
libvips.log(`Using cached ${tarPathCache}`);
|
||||
extractTarball(tarPathCache, platformAndArch);
|
||||
} else if (localLibvipsDir) {
|
||||
// If localLibvipsDir is given try to use binaries from local directory
|
||||
const tarPathLocal = path.join(path.resolve(localLibvipsDir), `v${minimumLibvipsVersionLabelled}`, tarFilename);
|
||||
libvips.log(`Using local libvips from ${tarPathLocal}`);
|
||||
extractTarball(tarPathLocal, platformAndArch);
|
||||
} else {
|
||||
const url = distBaseUrl + tarFilename;
|
||||
libvips.log(`Downloading ${url}`);
|
||||
|
||||
@@ -19,6 +19,11 @@ const colourspace = {
|
||||
* Tint the image using the provided chroma while preserving the image luminance.
|
||||
* An alpha channel may be present and will be unchanged by the operation.
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input)
|
||||
* .tint({ r: 255, g: 240, b: 16 })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {string|Object} rgb - parsed by the [color](https://www.npmjs.org/package/color) module to extract chroma values.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameter
|
||||
@@ -37,6 +42,10 @@ function tint (rgb) {
|
||||
* This may be overridden by other sharp operations such as `toColourspace('b-w')`,
|
||||
* which will produce an output image containing one color channel.
|
||||
* An alpha channel may be present, and will be unchanged by the operation.
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input).greyscale().toBuffer();
|
||||
*
|
||||
* @param {Boolean} [greyscale=true]
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
|
||||
@@ -50,12 +50,27 @@ const blend = {
|
||||
* `hard-light`, `soft-light`, `difference`, `exclusion`.
|
||||
*
|
||||
* More information about blend modes can be found at
|
||||
* https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
|
||||
* https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
|
||||
* and https://www.cairographics.org/operators/
|
||||
*
|
||||
* @since 0.22.0
|
||||
*
|
||||
* @example
|
||||
* await sharp(background)
|
||||
* .composite([
|
||||
* { input: layer1, gravity: 'northwest' },
|
||||
* { input: layer2, gravity: 'southeast' },
|
||||
* ])
|
||||
* .toFile('combined.png');
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp('input.gif', { animated: true })
|
||||
* .composite([
|
||||
* { input: 'overlay.png', tile: true, blend: 'saturate' }
|
||||
* ])
|
||||
* .toBuffer();
|
||||
*
|
||||
* @example
|
||||
* sharp('input.png')
|
||||
* .rotate(180)
|
||||
* .resize(300)
|
||||
@@ -89,7 +104,8 @@ const blend = {
|
||||
* @param {Number} [images[].raw.width]
|
||||
* @param {Number} [images[].raw.height]
|
||||
* @param {Number} [images[].raw.channels]
|
||||
* @param {boolean} [images[].failOnError=true] - @see {@link /api-constructor#parameters|constructor parameters}
|
||||
* @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
|
||||
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor#parameters|constructor parameters}
|
||||
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
|
||||
@@ -98,8 +98,7 @@ const debuglog = util.debuglog('sharp');
|
||||
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
||||
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||
* @param {Object} [options] - if present, is an Object with optional attributes.
|
||||
* @param {boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
|
||||
* Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid.
|
||||
* @param {string} [options.failOn='warning'] - level of sensitivity to invalid images, one of (in order of sensitivity): 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels.
|
||||
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
|
||||
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
||||
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
||||
@@ -186,8 +185,11 @@ const Sharp = function (input, options) {
|
||||
medianSize: 0,
|
||||
blurSigma: 0,
|
||||
sharpenSigma: 0,
|
||||
sharpenFlat: 1,
|
||||
sharpenJagged: 2,
|
||||
sharpenM1: 1,
|
||||
sharpenM2: 2,
|
||||
sharpenX1: 2,
|
||||
sharpenY2: 10,
|
||||
sharpenY3: 20,
|
||||
threshold: 0,
|
||||
thresholdGrayscale: true,
|
||||
trimThreshold: 0,
|
||||
@@ -317,9 +319,7 @@ Object.setPrototypeOf(Sharp, stream.Duplex);
|
||||
* // Using Promises to know when the pipeline is complete
|
||||
* const fs = require("fs");
|
||||
* const got = require("got");
|
||||
* const sharpStream = sharp({
|
||||
* failOnError: false
|
||||
* });
|
||||
* const sharpStream = sharp({ failOn: 'none' });
|
||||
*
|
||||
* const promises = [];
|
||||
*
|
||||
|
||||
47
lib/input.js
@@ -9,9 +9,9 @@ const sharp = require('./sharp');
|
||||
* @private
|
||||
*/
|
||||
function _inputOptionsFromObject (obj) {
|
||||
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd } = obj;
|
||||
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined)
|
||||
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOnError, animated, page, pages, subifd }
|
||||
const { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd } = obj;
|
||||
return [raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd].some(is.defined)
|
||||
? { raw, density, limitInputPixels, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd }
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ function _inputOptionsFromObject (obj) {
|
||||
*/
|
||||
function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
const inputDescriptor = {
|
||||
failOnError: true,
|
||||
failOn: 'warning',
|
||||
limitInputPixels: Math.pow(0x3FFF, 2),
|
||||
unlimited: false,
|
||||
sequentialRead: false
|
||||
@@ -39,7 +39,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
if (input.length === 0) {
|
||||
throw Error('Input Bit Array is empty');
|
||||
}
|
||||
inputDescriptor.buffer = Buffer.from(input.buffer);
|
||||
inputDescriptor.buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
||||
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
||||
// Plain Object descriptor, e.g. create
|
||||
inputOptions = input;
|
||||
@@ -56,14 +56,22 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
}`);
|
||||
}
|
||||
if (is.object(inputOptions)) {
|
||||
// Fail on error
|
||||
// Deprecated: failOnError
|
||||
if (is.defined(inputOptions.failOnError)) {
|
||||
if (is.bool(inputOptions.failOnError)) {
|
||||
inputDescriptor.failOnError = inputOptions.failOnError;
|
||||
inputDescriptor.failOn = inputOptions.failOnError ? 'warning' : 'none';
|
||||
} else {
|
||||
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
|
||||
}
|
||||
}
|
||||
// failOn
|
||||
if (is.defined(inputOptions.failOn)) {
|
||||
if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {
|
||||
inputDescriptor.failOn = inputOptions.failOn;
|
||||
} else {
|
||||
throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);
|
||||
}
|
||||
}
|
||||
// Density
|
||||
if (is.defined(inputOptions.density)) {
|
||||
if (is.inRange(inputOptions.density, 1, 100000)) {
|
||||
@@ -299,11 +307,11 @@ function _isStreamInput () {
|
||||
*
|
||||
* - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
|
||||
* - `size`: Total size of image in bytes, for Stream and Buffer input only
|
||||
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
|
||||
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration)
|
||||
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation)
|
||||
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
|
||||
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
|
||||
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
|
||||
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
||||
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat)
|
||||
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
|
||||
* - `density`: Number of pixels per inch (DPI), if present
|
||||
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||
@@ -343,6 +351,17 @@ function _isStreamInput () {
|
||||
* // data contains a WebP image half the width and height of the original JPEG
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // Based on EXIF rotation metadata, get the right-side-up width and height:
|
||||
*
|
||||
* const size = getNormalSize(await sharp(input).metadata());
|
||||
*
|
||||
* function getNormalSize({ width, height, orientation }) {
|
||||
* return orientation || 0 >= 5
|
||||
* ? { width: height, height: width }
|
||||
* : { width, height };
|
||||
* }
|
||||
*
|
||||
* @param {Function} [callback] - called with the arguments `(err, metadata)`
|
||||
* @returns {Promise<Object>|Sharp}
|
||||
*/
|
||||
@@ -401,9 +420,9 @@ function metadata (callback) {
|
||||
* - `maxX` (x-coordinate of one of the pixel where the maximum lies)
|
||||
* - `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
||||
* - `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
|
||||
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
||||
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
|
||||
* - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
|
||||
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any.
|
||||
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any.
|
||||
* - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram.
|
||||
*
|
||||
* **Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
|
||||
* written to a buffer in order to run `stats` on the result (see third example).
|
||||
|
||||
@@ -65,7 +65,12 @@ const isRosetta = function () {
|
||||
|
||||
const globalLibvipsVersion = function () {
|
||||
if (process.platform !== 'win32') {
|
||||
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
|
||||
const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
|
||||
...spawnSyncOptions,
|
||||
env: {
|
||||
PKG_CONFIG_PATH: pkgConfigPath()
|
||||
}
|
||||
}).stdout;
|
||||
/* istanbul ignore next */
|
||||
return (globalLibvipsVersion || '').trim();
|
||||
} else {
|
||||
@@ -85,7 +90,10 @@ const removeVendoredLibvips = function () {
|
||||
|
||||
const pkgConfigPath = function () {
|
||||
if (process.platform !== 'win32') {
|
||||
const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
|
||||
const brewPkgConfigPath = spawnSync(
|
||||
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
|
||||
spawnSyncOptions
|
||||
).stdout || '';
|
||||
return [
|
||||
brewPkgConfigPath.trim(),
|
||||
env.PKG_CONFIG_PATH,
|
||||
|
||||
175
lib/operation.js
@@ -63,6 +63,10 @@ function rotate (angle, options) {
|
||||
/**
|
||||
* Flip the image about the vertical Y axis. This always occurs after rotation, if any.
|
||||
* The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input).flip().toBuffer();
|
||||
*
|
||||
* @param {Boolean} [flip=true]
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
@@ -74,6 +78,10 @@ function flip (flip) {
|
||||
/**
|
||||
* Flop the image about the horizontal X axis. This always occurs after rotation, if any.
|
||||
* The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input).flop().toBuffer();
|
||||
*
|
||||
* @param {Boolean} [flop=true]
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
@@ -185,40 +193,107 @@ function affine (matrix, options) {
|
||||
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
|
||||
* Separate control over the level of sharpening in "flat" and "jagged" areas is available.
|
||||
*
|
||||
* @param {number} [sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||
* @param {number} [flat=1.0] - the level of sharpening to apply to "flat" areas.
|
||||
* @param {number} [jagged=2.0] - the level of sharpening to apply to "jagged" areas.
|
||||
* See {@link https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen|libvips sharpen} operation.
|
||||
*
|
||||
* @example
|
||||
* const data = await sharp(input).sharpen().toBuffer();
|
||||
*
|
||||
* @example
|
||||
* const data = await sharp(input).sharpen({ sigma: 2 }).toBuffer();
|
||||
*
|
||||
* @example
|
||||
* const data = await sharp(input)
|
||||
* .sharpen({
|
||||
* sigma: 2,
|
||||
* m1: 0
|
||||
* m2: 3,
|
||||
* x1: 3,
|
||||
* y2: 15,
|
||||
* y3: 15,
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object|number} [options] - if present, is an Object with attributes or (deprecated) a number for `options.sigma`.
|
||||
* @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||
* @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas.
|
||||
* @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas.
|
||||
* @param {number} [options.x1=2.0] - threshold between "flat" and "jagged"
|
||||
* @param {number} [options.y2=10.0] - maximum amount of brightening.
|
||||
* @param {number} [options.y3=20.0] - maximum amount of darkening.
|
||||
* @param {number} [flat] - (deprecated) see `options.m1`.
|
||||
* @param {number} [jagged] - (deprecated) see `options.m2`.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
function sharpen (sigma, flat, jagged) {
|
||||
if (!is.defined(sigma)) {
|
||||
function sharpen (options, flat, jagged) {
|
||||
if (!is.defined(options)) {
|
||||
// No arguments: default to mild sharpen
|
||||
this.options.sharpenSigma = -1;
|
||||
} else if (is.bool(sigma)) {
|
||||
// Boolean argument: apply mild sharpen?
|
||||
this.options.sharpenSigma = sigma ? -1 : 0;
|
||||
} else if (is.number(sigma) && is.inRange(sigma, 0.01, 10000)) {
|
||||
// Numeric argument: specific sigma
|
||||
this.options.sharpenSigma = sigma;
|
||||
// Control over flat areas
|
||||
} else if (is.bool(options)) {
|
||||
// Deprecated boolean argument: apply mild sharpen?
|
||||
this.options.sharpenSigma = options ? -1 : 0;
|
||||
} else if (is.number(options) && is.inRange(options, 0.01, 10000)) {
|
||||
// Deprecated numeric argument: specific sigma
|
||||
this.options.sharpenSigma = options;
|
||||
// Deprecated control over flat areas
|
||||
if (is.defined(flat)) {
|
||||
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
|
||||
this.options.sharpenFlat = flat;
|
||||
this.options.sharpenM1 = flat;
|
||||
} else {
|
||||
throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
|
||||
}
|
||||
}
|
||||
// Control over jagged areas
|
||||
// Deprecated control over jagged areas
|
||||
if (is.defined(jagged)) {
|
||||
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
|
||||
this.options.sharpenJagged = jagged;
|
||||
this.options.sharpenM2 = jagged;
|
||||
} else {
|
||||
throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
|
||||
}
|
||||
}
|
||||
} else if (is.plainObject(options)) {
|
||||
if (is.number(options.sigma) && is.inRange(options.sigma, 0.01, 10000)) {
|
||||
this.options.sharpenSigma = options.sigma;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.sigma', 'number between 0.01 and 10000', options.sigma);
|
||||
}
|
||||
if (is.defined(options.m1)) {
|
||||
if (is.number(options.m1) && is.inRange(options.m1, 0, 10000)) {
|
||||
this.options.sharpenM1 = options.m1;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.m1', 'number between 0 and 10000', options.m1);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.m2)) {
|
||||
if (is.number(options.m2) && is.inRange(options.m2, 0, 10000)) {
|
||||
this.options.sharpenM2 = options.m2;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.m2', 'number between 0 and 10000', options.m2);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.x1)) {
|
||||
if (is.number(options.x1) && is.inRange(options.x1, 0, 10000)) {
|
||||
this.options.sharpenX1 = options.x1;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.x1', 'number between 0 and 10000', options.x1);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.y2)) {
|
||||
if (is.number(options.y2) && is.inRange(options.y2, 0, 10000)) {
|
||||
this.options.sharpenY2 = options.y2;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.y2', 'number between 0 and 10000', options.y2);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.y3)) {
|
||||
if (is.number(options.y3) && is.inRange(options.y3, 0, 10000)) {
|
||||
this.options.sharpenY3 = options.y3;
|
||||
} else {
|
||||
throw is.invalidParameterError('options.y3', 'number between 0 and 10000', options.y3);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', sigma);
|
||||
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', options);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -226,6 +301,13 @@ function sharpen (sigma, flat, jagged) {
|
||||
/**
|
||||
* Apply median filter.
|
||||
* When used without parameters the default window is 3x3.
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input).median().toBuffer();
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input).median(5).toBuffer();
|
||||
*
|
||||
* @param {number} [size=3] square mask size: size x size
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
@@ -338,6 +420,17 @@ function gamma (gamma, gammaOut) {
|
||||
|
||||
/**
|
||||
* Produce the "negative" of the image.
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input)
|
||||
* .negate()
|
||||
* .toBuffer();
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input)
|
||||
* .negate({ alpha: false })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.alpha=true] Whether or not to negate any alpha channel
|
||||
* @returns {Sharp}
|
||||
@@ -356,6 +449,10 @@ function negate (options) {
|
||||
|
||||
/**
|
||||
* Enhance output image contrast by stretching its luminance to cover the full dynamic range.
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input).normalise().toBuffer();
|
||||
*
|
||||
* @param {Boolean} [normalise=true]
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
@@ -366,6 +463,10 @@ function normalise (normalise) {
|
||||
|
||||
/**
|
||||
* Alternative spelling of normalise.
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input).normalize().toBuffer();
|
||||
*
|
||||
* @param {Boolean} [normalize=true]
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
@@ -381,6 +482,14 @@ function normalize (normalize) {
|
||||
*
|
||||
* @since 0.28.3
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input)
|
||||
* .clahe({
|
||||
* width: 3,
|
||||
* height: 3,
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {number} options.width - integer width of the region in pixels.
|
||||
* @param {number} options.height - integer height of the region in pixels.
|
||||
@@ -590,28 +699,38 @@ function recomb (inputMatrix) {
|
||||
* @since 0.22.1
|
||||
*
|
||||
* @example
|
||||
* sharp(input)
|
||||
* // increase brightness by a factor of 2
|
||||
* const output = await sharp(input)
|
||||
* .modulate({
|
||||
* brightness: 2 // increase brightness by a factor of 2
|
||||
* });
|
||||
* brightness: 2
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* sharp(input)
|
||||
* @example
|
||||
* // hue-rotate by 180 degrees
|
||||
* const output = await sharp(input)
|
||||
* .modulate({
|
||||
* hue: 180 // hue-rotate by 180 degrees
|
||||
* });
|
||||
* hue: 180
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* sharp(input)
|
||||
* @example
|
||||
* // increase lightness by +50
|
||||
* const output = await sharp(input)
|
||||
* .modulate({
|
||||
* lightness: 50 // increase lightness by +50
|
||||
* });
|
||||
* lightness: 50
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @example
|
||||
* // decreate brightness and saturation while also hue-rotating by 90 degrees
|
||||
* sharp(input)
|
||||
* const output = await sharp(input)
|
||||
* .modulate({
|
||||
* brightness: 0.5,
|
||||
* saturation: 0.5,
|
||||
* hue: 90
|
||||
* });
|
||||
* hue: 90,
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {number} [options.brightness] Brightness multiplier
|
||||
|
||||
@@ -82,6 +82,8 @@ function toFile (fileOut, callback) {
|
||||
* Write output to a Buffer.
|
||||
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
||||
*
|
||||
* Use {@link toFormat} or one of the format-specific functions such as {@link jpeg}, {@link png} etc. to set the output format.
|
||||
*
|
||||
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
||||
*
|
||||
* By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
@@ -108,6 +110,7 @@ function toFile (fileOut, callback) {
|
||||
*
|
||||
* @example
|
||||
* sharp(input)
|
||||
* .png()
|
||||
* .toBuffer({ resolveWithObject: true })
|
||||
* .then(({ data, info }) => { ... })
|
||||
* .catch(err => { ... });
|
||||
@@ -151,6 +154,8 @@ function toBuffer (options, callback) {
|
||||
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
||||
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
||||
*
|
||||
* EXIF metadata is unsupported for TIFF output.
|
||||
*
|
||||
* @example
|
||||
* sharp('input.jpg')
|
||||
* .withMetadata()
|
||||
@@ -514,7 +519,7 @@ function webp (options) {
|
||||
* // Convert PNG to GIF
|
||||
* await sharp(pngBuffer)
|
||||
* .gif()
|
||||
* .toBuffer());
|
||||
* .toBuffer();
|
||||
*
|
||||
* @example
|
||||
* // Convert animated WebP to animated GIF
|
||||
|
||||
@@ -7,10 +7,12 @@ const env = process.env;
|
||||
module.exports = function () {
|
||||
const arch = env.npm_config_arch || process.arch;
|
||||
const platform = env.npm_config_platform || process.platform;
|
||||
/* istanbul ignore next */
|
||||
const libc = (platform === 'linux' && detectLibc.isNonGlibcLinuxSync()) ? detectLibc.familySync() : '';
|
||||
const libc = process.env.npm_config_libc ||
|
||||
/* istanbul ignore next */
|
||||
(detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '');
|
||||
const libcId = platform !== 'linux' || libc === detectLibc.GLIBC ? '' : libc;
|
||||
|
||||
const platformId = [`${platform}${libc}`];
|
||||
const platformId = [`${platform}${libcId}`];
|
||||
|
||||
if (arch === 'arm') {
|
||||
const fallback = process.versions.electron ? '7' : '6';
|
||||
|
||||
@@ -13,7 +13,7 @@ try {
|
||||
} else {
|
||||
const [platform, arch] = platformAndArch.split('-');
|
||||
help.push(
|
||||
'- Install with the --verbose flag and look for errors: "npm install --ignore-scripts=false --verbose sharp"',
|
||||
'- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"',
|
||||
`- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"`
|
||||
);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ try {
|
||||
'- Consult the installation documentation: https://sharp.pixelplumbing.com/install'
|
||||
);
|
||||
// Check loaded
|
||||
if (process.platform === 'win32') {
|
||||
if (process.platform === 'win32' || /symbol/.test(err.message)) {
|
||||
const loadedModule = Object.keys(require.cache).find((i) => /[\\/]build[\\/]Release[\\/]sharp(.*)\.node$/.test(i));
|
||||
if (loadedModule) {
|
||||
const [, loadedPackage] = loadedModule.match(/node_modules[\\/]([^\\/]+)[\\/]/);
|
||||
|
||||
@@ -103,7 +103,11 @@ cache(true);
|
||||
|
||||
/**
|
||||
* Gets or, when a concurrency is provided, sets
|
||||
* the number of threads _libvips'_ should create to process each image.
|
||||
* the maximum number of threads _libvips_ should use to process _each image_.
|
||||
* These are from a thread pool managed by glib,
|
||||
* which helps avoid the overhead of creating new threads.
|
||||
*
|
||||
* This method always returns the current concurrency.
|
||||
*
|
||||
* The default value is the number of CPU cores,
|
||||
* except when using glibc-based Linux without jemalloc,
|
||||
@@ -111,10 +115,19 @@ cache(true);
|
||||
*
|
||||
* A value of `0` will reset this to the number of CPU cores.
|
||||
*
|
||||
* The maximum number of images that can be processed in parallel
|
||||
* is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
|
||||
* Some image format libraries spawn additional threads,
|
||||
* e.g. libaom manages its own 4 threads when encoding AVIF images,
|
||||
* and these are independent of the value set here.
|
||||
*
|
||||
* This method always returns the current concurrency.
|
||||
* The maximum number of images that sharp can process in parallel
|
||||
* is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
|
||||
* which defaults to 4.
|
||||
*
|
||||
* https://nodejs.org/api/cli.html#uv_threadpool_sizesize
|
||||
*
|
||||
* For example, by default, a machine with 8 CPU cores will process
|
||||
* 4 images in parallel and use up to 8 threads per image,
|
||||
* so there will be up to 32 concurrent threads.
|
||||
*
|
||||
* @example
|
||||
* const threads = sharp.concurrency(); // 4
|
||||
|
||||
16
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
||||
"version": "0.30.2",
|
||||
"version": "0.30.5",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://github.com/lovell/sharp",
|
||||
"contributors": [
|
||||
@@ -81,7 +81,9 @@
|
||||
"Taneli Vatanen <taneli.vatanen@gmail.com>",
|
||||
"Joris Dugué <zaruike10@gmail.com>",
|
||||
"Chris Banks <christopher.bradley.banks@gmail.com>",
|
||||
"Ompal Singh <ompal.hitm09@gmail.com>"
|
||||
"Ompal Singh <ompal.hitm09@gmail.com>",
|
||||
"Brodan <christopher.hranj@gmail.com",
|
||||
"Ankur Parihar <ankur.github@gmail.com>"
|
||||
],
|
||||
"scripts": {
|
||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
|
||||
@@ -126,11 +128,11 @@
|
||||
"vips"
|
||||
],
|
||||
"dependencies": {
|
||||
"color": "^4.2.1",
|
||||
"color": "^4.2.3",
|
||||
"detect-libc": "^2.0.1",
|
||||
"node-addon-api": "^4.3.0",
|
||||
"prebuild-install": "^7.0.1",
|
||||
"semver": "^7.3.5",
|
||||
"node-addon-api": "^5.0.0",
|
||||
"prebuild-install": "^7.1.0",
|
||||
"semver": "^7.3.7",
|
||||
"simple-get": "^4.0.1",
|
||||
"tar-fs": "^2.1.1",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
@@ -143,7 +145,7 @@
|
||||
"exif-reader": "^1.0.3",
|
||||
"icc": "^2.0.0",
|
||||
"license-checker": "^25.0.1",
|
||||
"mocha": "^9.2.1",
|
||||
"mocha": "^10.0.0",
|
||||
"mock-fs": "^5.1.2",
|
||||
"nyc": "^15.1.0",
|
||||
"prebuild": "^11.0.3",
|
||||
|
||||
@@ -85,7 +85,9 @@ namespace sharp {
|
||||
descriptor->buffer = buffer.Data();
|
||||
descriptor->isBuffer = TRUE;
|
||||
}
|
||||
descriptor->failOnError = AttrAsBool(input, "failOnError");
|
||||
descriptor->failOn = static_cast<VipsFailOn>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_FAIL_ON,
|
||||
AttrAsStr(input, "failOn").data()));
|
||||
// Density for vector-based input
|
||||
if (HasAttr(input, "density")) {
|
||||
descriptor->density = AttrAsDouble(input, "density");
|
||||
@@ -329,7 +331,7 @@ namespace sharp {
|
||||
try {
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", descriptor->access)
|
||||
->set("fail", descriptor->failOnError);
|
||||
->set("fail_on", descriptor->failOn);
|
||||
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
|
||||
option->set("unlimited", TRUE);
|
||||
}
|
||||
@@ -375,12 +377,6 @@ namespace sharp {
|
||||
VImage::option()->set("mean", descriptor->createNoiseMean)->set("sigma", descriptor->createNoiseSigma)));
|
||||
}
|
||||
image = image.bandjoin(bands);
|
||||
image = image.cast(VipsBandFormat::VIPS_FORMAT_UCHAR);
|
||||
if (channels < 3) {
|
||||
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
||||
} else {
|
||||
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
|
||||
}
|
||||
} else {
|
||||
std::vector<double> background = {
|
||||
descriptor->createBackground[0],
|
||||
@@ -392,7 +388,8 @@ namespace sharp {
|
||||
}
|
||||
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
||||
}
|
||||
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
|
||||
image.get_image()->Type = image.guess_interpretation();
|
||||
image = image.cast(VIPS_FORMAT_UCHAR);
|
||||
imageType = ImageType::RAW;
|
||||
} else {
|
||||
// From filesystem
|
||||
@@ -402,13 +399,13 @@ namespace sharp {
|
||||
throw vips::VError("Input file is missing, did you mean "
|
||||
"sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");
|
||||
}
|
||||
throw vips::VError("Input file is missing");
|
||||
throw vips::VError("Input file is missing: " + descriptor->file);
|
||||
}
|
||||
if (imageType != ImageType::UNKNOWN) {
|
||||
try {
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", descriptor->access)
|
||||
->set("fail", descriptor->failOnError);
|
||||
->set("fail_on", descriptor->failOn);
|
||||
if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
|
||||
option->set("unlimited", TRUE);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace sharp {
|
||||
std::string name;
|
||||
std::string file;
|
||||
char *buffer;
|
||||
bool failOnError;
|
||||
VipsFailOn failOn;
|
||||
int limitInputPixels;
|
||||
bool unlimited;
|
||||
VipsAccess access;
|
||||
@@ -74,7 +74,7 @@ namespace sharp {
|
||||
|
||||
InputDescriptor():
|
||||
buffer(nullptr),
|
||||
failOnError(TRUE),
|
||||
failOn(VIPS_FAIL_ON_WARNING),
|
||||
limitInputPixels(0x3FFF * 0x3FFF),
|
||||
unlimited(FALSE),
|
||||
access(VIPS_ACCESS_RANDOM),
|
||||
|
||||
@@ -209,7 +209,8 @@ namespace sharp {
|
||||
/*
|
||||
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
|
||||
*/
|
||||
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged) {
|
||||
VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
|
||||
double const x1, double const y2, double const y3) {
|
||||
if (sigma == -1.0) {
|
||||
// Fast, mild sharpen
|
||||
VImage sharpen = VImage::new_matrixv(3, 3,
|
||||
@@ -224,8 +225,14 @@ namespace sharp {
|
||||
if (colourspaceBeforeSharpen == VIPS_INTERPRETATION_RGB) {
|
||||
colourspaceBeforeSharpen = VIPS_INTERPRETATION_sRGB;
|
||||
}
|
||||
return image.sharpen(
|
||||
VImage::option()->set("sigma", sigma)->set("m1", flat)->set("m2", jagged))
|
||||
return image
|
||||
.sharpen(VImage::option()
|
||||
->set("sigma", sigma)
|
||||
->set("m1", m1)
|
||||
->set("m2", m2)
|
||||
->set("x1", x1)
|
||||
->set("y2", y2)
|
||||
->set("y3", y3))
|
||||
.colourspace(colourspaceBeforeSharpen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,8 @@ namespace sharp {
|
||||
/*
|
||||
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
|
||||
*/
|
||||
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged);
|
||||
VImage Sharpen(VImage image, double const sigma, double const m1, double const m2,
|
||||
double const x1, double const y2, double const y3);
|
||||
|
||||
/*
|
||||
Threshold an image
|
||||
|
||||
@@ -95,16 +95,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (baton->rotateBeforePreExtract) {
|
||||
if (rotation != VIPS_ANGLE_D0) {
|
||||
image = image.rot(rotation);
|
||||
if (flip) {
|
||||
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
||||
flip = FALSE;
|
||||
}
|
||||
if (flop) {
|
||||
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
||||
flop = FALSE;
|
||||
}
|
||||
}
|
||||
if (flip) {
|
||||
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
||||
}
|
||||
if (flop) {
|
||||
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
||||
}
|
||||
if (rotation != VIPS_ANGLE_D0 || flip || flop) {
|
||||
image = sharp::RemoveExifOrientation(image);
|
||||
}
|
||||
flop = FALSE;
|
||||
flip = FALSE;
|
||||
if (baton->rotationAngle != 0.0) {
|
||||
MultiPageUnsupported(nPages, "Rotate");
|
||||
std::vector<double> background;
|
||||
@@ -200,7 +202,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", baton->input->access)
|
||||
->set("shrink", jpegShrinkOnLoad)
|
||||
->set("fail", baton->input->failOnError);
|
||||
->set("fail_on", baton->input->failOn);
|
||||
if (baton->input->buffer != nullptr) {
|
||||
// Reload JPEG buffer
|
||||
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
||||
@@ -214,7 +216,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
vips::VOption *option = VImage::option()
|
||||
->set("access", baton->input->access)
|
||||
->set("scale", scale)
|
||||
->set("fail", baton->input->failOnError);
|
||||
->set("fail_on", baton->input->failOn);
|
||||
if (inputImageType == sharp::ImageType::WEBP) {
|
||||
option->set("n", baton->input->pages);
|
||||
option->set("page", baton->input->page);
|
||||
@@ -241,8 +243,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Reload SVG file
|
||||
image = VImage::svgload(const_cast<char*>(baton->input->file.data()), option);
|
||||
}
|
||||
|
||||
sharp::SetDensity(image, baton->input->density);
|
||||
if (image.width() > 32767 || image.height() > 32767) {
|
||||
throw vips::VError("Input SVG image will exceed 32767x32767 pixel limit when scaled");
|
||||
}
|
||||
} else if (inputImageType == sharp::ImageType::PDF) {
|
||||
option->set("n", baton->input->pages);
|
||||
option->set("page", baton->input->page);
|
||||
@@ -260,6 +264,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
sharp::SetDensity(image, baton->input->density);
|
||||
}
|
||||
} else {
|
||||
if (inputImageType == sharp::ImageType::SVG && (image.width() > 32767 || image.height() > 32767)) {
|
||||
throw vips::VError("Input SVG image exceeds 32767x32767 pixel limit");
|
||||
}
|
||||
}
|
||||
|
||||
// Any pre-shrinking may already have been done
|
||||
@@ -354,7 +362,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
}
|
||||
|
||||
bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
|
||||
(shouldResize || shouldBlur || shouldConv || shouldSharpen || shouldComposite);
|
||||
(shouldResize || shouldBlur || shouldConv || shouldSharpen);
|
||||
|
||||
// Premultiply image alpha channel before all transformations to avoid
|
||||
// dark fringing around bright pixels
|
||||
@@ -577,7 +585,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
// Sharpen
|
||||
if (shouldSharpen) {
|
||||
image = sharp::Sharpen(image, baton->sharpenSigma, baton->sharpenFlat, baton->sharpenJagged);
|
||||
image = sharp::Sharpen(image, baton->sharpenSigma, baton->sharpenM1, baton->sharpenM2,
|
||||
baton->sharpenX1, baton->sharpenY2, baton->sharpenY3);
|
||||
}
|
||||
|
||||
// Composite
|
||||
@@ -1400,8 +1409,11 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->lightness = sharp::AttrAsDouble(options, "lightness");
|
||||
baton->medianSize = sharp::AttrAsUint32(options, "medianSize");
|
||||
baton->sharpenSigma = sharp::AttrAsDouble(options, "sharpenSigma");
|
||||
baton->sharpenFlat = sharp::AttrAsDouble(options, "sharpenFlat");
|
||||
baton->sharpenJagged = sharp::AttrAsDouble(options, "sharpenJagged");
|
||||
baton->sharpenM1 = sharp::AttrAsDouble(options, "sharpenM1");
|
||||
baton->sharpenM2 = sharp::AttrAsDouble(options, "sharpenM2");
|
||||
baton->sharpenX1 = sharp::AttrAsDouble(options, "sharpenX1");
|
||||
baton->sharpenY2 = sharp::AttrAsDouble(options, "sharpenY2");
|
||||
baton->sharpenY3 = sharp::AttrAsDouble(options, "sharpenY3");
|
||||
baton->threshold = sharp::AttrAsInt32(options, "threshold");
|
||||
baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
|
||||
baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
|
||||
|
||||
@@ -90,8 +90,11 @@ struct PipelineBaton {
|
||||
double lightness;
|
||||
int medianSize;
|
||||
double sharpenSigma;
|
||||
double sharpenFlat;
|
||||
double sharpenJagged;
|
||||
double sharpenM1;
|
||||
double sharpenM2;
|
||||
double sharpenX1;
|
||||
double sharpenY2;
|
||||
double sharpenY3;
|
||||
int threshold;
|
||||
bool thresholdGrayscale;
|
||||
double trimThreshold;
|
||||
@@ -234,8 +237,11 @@ struct PipelineBaton {
|
||||
lightness(0),
|
||||
medianSize(0),
|
||||
sharpenSigma(0.0),
|
||||
sharpenFlat(1.0),
|
||||
sharpenJagged(2.0),
|
||||
sharpenM1(1.0),
|
||||
sharpenM2(2.0),
|
||||
sharpenX1(2.0),
|
||||
sharpenY2(10.0),
|
||||
sharpenY3(20.0),
|
||||
threshold(0),
|
||||
thresholdGrayscale(true),
|
||||
trimThreshold(0.0),
|
||||
|
||||
BIN
test/fixtures/expected/composite-multiple.png
vendored
|
Before Width: | Height: | Size: 320 B After Width: | Height: | Size: 320 B |
BIN
test/fixtures/expected/composite.blend.dest-over.png
vendored
|
Before Width: | Height: | Size: 291 B After Width: | Height: | Size: 292 B |
BIN
test/fixtures/expected/composite.blend.over.png
vendored
|
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 292 B |
BIN
test/fixtures/expected/composite.blend.saturate.png
vendored
|
Before Width: | Height: | Size: 288 B After Width: | Height: | Size: 286 B |
BIN
test/fixtures/expected/composite.blend.xor.png
vendored
|
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 285 B |
@@ -158,6 +158,6 @@ describe('Extend', function () {
|
||||
})
|
||||
.raw()
|
||||
.toBuffer();
|
||||
assert.deepStrictEqual(Array.from(data), [191, 25, 65, 204, 238, 31, 82, 204]);
|
||||
assert.deepStrictEqual(Array.from(data), [191, 25, 66, 204, 191, 25, 66, 204]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -168,14 +168,51 @@ describe('Partial image extraction', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Rotate with EXIF mirroring then extract', function (done) {
|
||||
sharp(fixtures.inputJpgWithLandscapeExif7)
|
||||
.rotate()
|
||||
.extract({ left: 0, top: 208, width: 60, height: 40 })
|
||||
.toBuffer(function (err, data) {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('rotate-mirror-extract.jpg'), data, done);
|
||||
describe('Apply exif orientation and mirroring then extract', () => {
|
||||
[
|
||||
{
|
||||
name: 'EXIF-1',
|
||||
image: fixtures.inputJpgWithLandscapeExif1
|
||||
},
|
||||
{
|
||||
name: 'EXIF-2',
|
||||
image: fixtures.inputJpgWithLandscapeExif2
|
||||
},
|
||||
{
|
||||
name: 'EXIF-3',
|
||||
image: fixtures.inputJpgWithLandscapeExif3
|
||||
},
|
||||
{
|
||||
name: 'EXIF-4',
|
||||
image: fixtures.inputJpgWithLandscapeExif4
|
||||
},
|
||||
{
|
||||
name: 'EXIF-5',
|
||||
image: fixtures.inputJpgWithLandscapeExif5
|
||||
},
|
||||
{
|
||||
name: 'EXIF-6',
|
||||
image: fixtures.inputJpgWithLandscapeExif6
|
||||
},
|
||||
{
|
||||
name: 'EXIF-7',
|
||||
image: fixtures.inputJpgWithLandscapeExif7
|
||||
},
|
||||
{
|
||||
name: 'EXIF-8',
|
||||
image: fixtures.inputJpgWithLandscapeExif8
|
||||
}
|
||||
].forEach(({ name, image }) => {
|
||||
it(name, function (done) {
|
||||
sharp(image)
|
||||
.rotate()
|
||||
.extract({ left: 0, top: 208, width: 60, height: 40 })
|
||||
.toBuffer(function (err, data) {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('rotate-mirror-extract.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid parameters', function () {
|
||||
|
||||
@@ -3,56 +3,70 @@
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const sharp = require('../../');
|
||||
const sharp = require('../../lib');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
describe('failOnError', function () {
|
||||
describe('failOn', () => {
|
||||
it('handles truncated JPEG', function (done) {
|
||||
sharp(fixtures.inputJpgTruncated, { failOnError: false })
|
||||
.resize(320, 240)
|
||||
sharp(fixtures.inputJpgTruncated, { failOn: 'none' })
|
||||
.resize(32, 24)
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
assert.strictEqual(240, info.height);
|
||||
assert.strictEqual(32, info.width);
|
||||
assert.strictEqual(24, info.height);
|
||||
fixtures.assertSimilar(fixtures.expected('truncated.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('handles truncated PNG, emits warnings', function (done) {
|
||||
let isWarningEmitted = false;
|
||||
sharp(fixtures.inputPngTruncated, { failOnError: false })
|
||||
sharp(fixtures.inputPngTruncated, { failOn: 'none' })
|
||||
.on('warning', function (warning) {
|
||||
assert.ok(warning.includes('not enough data') || warning.includes('end of stream'));
|
||||
isWarningEmitted = true;
|
||||
})
|
||||
.resize(320, 240)
|
||||
.resize(32, 24)
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, isWarningEmitted);
|
||||
assert.strictEqual('png', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
assert.strictEqual(240, info.height);
|
||||
assert.strictEqual(32, info.width);
|
||||
assert.strictEqual(24, info.height);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects invalid values', function () {
|
||||
assert.doesNotThrow(function () {
|
||||
sharp(fixtures.inputJpg, { failOnError: true });
|
||||
});
|
||||
it('throws for invalid options', () => {
|
||||
assert.throws(
|
||||
() => sharp({ failOn: 'zoinks' }),
|
||||
/Expected one of: none, truncated, error, warning for failOn but received zoinks of type string/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ failOn: 1 }),
|
||||
/Expected one of: none, truncated, error, warning for failOn but received 1 of type number/
|
||||
);
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
sharp(fixtures.inputJpg, { failOnError: 'zoinks' });
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
sharp(fixtures.inputJpg, { failOnError: 1 });
|
||||
});
|
||||
it('deprecated failOnError', () => {
|
||||
assert.doesNotThrow(
|
||||
() => sharp({ failOnError: true })
|
||||
);
|
||||
assert.doesNotThrow(
|
||||
() => sharp({ failOnError: false })
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ failOnError: 'zoinks' }),
|
||||
/Expected boolean for failOnError but received zoinks of type string/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ failOnError: 1 }),
|
||||
/Expected boolean for failOnError but received 1 of type number/
|
||||
);
|
||||
});
|
||||
|
||||
it('returns errors to callback for truncated JPEG', function (done) {
|
||||
sharp(fixtures.inputJpgTruncated).toBuffer(function (err, data, info) {
|
||||
sharp(fixtures.inputJpgTruncated, { failOn: 'truncated' }).toBuffer(function (err, data, info) {
|
||||
assert.ok(err.message.includes('VipsJpeg: Premature end of'), err);
|
||||
assert.strictEqual(data, undefined);
|
||||
assert.strictEqual(info, undefined);
|
||||
@@ -61,7 +75,7 @@ describe('failOnError', function () {
|
||||
});
|
||||
|
||||
it('returns errors to callback for truncated PNG', function (done) {
|
||||
sharp(fixtures.inputPngTruncated).toBuffer(function (err, data, info) {
|
||||
sharp(fixtures.inputPngTruncated, { failOn: 'truncated' }).toBuffer(function (err, data, info) {
|
||||
assert.ok(err.message.includes('read error'), err);
|
||||
assert.strictEqual(data, undefined);
|
||||
assert.strictEqual(info, undefined);
|
||||
@@ -70,7 +84,7 @@ describe('failOnError', function () {
|
||||
});
|
||||
|
||||
it('rejects promises for truncated JPEG', function (done) {
|
||||
sharp(fixtures.inputJpgTruncated)
|
||||
sharp(fixtures.inputJpgTruncated, { failOn: 'error' })
|
||||
.toBuffer()
|
||||
.then(() => {
|
||||
throw new Error('Expected rejection');
|
||||
@@ -80,8 +94,8 @@ describe('failOnError', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('handles stream-based input', function () {
|
||||
const writable = sharp({ failOnError: false });
|
||||
it('handles stream-based input', async () => {
|
||||
const writable = sharp({ failOn: 'none' }).resize(32, 24);
|
||||
fs.createReadStream(fixtures.inputJpgTruncated).pipe(writable);
|
||||
return writable.toBuffer();
|
||||
});
|
||||
@@ -182,6 +182,24 @@ describe('Input/output', function () {
|
||||
assert.strictEqual(info.height, 1);
|
||||
});
|
||||
|
||||
it('Read from Uint8ClampedArray with byteOffset and output to Buffer', async () => {
|
||||
// since a Uint8ClampedArray is the same as Uint8Array but clamps the values
|
||||
// between 0-255 it seemed good to add this also
|
||||
const uint8array = Uint8ClampedArray.from([0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255]);
|
||||
const uint8ArrayWithByteOffset = new Uint8ClampedArray(uint8array.buffer, 3, 6);
|
||||
const { data, info } = await sharp(uint8ArrayWithByteOffset, {
|
||||
raw: {
|
||||
width: 2,
|
||||
height: 1,
|
||||
channels: 3
|
||||
}
|
||||
}).toBuffer({ resolveWithObject: true });
|
||||
|
||||
assert.deepStrictEqual(Uint8ClampedArray.from([255, 255, 255, 0, 0, 0]), new Uint8ClampedArray(data));
|
||||
assert.strictEqual(info.width, 2);
|
||||
assert.strictEqual(info.height, 1);
|
||||
});
|
||||
|
||||
it('Stream should emit info event', function (done) {
|
||||
const readable = fs.createReadStream(fixtures.inputJpg);
|
||||
const writable = fs.createWriteStream(outputJpg);
|
||||
@@ -439,7 +457,7 @@ describe('Input/output', function () {
|
||||
done();
|
||||
}).catch(function (err) {
|
||||
assert(err instanceof Error);
|
||||
assert.strictEqual('Input file is missing', err.message);
|
||||
assert.strictEqual('Input file is missing: does-not-exist', err.message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,15 +48,8 @@ describe('JP2 output', () => {
|
||||
.resize(320, 240)
|
||||
.toBuffer(function (err, buffer80) {
|
||||
if (err) throw err;
|
||||
sharp(fixtures.inputJp2)
|
||||
.resize(320, 240)
|
||||
.jp2({ quality: 90 })
|
||||
.toBuffer(function (err, buffer90) {
|
||||
if (err) throw err;
|
||||
assert(buffer70.length < buffer80.length);
|
||||
assert(buffer80.length < buffer90.length);
|
||||
done();
|
||||
});
|
||||
assert(buffer70.length < buffer80.length);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -186,6 +186,22 @@ describe('Negate', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('negate create', async () => {
|
||||
const [r, g, b] = await sharp({
|
||||
create: {
|
||||
width: 1,
|
||||
height: 1,
|
||||
channels: 3,
|
||||
background: { r: 10, g: 20, b: 30 }
|
||||
}
|
||||
})
|
||||
.negate()
|
||||
.raw()
|
||||
.toBuffer();
|
||||
|
||||
assert.deepStrictEqual({ r, g, b }, { r: 245, g: 235, b: 225 });
|
||||
});
|
||||
|
||||
it('invalid alpha value', function () {
|
||||
assert.throws(function () {
|
||||
sharp(fixtures.inputWebPWithTransparency).negate({ alpha: 'non-bool' });
|
||||
|
||||
@@ -19,7 +19,7 @@ describe('Gaussian noise', function () {
|
||||
sigma: 30
|
||||
}
|
||||
}
|
||||
});
|
||||
}).toColourspace('b-w');
|
||||
noise.toFile(output, function (err, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('png', info.format);
|
||||
@@ -131,6 +131,7 @@ describe('Gaussian noise', function () {
|
||||
}
|
||||
});
|
||||
noise
|
||||
.toColourspace('b-w')
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(1, info.channels);
|
||||
|
||||
@@ -61,4 +61,28 @@ describe('Platform-detection', function () {
|
||||
delete process.env.npm_config_arch;
|
||||
delete process.versions.electron;
|
||||
});
|
||||
|
||||
it('Can override libc if platform is linux', function () {
|
||||
process.env.npm_config_platform = 'linux';
|
||||
process.env.npm_config_libc = 'test';
|
||||
assert.strictEqual('linuxtest', platform().split('-')[0]);
|
||||
delete process.env.npm_config_platform;
|
||||
delete process.env.npm_config_libc;
|
||||
});
|
||||
|
||||
it('Handles libc value "glibc" as default linux', function () {
|
||||
process.env.npm_config_platform = 'linux';
|
||||
process.env.npm_config_libc = 'glibc';
|
||||
assert.strictEqual('linux', platform().split('-')[0]);
|
||||
delete process.env.npm_config_platform;
|
||||
delete process.env.npm_config_libc;
|
||||
});
|
||||
|
||||
it('Discards libc value on non-linux platform', function () {
|
||||
process.env.npm_config_platform = 'win32';
|
||||
process.env.npm_config_libc = 'gnuwin32';
|
||||
assert.strictEqual('win32', platform().split('-')[0]);
|
||||
delete process.env.npm_config_platform;
|
||||
delete process.env.npm_config_libc;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -45,6 +45,22 @@ describe('Sharpen', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('sigma=3.5, m1=2, m2=4', (done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.sharpen({ sigma: 3.5, m1: 2, m2: 4 })
|
||||
.toBuffer()
|
||||
.then(data => fixtures.assertSimilar(fixtures.expected('sharpen-5-2-4.jpg'), data, done));
|
||||
});
|
||||
|
||||
it('sigma=3.5, m1=2, m2=4, x1=2, y2=5, y3=25', (done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.sharpen({ sigma: 3.5, m1: 2, m2: 4, x1: 2, y2: 5, y3: 25 })
|
||||
.toBuffer()
|
||||
.then(data => fixtures.assertSimilar(fixtures.expected('sharpen-5-2-4.jpg'), data, done));
|
||||
});
|
||||
|
||||
if (!process.env.SHARP_TEST_WITHOUT_CACHE) {
|
||||
it('specific radius/levels with alpha channel', function (done) {
|
||||
sharp(fixtures.inputPngWithTransparency)
|
||||
@@ -92,6 +108,36 @@ describe('Sharpen', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid options.sigma', () => assert.throws(
|
||||
() => sharp().sharpen({ sigma: -1 }),
|
||||
/Expected number between 0\.01 and 10000 for options\.sigma but received -1 of type number/
|
||||
));
|
||||
|
||||
it('invalid options.m1', () => assert.throws(
|
||||
() => sharp().sharpen({ sigma: 1, m1: -1 }),
|
||||
/Expected number between 0 and 10000 for options\.m1 but received -1 of type number/
|
||||
));
|
||||
|
||||
it('invalid options.m2', () => assert.throws(
|
||||
() => sharp().sharpen({ sigma: 1, m2: -1 }),
|
||||
/Expected number between 0 and 10000 for options\.m2 but received -1 of type number/
|
||||
));
|
||||
|
||||
it('invalid options.x1', () => assert.throws(
|
||||
() => sharp().sharpen({ sigma: 1, x1: -1 }),
|
||||
/Expected number between 0 and 10000 for options\.x1 but received -1 of type number/
|
||||
));
|
||||
|
||||
it('invalid options.y2', () => assert.throws(
|
||||
() => sharp().sharpen({ sigma: 1, y2: -1 }),
|
||||
/Expected number between 0 and 10000 for options\.y2 but received -1 of type number/
|
||||
));
|
||||
|
||||
it('invalid options.y3', () => assert.throws(
|
||||
() => sharp().sharpen({ sigma: 1, y3: -1 }),
|
||||
/Expected number between 0 and 10000 for options\.y3 but received -1 of type number/
|
||||
));
|
||||
|
||||
it('sharpened image is larger than non-sharpened', function (done) {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
|
||||
@@ -136,6 +136,20 @@ describe('SVG input', function () {
|
||||
assert.strictEqual(info.channels, 4);
|
||||
});
|
||||
|
||||
it('Fails to render SVG larger than 32767x32767', () =>
|
||||
assert.rejects(
|
||||
() => sharp(Buffer.from('<svg width="32768" height="1" />')).toBuffer(),
|
||||
/Input SVG image exceeds 32767x32767 pixel limit/
|
||||
)
|
||||
);
|
||||
|
||||
it('Fails to render scaled SVG larger than 32767x32767', () =>
|
||||
assert.rejects(
|
||||
() => sharp(Buffer.from('<svg width="32767" height="1" />')).resize(32768).toBuffer(),
|
||||
/Input SVG image will exceed 32767x32767 pixel limit when scaled/
|
||||
)
|
||||
);
|
||||
|
||||
it('Detects SVG passed as a string', () =>
|
||||
assert.rejects(
|
||||
() => sharp('<svg></svg>').toBuffer(),
|
||||
|
||||
@@ -68,10 +68,13 @@ describe('Utilities', function () {
|
||||
});
|
||||
|
||||
describe('Counters', function () {
|
||||
it('Have zero value at rest', function () {
|
||||
const counters = sharp.counters();
|
||||
assert.strictEqual(0, counters.queue);
|
||||
assert.strictEqual(0, counters.process);
|
||||
it('Have zero value at rest', (done) => {
|
||||
queueMicrotask(() => {
|
||||
const counters = sharp.counters();
|
||||
assert.strictEqual(0, counters.queue);
|
||||
assert.strictEqual(0, counters.process);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||