Compare commits

...

56 Commits

Author SHA1 Message Date
Lovell Fuller
5bed3a7d52 Release v0.20.7 2018-08-21 11:50:14 +01:00
Lovell Fuller
ece111280b Use copy+unlink if rename fails during install #1345 2018-08-20 15:14:31 +01:00
Lovell Fuller
a15a9b956b Release v0.20.6 2018-08-20 11:40:10 +01:00
Lovell Fuller
42860c2f83 Changelog, credit and doc refresh for #1342 2018-08-19 10:43:25 +01:00
Alun Davies
b5b95e5ae1 Expose depth option for tile-based output (#1342) 2018-08-18 15:09:53 +01:00
Lovell Fuller
d705cffdd6 Ensure extractChannel works with 16-bit images #1330 2018-08-12 20:22:39 +01:00
Rodrigo Alviani
23a4bc103e Docs: correct quality option in overlayWith example (#1325) 2018-08-08 08:42:18 +01:00
Lovell Fuller
c14434f9e7 Add removeAlpha op, removes alpha channel if any #1248 2018-08-07 20:32:11 +01:00
Lovell Fuller
25bd2cea3e Add experimental entropy field to stats response 2018-08-06 15:41:27 +01:00
Lovell Fuller
532de4ecab Cache libvips binaries to reduce re-install time #1301 2018-08-05 10:31:41 +01:00
Lovell Fuller
bfdd27eeef Doc refresh and dependency bumps 2018-08-05 09:42:09 +01:00
Lovell Fuller
bd9f238ab4 Improve install time error messages for FreeBSD #1310 2018-08-04 22:27:32 +01:00
Lovell Fuller
75556bb57c Ensure vendor platform mismatch throws error #1303 2018-08-04 21:34:11 +01:00
thegareth
2de062a34a Docs: update the "make a transparent image" example (#1316)
Alpha for colour is between 0-1, not 0-255.
2018-08-02 09:42:25 +01:00
Lovell Fuller
4589b15dea Changelog and credit for #1285 #1290 2018-07-10 16:12:16 +01:00
Sylvain Dumont
8b75ce6786 Allow full WebP alphaQuality range of 0-100 (#1290) 2018-07-10 15:58:17 +01:00
Espen Hovlandsdal
7bbc5176a1 Expose mozjpeg quant_table flag (#1285) 2018-07-10 15:56:05 +01:00
Lovell Fuller
5cb35485f1 Release v0.20.5 2018-06-27 08:44:31 +01:00
Lovell Fuller
80189ed689 Add changelog and credit for #1265 2018-06-27 07:54:41 +01:00
Muhammad Faheem Akhtar
3d7e8ef432 Docs: update new Buffer() to Buffer.from() (#1273) 2018-06-26 08:19:27 +01:00
Lovell Fuller
1999c7103c Docs: add CSS to improve left-hand nav nesting 2018-06-25 19:58:50 +01:00
Thomas Vantuycom
9c20ae383e Remove top of file table of contents in documentation (#1270) 2018-06-24 21:45:51 +01:00
Tom Lokhorst
76c41eaf05 Expose libjpeg optimize_coding flag (#1265) 2018-06-21 18:12:10 +01:00
Lovell Fuller
873aa6700f Release v0.20.4 2018-06-20 08:26:28 +01:00
Lovell Fuller
0d9590a9a0 Documentation refresh 2018-06-20 08:14:03 +01:00
Lovell Fuller
94607b585a Ensure extractChannel sets bw colourspace interp #1257 2018-06-19 22:47:52 +01:00
Lovell Fuller
da0b0348a2 Prevent rounding err with shrink-on-load and 90/270 rot #1241 2018-06-19 21:19:34 +01:00
Lovell Fuller
09263455b5 Release v0.20.3 2018-05-29 08:38:42 +01:00
Lovell Fuller
ddc23493d4 Add warning about possible concurrent tile race #1151 2018-05-25 19:53:23 +01:00
Lovell Fuller
54a71fc142 Fix tint op by ensuring LAB and allowing negative values #1235
Add test cases for more tint colours and input interpretations
2018-05-23 20:51:47 +01:00
Lovell Fuller
b1a9bf10a2 Pre-built binaries provided for even-numbered major versions 2018-05-21 11:44:34 +01:00
Lovell Fuller
97cfbe1b63 Bump dependency versions 2018-05-19 15:19:23 +01:00
Lovell Fuller
0ee8c63551 Replace use of libvips API deprecated since v8.6.1
See jcupitt/libvips@b085908
2018-05-19 15:03:50 +01:00
Lovell Fuller
0ac5a9ad82 Promote nw.js details to main installation docs - see 6e51f2d 2018-05-19 14:53:22 +01:00
Michael
6e51f2d608 rebuild doc for NW.js (#1229)
refers to https://github.com/lovell/sharp/issues/1223
2018-05-15 07:28:09 +01:00
Lovell Fuller
f23a8dc9dc Release v0.20.2 2018-04-28 18:29:32 +01:00
Lovell Fuller
d09fe6178c Add support for Node 10, drop support for Node 9 2018-04-28 18:12:10 +01:00
Lovell Fuller
ae2cfcc4f3 Version bumps, hold simple-get at v2 for Node 4 support 2018-04-28 18:11:20 +01:00
Lovell Fuller
853cc65e32 Changelog entry for #1208 2018-04-28 14:06:45 +01:00
Nathan Graves
5d140d949f Add support for Group4 (CCITTFAX4) compression to TIFF output (#1208) 2018-04-26 19:49:08 +01:00
Lovell Fuller
6d2da2b3ba Switch unzip test dependency for Node 10 support 2018-04-25 20:37:10 +01:00
Lovell Fuller
165e337e44 Changelog entry and doc refresh for #1204 2018-04-25 10:19:33 +01:00
Nathan Graves
b154cd0418 Add support for page selection with multi-page TIFF input (#1204) 2018-04-24 22:57:27 +01:00
Lovell Fuller
8ef1532691 Dependency version bumps 2018-04-17 20:46:22 +01:00
Lovell Fuller
771e44f2a7 Add error highlighting lack of Windows x86 node.exe support 2018-04-17 20:37:19 +01:00
Lovell Fuller
8933f1128d Changelog entry and credit for #1165 2018-04-11 20:30:16 +01:00
Rik Heywood
dbac4b9a63 Add tint operation to set image chroma 2018-04-11 20:05:48 +01:00
Lovell Fuller
bdac5b5807 Add code usage examples for output-related functions 2018-04-06 19:52:00 +01:00
Lovell Fuller
b0961b5213 Bump dependencies 2018-04-06 19:49:17 +01:00
Thomas Parisot
0d7c3fc4d8 Ignore global libvips during install via SHARP_IGNORE_GLOBAL_LIBVIPS (#1165) 2018-03-22 18:48:30 +00:00
Lovell Fuller
8dac256096 Release v0.20.1 2018-03-17 14:04:35 +00:00
Lovell Fuller
8320da39c3 Changelog entry and doc refresh for #1161 2018-03-17 11:12:43 +00:00
Andrea Bianco
875937e3d8 Expose libvips' median filter operation (#1161) 2018-03-17 10:52:44 +00:00
Lovell Fuller
f880adbaac Bump dependencies ahead of v0.20.1 2018-03-16 20:46:08 +00:00
Lovell Fuller
48c5f86adb Improve install when global libvips below min version #1148 2018-03-13 20:37:42 +00:00
Lovell Fuller
f60f7dab12 Prevent error when cumulative rounding below target #1154 2018-03-13 19:42:10 +00:00
65 changed files with 1765 additions and 677 deletions

View File

@@ -8,6 +8,7 @@ test
.travis.yml .travis.yml
appveyor.yml appveyor.yml
mkdocs.yml mkdocs.yml
docs/css/
vendor vendor
.prebuildrc .prebuildrc
.nyc_output .nyc_output

View File

@@ -16,7 +16,7 @@ matrix:
- os: linux - os: linux
dist: trusty dist: trusty
sudo: false sudo: false
node_js: "9" node_js: "10"
- os: osx - os: osx
osx_image: xcode8.3 osx_image: xcode8.3
node_js: "4" node_js: "4"
@@ -28,7 +28,7 @@ matrix:
node_js: "8" node_js: "8"
- os: osx - os: osx
osx_image: xcode8.3 osx_image: xcode8.3
node_js: "9" node_js: "10"
after_success: after_success:
- npm install coveralls - npm install coveralls
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

View File

@@ -41,7 +41,6 @@ Any change that modifies the existing public API should be added to the relevant
| Release | WIP branch | | Release | WIP branch |
| ------: | :--------- | | ------: | :--------- |
| v0.20.0 | prebuild |
| v0.21.0 | teeth | | v0.21.0 | teeth |
| v0.22.0 | uptake | | v0.22.0 | uptake |
@@ -90,15 +89,6 @@ Requires [Valgrind](http://valgrind.org/).
npm run test-leak npm run test-leak
``` ```
### Packaging tests
Tests the installation on a number of Linux-based operating systems.
Requires docker.
```sh
npm run test-packaging
```
## Finally ## Finally
Please feel free to ask any questions via a Please feel free to ask any questions via a

View File

@@ -22,7 +22,7 @@ As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available. rotation, extraction, compositing and gamma correction are available.
Most modern 64-bit OS X, Windows and Linux (glibc) systems running Most modern 64-bit OS X, Windows and Linux (glibc) systems running
Node versions 4, 6, 8 and 9 Node versions 4, 6, 8 and 10
do not require any additional install or runtime dependencies. do not require any additional install or runtime dependencies.
## Examples ## Examples
@@ -48,7 +48,7 @@ sharp('input.jpg')
``` ```
```javascript ```javascript
const roundedCorners = new Buffer( const roundedCorners = Buffer.from(
'<svg><rect x="0" y="0" width="200" height="200" rx="50" ry="50"/></svg>' '<svg><rect x="0" y="0" width="200" height="200" rx="50" ry="50"/></svg>'
); );

View File

@@ -7,10 +7,10 @@ environment:
- nodejs_version: "4" - nodejs_version: "4"
- nodejs_version: "6" - nodejs_version: "6"
- nodejs_version: "8" - nodejs_version: "8"
- nodejs_version: "9" - nodejs_version: "10"
install: install:
- ps: Install-Product node $env:nodejs_version x64 - ps: Install-Product node $env:nodejs_version x64
- npm install -g npm@5.3.x - npm install -g npm@5
- npm install - npm install
test_script: test_script:
- npm test - npm test

View File

@@ -1,20 +1,30 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents ## removeAlpha
- [extractChannel][1] Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
- [joinChannel][2]
- [bandbool][3] ### Examples
```javascript
sharp('rgba.png')
.removeAlpha()
.toFile('rgb.png', function(err, info) {
// rgb.png is a 3 channel image without an alpha channel
});
```
Returns **Sharp**
## extractChannel ## extractChannel
Extract a single channel from a multi-channel image. Extract a single channel from a multi-channel image.
**Parameters** ### Parameters
- `channel` **([Number][4] \| [String][5])** zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively. - `channel` **([Number][1] \| [String][2])** zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively.
**Examples** ### Examples
```javascript ```javascript
sharp(input) sharp(input)
@@ -25,7 +35,7 @@ sharp(input)
}); });
``` ```
- Throws **[Error][6]** Invalid channel - Throws **[Error][3]** Invalid channel
Returns **Sharp** Returns **Sharp**
@@ -42,13 +52,13 @@ Channel ordering follows vips convention:
Buffers may be any of the image formats supported by sharp: JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data. Buffers may be any of the image formats supported by sharp: JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data.
For raw pixel input, the `options` object should contain a `raw` attribute, which follows the format of the attribute of the same name in the `sharp()` constructor. For raw pixel input, the `options` object should contain a `raw` attribute, which follows the format of the attribute of the same name in the `sharp()` constructor.
**Parameters** ### Parameters
- `images` **([Array][7]&lt;([String][5] \| [Buffer][8])> | [String][5] \| [Buffer][8])** one or more images (file paths, Buffers). - `images` **([Array][4]&lt;([String][2] \| [Buffer][5])> | [String][2] \| [Buffer][5])** one or more images (file paths, Buffers).
- `options` **[Object][9]** image options, see `sharp()` constructor. - `options` **[Object][6]** image options, see `sharp()` constructor.
- Throws **[Error][6]** Invalid parameters - Throws **[Error][3]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -56,11 +66,11 @@ Returns **Sharp**
Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image. Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
**Parameters** ### Parameters
- `boolOp` **[String][5]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively. - `boolOp` **[String][2]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
**Examples** ### Examples
```javascript ```javascript
sharp('3-channel-rgb-input.png') sharp('3-channel-rgb-input.png')
@@ -72,24 +82,18 @@ sharp('3-channel-rgb-input.png')
}); });
``` ```
- Throws **[Error][6]** Invalid parameters - Throws **[Error][3]** Invalid parameters
Returns **Sharp** Returns **Sharp**
[1]: #extractchannel [1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[2]: #joinchannel [2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[3]: #bandbool [3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String [5]: https://nodejs.org/api/buffer.html
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error [6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[8]: https://nodejs.org/api/buffer.html
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object

View File

@@ -1,13 +1,5 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [background][1]
- [greyscale][2]
- [grayscale][3]
- [toColourspace][4]
- [toColorspace][5]
## background ## background
Set the background for the `embed`, `flatten` and `extend` operations. Set the background for the `embed`, `flatten` and `extend` operations.
@@ -17,12 +9,26 @@ Delegates to the _color_ module, which can throw an Error
but is liberal in what it accepts, clipping values to sensible min/max. but is liberal in what it accepts, clipping values to sensible min/max.
The alpha value is a float between `0` (transparent) and `1` (opaque). The alpha value is a float between `0` (transparent) and `1` (opaque).
**Parameters** ### Parameters
- `rgba` **([String][6] \| [Object][7])** parsed by the [color][8] module to extract values for red, green, blue and alpha. - `rgba` **([String][1] \| [Object][2])** parsed by the [color][3] module to extract values for red, green, blue and alpha.
- Throws **[Error][9]** Invalid parameter - Throws **[Error][4]** Invalid parameter
Returns **Sharp**
## tint
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.
### Parameters
- `rgb` **([String][1] \| [Object][2])** parsed by the [color][3] module to extract chroma values.
- Throws **[Error][4]** Invalid parameter
Returns **Sharp** Returns **Sharp**
@@ -35,9 +41,9 @@ This may be overridden by other sharp operations such as `toColourspace('b-w')`,
which will produce an output image containing one color channel. which will produce an output image containing one color channel.
An alpha channel may be present, and will be unchanged by the operation. An alpha channel may be present, and will be unchanged by the operation.
**Parameters** ### Parameters
- `greyscale` **[Boolean][10]** (optional, default `true`) - `greyscale` **[Boolean][5]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -45,9 +51,9 @@ Returns **Sharp**
Alternative spelling of `greyscale`. Alternative spelling of `greyscale`.
**Parameters** ### Parameters
- `grayscale` **[Boolean][10]** (optional, default `true`) - `grayscale` **[Boolean][5]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -56,12 +62,12 @@ Returns **Sharp**
Set the output colourspace. Set the output colourspace.
By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels. By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
**Parameters** ### Parameters
- `colourspace` **[String][6]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][11] - `colourspace` **[String][1]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][6]
- Throws **[Error][9]** Invalid parameters - Throws **[Error][4]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -69,33 +75,23 @@ Returns **Sharp**
Alternative spelling of `toColourspace`. Alternative spelling of `toColourspace`.
**Parameters** ### Parameters
- `colorspace` **[String][6]?** output colorspace. - `colorspace` **[String][1]?** output colorspace.
- Throws **[Error][9]** Invalid parameters - Throws **[Error][4]** Invalid parameters
Returns **Sharp** Returns **Sharp**
[1]: #background [1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[2]: #greyscale [2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[3]: #grayscale [3]: https://www.npmjs.org/package/color
[4]: #tocolourspace [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[5]: #tocolorspace [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String [6]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[8]: https://www.npmjs.org/package/color
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[11]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568

View File

@@ -1,9 +1,5 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [overlayWith][1]
## overlayWith ## overlayWith
Overlay (composite) an image over the processed (resized, extracted etc.) image. Overlay (composite) an image over the processed (resized, extracted etc.) image.
@@ -13,27 +9,27 @@ If both `top` and `left` options are provided, they take precedence over `gravit
If the overlay image contains an alpha channel then composition with premultiplication will occur. If the overlay image contains an alpha channel then composition with premultiplication will occur.
**Parameters** ### Parameters
- `overlay` **([Buffer][2] \| [String][3])** Buffer containing image data or String containing the path to an image file. - `overlay` **([Buffer][1] \| [String][2])** Buffer containing image data or String containing the path to an image file.
- `options` **[Object][4]?** - `options` **[Object][3]?**
- `options.gravity` **[String][3]** gravity at which to place the overlay. (optional, default `'centre'`) - `options.gravity` **[String][2]** gravity at which to place the overlay. (optional, default `'centre'`)
- `options.top` **[Number][5]?** the pixel offset from the top edge. - `options.top` **[Number][4]?** the pixel offset from the top edge.
- `options.left` **[Number][5]?** the pixel offset from the left edge. - `options.left` **[Number][4]?** the pixel offset from the left edge.
- `options.tile` **[Boolean][6]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`) - `options.tile` **[Boolean][5]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
- `options.cutout` **[Boolean][6]** set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another. (optional, default `false`) - `options.cutout` **[Boolean][5]** set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another. (optional, default `false`)
- `options.density` **[Number][5]** integral number representing the DPI for vector overlay image. (optional, default `72`) - `options.density` **[Number][4]** integral number representing the DPI for vector overlay image. (optional, default `72`)
- `options.raw` **[Object][4]?** describes overlay when using raw pixel data. - `options.raw` **[Object][3]?** describes overlay when using raw pixel data.
- `options.raw.width` **[Number][5]?** - `options.raw.width` **[Number][4]?**
- `options.raw.height` **[Number][5]?** - `options.raw.height` **[Number][4]?**
- `options.raw.channels` **[Number][5]?** - `options.raw.channels` **[Number][4]?**
- `options.create` **[Object][4]?** describes a blank overlay to be created. - `options.create` **[Object][3]?** describes a blank overlay to be created.
- `options.create.width` **[Number][5]?** - `options.create.width` **[Number][4]?**
- `options.create.height` **[Number][5]?** - `options.create.height` **[Number][4]?**
- `options.create.channels` **[Number][5]?** 3-4 - `options.create.channels` **[Number][4]?** 3-4
- `options.create.background` **([String][3] \| [Object][4])?** parsed by the [color][7] module to extract values for red, green, blue and alpha. - `options.create.background` **([String][2] \| [Object][3])?** parsed by the [color][6] module to extract values for red, green, blue and alpha.
**Examples** ### Examples
```javascript ```javascript
sharp('input.png') sharp('input.png')
@@ -44,8 +40,7 @@ sharp('input.png')
.overlayWith('overlay.png', { gravity: sharp.gravity.southeast } ) .overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
.sharpen() .sharpen()
.withMetadata() .withMetadata()
.quality(90) .webp( { quality: 90 } )
.webp()
.toBuffer() .toBuffer()
.then(function(outputBuffer) { .then(function(outputBuffer) {
// outputBuffer contains upside down, 300px wide, alpha channel flattened // outputBuffer contains upside down, 300px wide, alpha channel flattened
@@ -54,22 +49,20 @@ sharp('input.png')
}); });
``` ```
- Throws **[Error][8]** Invalid parameters - Throws **[Error][7]** Invalid parameters
Returns **Sharp** Returns **Sharp**
[1]: #overlaywith [1]: https://nodejs.org/api/buffer.html
[2]: https://nodejs.org/api/buffer.html [2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String [3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean [6]: https://www.npmjs.org/package/color
[7]: https://www.npmjs.org/package/color [7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error

View File

@@ -1,36 +1,30 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [Sharp][1]
- [format][2]
- [versions][3]
- [queue][4]
## Sharp ## Sharp
**Parameters** ### Parameters
- `input` **([Buffer][5] \| [String][6])?** if present, can be - `input` **([Buffer][1] \| [String][2])?** if present, can be
a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file. a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present. JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
- `options` **[Object][7]?** if present, is an Object with optional attributes. - `options` **[Object][3]?** if present, is an Object with optional attributes.
- `options.failOnError` **[Boolean][8]** by default apply a "best effort" - `options.failOnError` **[Boolean][4]** by default apply a "best effort"
to decode images, even if the data is corrupt or invalid. Set this flag to true to decode images, even if the data is corrupt or invalid. Set this flag to true
if you'd rather halt processing and raise an error when loading invalid images. (optional, default `false`) if you'd rather halt processing and raise an error when loading invalid images. (optional, default `false`)
- `options.density` **[Number][9]** integral number representing the DPI for vector images. (optional, default `72`) - `options.density` **[Number][5]** integral number representing the DPI for vector images. (optional, default `72`)
- `options.raw` **[Object][7]?** describes raw pixel input image data. See `raw()` for pixel ordering. - `options.page` **[Number][5]** page number to extract for multi-page input (GIF, TIFF) (optional, default `0`)
- `options.raw.width` **[Number][9]?** - `options.raw` **[Object][3]?** describes raw pixel input image data. See `raw()` for pixel ordering.
- `options.raw.height` **[Number][9]?** - `options.raw.width` **[Number][5]?**
- `options.raw.channels` **[Number][9]?** 1-4 - `options.raw.height` **[Number][5]?**
- `options.create` **[Object][7]?** describes a new image to be created. - `options.raw.channels` **[Number][5]?** 1-4
- `options.create.width` **[Number][9]?** - `options.create` **[Object][3]?** describes a new image to be created.
- `options.create.height` **[Number][9]?** - `options.create.width` **[Number][5]?**
- `options.create.channels` **[Number][9]?** 3-4 - `options.create.height` **[Number][5]?**
- `options.create.background` **([String][6] \| [Object][7])?** parsed by the [color][10] module to extract values for red, green, blue and alpha. - `options.create.channels` **[Number][5]?** 3-4
- `options.create.background` **([String][2] \| [Object][3])?** parsed by the [color][6] module to extract values for red, green, blue and alpha.
**Examples** ### Examples
```javascript ```javascript
sharp('input.jpg') sharp('input.jpg')
@@ -61,7 +55,7 @@ sharp({
width: 300, width: 300,
height: 200, height: 200,
channels: 4, channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 128 } background: { r: 255, g: 0, b: 0, alpha: 0.5 }
} }
}) })
.png() .png()
@@ -69,27 +63,27 @@ sharp({
.then( ... ); .then( ... );
``` ```
- Throws **[Error][11]** Invalid parameters - Throws **[Error][7]** Invalid parameters
Returns **[Sharp][12]** Returns **[Sharp][8]**
### format ### format
An Object containing nested boolean values representing the available input and output formats/methods. An Object containing nested boolean values representing the available input and output formats/methods.
**Examples** #### Examples
```javascript ```javascript
console.log(sharp.format); console.log(sharp.format);
``` ```
Returns **[Object][7]** Returns **[Object][3]**
### versions ### versions
An Object containing the version numbers of libvips and its dependencies. An Object containing the version numbers of libvips and its dependencies.
**Examples** #### Examples
```javascript ```javascript
console.log(sharp.versions); console.log(sharp.versions);
@@ -102,7 +96,7 @@ An EventEmitter that emits a `change` event when a task is either:
- queued, waiting for _libuv_ to provide a worker thread - queued, waiting for _libuv_ to provide a worker thread
- complete - complete
**Examples** ### Examples
```javascript ```javascript
sharp.queue.on('change', function(queueLength) { sharp.queue.on('change', function(queueLength) {
@@ -110,26 +104,18 @@ sharp.queue.on('change', function(queueLength) {
}); });
``` ```
[1]: #sharp [1]: https://nodejs.org/api/buffer.html
[2]: #format [2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[3]: #versions [3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[4]: #queue [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[5]: https://nodejs.org/api/buffer.html [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String [6]: https://www.npmjs.org/package/color
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object [7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean [8]: #sharp
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[10]: https://www.npmjs.org/package/color
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[12]: #sharp

View File

@@ -1,20 +1,12 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [clone][1]
- [metadata][2]
- [stats][3]
- [limitInputPixels][4]
- [sequentialRead][5]
## clone ## clone
Take a "snapshot" of the Sharp instance, returning a new instance. Take a "snapshot" of the Sharp instance, returning a new instance.
Cloned instances inherit the input of their parent instance. Cloned instances inherit the input of their parent instance.
This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream. This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream.
**Examples** ### Examples
```javascript ```javascript
const pipeline = sharp().rotate(); const pipeline = sharp().rotate();
@@ -35,23 +27,23 @@ A Promises/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` - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
- `width`: Number of pixels wide (EXIF orientation is not taken into consideration) - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
- `height`: Number of pixels high (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` [...][6] - `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 - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
- `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...][7] - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...][2]
- `density`: Number of pixels per inch (DPI), if present - `density`: Number of pixels per inch (DPI), if present
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
- `orientation`: Number value of the EXIF Orientation header, if present - `orientation`: Number value of the EXIF Orientation header, if present
- `exif`: Buffer containing raw EXIF data, if present - `exif`: Buffer containing raw EXIF data, if present
- `icc`: Buffer containing raw [ICC][8] profile data, if present - `icc`: Buffer containing raw [ICC][3] profile data, if present
- `iptc`: Buffer containing raw IPTC data, if present - `iptc`: Buffer containing raw IPTC data, if present
- `xmp`: Buffer containing raw XMP data, if present - `xmp`: Buffer containing raw XMP data, if present
**Parameters** ### Parameters
- `callback` **[Function][9]?** called with the arguments `(err, metadata)` - `callback` **[Function][4]?** called with the arguments `(err, metadata)`
**Examples** ### Examples
```javascript ```javascript
const image = sharp(inputJpg); const image = sharp(inputJpg);
@@ -68,7 +60,7 @@ image
}); });
``` ```
Returns **([Promise][10]&lt;[Object][11]> | Sharp)** Returns **([Promise][5]&lt;[Object][6]> | Sharp)**
## stats ## stats
@@ -87,12 +79,13 @@ A Promise is returned when `callback` is not provided.
- `maxX` (x-coordinate of one of the pixel where the maximum lies) - `maxX` (x-coordinate of one of the pixel where the maximum lies)
- `maxY` (y-coordinate of one of the pixel where the maximum lies) - `maxY` (y-coordinate of one of the pixel where the maximum lies)
- `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel - `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
- `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
**Parameters** ### Parameters
- `callback` **[Function][9]?** called with the arguments `(err, stats)` - `callback` **[Function][4]?** called with the arguments `(err, stats)`
**Examples** ### Examples
```javascript ```javascript
const image = sharp(inputJpg); const image = sharp(inputJpg);
@@ -103,7 +96,7 @@ image
}); });
``` ```
Returns **[Promise][10]&lt;[Object][11]>** Returns **[Promise][5]&lt;[Object][6]>**
## limitInputPixels ## limitInputPixels
@@ -111,12 +104,12 @@ Do not process input images where the number of pixels (width _ height) exceeds
Assumes image dimensions contained in the input metadata can be trusted. Assumes image dimensions contained in the input metadata can be trusted.
The default limit is 268402689 (0x3FFF _ 0x3FFF) pixels. The default limit is 268402689 (0x3FFF _ 0x3FFF) pixels.
**Parameters** ### Parameters
- `limit` **([Number][12] \| [Boolean][13])** an integral Number of pixels, zero or false to remove limit, true to use default limit. - `limit` **([Number][7] \| [Boolean][8])** an integral Number of pixels, zero or false to remove limit, true to use default limit.
- Throws **[Error][14]** Invalid limit - Throws **[Error][9]** Invalid limit
Returns **Sharp** Returns **Sharp**
@@ -127,36 +120,26 @@ This will reduce memory usage and can improve performance on some systems.
The default behaviour _before_ function call is `false`, meaning the libvips access method is not sequential. The default behaviour _before_ function call is `false`, meaning the libvips access method is not sequential.
**Parameters** ### Parameters
- `sequentialRead` **[Boolean][13]** (optional, default `true`) - `sequentialRead` **[Boolean][8]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
[1]: #clone [1]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636
[2]: #metadata [2]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672
[3]: #stats [3]: https://www.npmjs.com/package/icc
[4]: #limitinputpixels [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[5]: #sequentialread [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[6]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636 [6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[7]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672 [7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[8]: https://www.npmjs.com/package/icc [8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function [9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error

View File

@@ -1,25 +1,5 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [rotate][1]
- [extract][2]
- [flip][3]
- [flop][4]
- [sharpen][5]
- [blur][6]
- [extend][7]
- [flatten][8]
- [trim][9]
- [gamma][10]
- [negate][11]
- [normalise][12]
- [normalize][13]
- [convolve][14]
- [threshold][15]
- [boolean][16]
- [linear][17]
## rotate ## rotate
Rotate the output image by either an explicit angle Rotate the output image by either an explicit angle
@@ -36,11 +16,11 @@ The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
Method order is important when both rotating and extracting regions, Method order is important when both rotating and extracting regions,
for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`. for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.
**Parameters** ### Parameters
- `angle` **[Number][18]** angle of rotation, must be a multiple of 90. (optional, default `auto`) - `angle` **[Number][1]** angle of rotation, must be a multiple of 90. (optional, default `auto`)
**Examples** ### Examples
```javascript ```javascript
const pipeline = sharp() const pipeline = sharp()
@@ -54,7 +34,7 @@ const pipeline = sharp()
readableStream.pipe(pipeline); readableStream.pipe(pipeline);
``` ```
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -66,15 +46,15 @@ Extract a region of the image.
- Use `extract` after `resize` for post-resize extraction. - Use `extract` after `resize` for post-resize extraction.
- Use `extract` before and after for both. - Use `extract` before and after for both.
**Parameters** ### Parameters
- `options` **[Object][20]** - `options` **[Object][3]**
- `options.left` **[Number][18]** zero-indexed offset from left edge - `options.left` **[Number][1]** zero-indexed offset from left edge
- `options.top` **[Number][18]** zero-indexed offset from top edge - `options.top` **[Number][1]** zero-indexed offset from top edge
- `options.width` **[Number][18]** dimension of extracted image - `options.width` **[Number][1]** dimension of extracted image
- `options.height` **[Number][18]** dimension of extracted image - `options.height` **[Number][1]** dimension of extracted image
**Examples** ### Examples
```javascript ```javascript
sharp(input) sharp(input)
@@ -94,7 +74,7 @@ sharp(input)
}); });
``` ```
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -103,9 +83,9 @@ Returns **Sharp**
Flip the image about the vertical Y axis. This always occurs after rotation, if any. 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. The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
**Parameters** ### Parameters
- `flip` **[Boolean][21]** (optional, default `true`) - `flip` **[Boolean][4]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -114,9 +94,9 @@ Returns **Sharp**
Flop the image about the horizontal X axis. This always occurs after rotation, if any. 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. The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
**Parameters** ### Parameters
- `flop` **[Boolean][21]** (optional, default `true`) - `flop` **[Boolean][4]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -127,14 +107,28 @@ 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. 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. Separate control over the level of sharpening in "flat" and "jagged" areas is available.
**Parameters** ### Parameters
- `sigma` **[Number][18]?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`. - `sigma` **[Number][1]?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- `flat` **[Number][18]** the level of sharpening to apply to "flat" areas. (optional, default `1.0`) - `flat` **[Number][1]** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
- `jagged` **[Number][18]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`) - `jagged` **[Number][1]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp**
## median
Apply median filter.
When used without parameters the default window is 3x3.
### Parameters
- `size` **[Number][1]** square mask size: size x size (optional, default `3`)
- Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -144,12 +138,12 @@ Blur the image.
When used without parameters, performs a fast, mild blur of the output image. When used without parameters, performs a fast, mild blur of the output image.
When a `sigma` is provided, performs a slower, more accurate Gaussian blur. When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
**Parameters** ### Parameters
- `sigma` **[Number][18]?** a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`. - `sigma` **[Number][1]?** a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -158,15 +152,15 @@ Returns **Sharp**
Extends/pads the edges of the image with the colour provided to the `background` method. Extends/pads the edges of the image with the colour provided to the `background` method.
This operation will always occur after resizing and extraction, if any. This operation will always occur after resizing and extraction, if any.
**Parameters** ### Parameters
- `extend` **([Number][18] \| [Object][20])** single pixel count to add to all edges or an Object with per-edge counts - `extend` **([Number][1] \| [Object][3])** single pixel count to add to all edges or an Object with per-edge counts
- `extend.top` **[Number][18]?** - `extend.top` **[Number][1]?**
- `extend.left` **[Number][18]?** - `extend.left` **[Number][1]?**
- `extend.bottom` **[Number][18]?** - `extend.bottom` **[Number][1]?**
- `extend.right` **[Number][18]?** - `extend.right` **[Number][1]?**
**Examples** ### Examples
```javascript ```javascript
// Resize to 140 pixels wide, then add 10 transparent pixels // Resize to 140 pixels wide, then add 10 transparent pixels
@@ -178,7 +172,7 @@ sharp(input)
... ...
``` ```
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -186,9 +180,9 @@ Returns **Sharp**
Merge alpha transparency channel, if any, with `background`. Merge alpha transparency channel, if any, with `background`.
**Parameters** ### Parameters
- `flatten` **[Boolean][21]** (optional, default `true`) - `flatten` **[Boolean][4]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -196,12 +190,12 @@ Returns **Sharp**
Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel. Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel.
**Parameters** ### Parameters
- `tolerance` **[Number][18]** value between 1 and 99 representing the percentage similarity. (optional, default `10`) - `tolerance` **[Number][1]** value between 1 and 99 representing the percentage similarity. (optional, default `10`)
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -213,12 +207,12 @@ This can improve the perceived brightness of a resized image in non-linear colou
JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation
when applying a gamma correction. when applying a gamma correction.
**Parameters** ### Parameters
- `gamma` **[Number][18]** value between 1.0 and 3.0. (optional, default `2.2`) - `gamma` **[Number][1]** value between 1.0 and 3.0. (optional, default `2.2`)
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -226,9 +220,9 @@ Returns **Sharp**
Produce the "negative" of the image. Produce the "negative" of the image.
**Parameters** ### Parameters
- `negate` **[Boolean][21]** (optional, default `true`) - `negate` **[Boolean][4]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -236,9 +230,9 @@ Returns **Sharp**
Enhance output image contrast by stretching its luminance to cover the full dynamic range. Enhance output image contrast by stretching its luminance to cover the full dynamic range.
**Parameters** ### Parameters
- `normalise` **[Boolean][21]** (optional, default `true`) - `normalise` **[Boolean][4]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -246,9 +240,9 @@ Returns **Sharp**
Alternative spelling of normalise. Alternative spelling of normalise.
**Parameters** ### Parameters
- `normalize` **[Boolean][21]** (optional, default `true`) - `normalize` **[Boolean][4]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -256,16 +250,16 @@ Returns **Sharp**
Convolve the image with the specified kernel. Convolve the image with the specified kernel.
**Parameters** ### Parameters
- `kernel` **[Object][20]** - `kernel` **[Object][3]**
- `kernel.width` **[Number][18]** width of the kernel in pixels. - `kernel.width` **[Number][1]** width of the kernel in pixels.
- `kernel.height` **[Number][18]** width of the kernel in pixels. - `kernel.height` **[Number][1]** width of the kernel in pixels.
- `kernel.kernel` **[Array][22]&lt;[Number][18]>** Array of length `width*height` containing the kernel values. - `kernel.kernel` **[Array][5]&lt;[Number][1]>** Array of length `width*height` containing the kernel values.
- `kernel.scale` **[Number][18]** the scale of the kernel in pixels. (optional, default `sum`) - `kernel.scale` **[Number][1]** the scale of the kernel in pixels. (optional, default `sum`)
- `kernel.offset` **[Number][18]** the offset of the kernel in pixels. (optional, default `0`) - `kernel.offset` **[Number][1]** the offset of the kernel in pixels. (optional, default `0`)
**Examples** ### Examples
```javascript ```javascript
sharp(input) sharp(input)
@@ -281,7 +275,7 @@ sharp(input)
}); });
``` ```
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -289,15 +283,15 @@ Returns **Sharp**
Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0. Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
**Parameters** ### Parameters
- `threshold` **[Number][18]** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`) - `threshold` **[Number][1]** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`)
- `options` **[Object][20]?** - `options` **[Object][3]?**
- `options.greyscale` **[Boolean][21]** convert to single channel greyscale. (optional, default `true`) - `options.greyscale` **[Boolean][4]** convert to single channel greyscale. (optional, default `true`)
- `options.grayscale` **[Boolean][21]** alternative spelling for greyscale. (optional, default `true`) - `options.grayscale` **[Boolean][4]** alternative spelling for greyscale. (optional, default `true`)
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -308,18 +302,18 @@ Perform a bitwise boolean operation with operand image.
This operation creates an output image where each pixel is the result of This operation creates an output image where each pixel is the result of
the selected bitwise boolean `operation` between the corresponding pixels of the input images. the selected bitwise boolean `operation` between the corresponding pixels of the input images.
**Parameters** ### Parameters
- `operand` **([Buffer][23] \| [String][24])** Buffer containing image data or String containing the path to an image file. - `operand` **([Buffer][6] \| [String][7])** Buffer containing image data or String containing the path to an image file.
- `operator` **[String][24]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively. - `operator` **[String][7]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
- `options` **[Object][20]?** - `options` **[Object][3]?**
- `options.raw` **[Object][20]?** describes operand when using raw pixel data. - `options.raw` **[Object][3]?** describes operand when using raw pixel data.
- `options.raw.width` **[Number][18]?** - `options.raw.width` **[Number][1]?**
- `options.raw.height` **[Number][18]?** - `options.raw.height` **[Number][1]?**
- `options.raw.channels` **[Number][18]?** - `options.raw.channels` **[Number][1]?**
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -327,60 +321,26 @@ Returns **Sharp**
Apply the linear formula a \* input + b to the image (levels adjustment) Apply the linear formula a \* input + b to the image (levels adjustment)
**Parameters** ### Parameters
- `a` **[Number][18]** multiplier (optional, default `1.0`) - `a` **[Number][1]** multiplier (optional, default `1.0`)
- `b` **[Number][18]** offset (optional, default `0.0`) - `b` **[Number][1]** offset (optional, default `0.0`)
- Throws **[Error][19]** Invalid parameters - Throws **[Error][2]** Invalid parameters
Returns **Sharp** Returns **Sharp**
[1]: #rotate [1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[2]: #extract [2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[3]: #flip [3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[4]: #flop [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[5]: #sharpen [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[6]: #blur [6]: https://nodejs.org/api/buffer.html
[7]: #extend [7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[8]: #flatten
[9]: #trim
[10]: #gamma
[11]: #negate
[12]: #normalise
[13]: #normalize
[14]: #convolve
[15]: #threshold
[16]: #boolean
[17]: #linear
[18]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[20]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[21]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[22]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[23]: https://nodejs.org/api/buffer.html
[24]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String

View File

@@ -1,18 +1,5 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [toFile][1]
- [toBuffer][2]
- [withMetadata][3]
- [jpeg][4]
- [png][5]
- [webp][6]
- [tiff][7]
- [raw][8]
- [toFormat][9]
- [tile][10]
## toFile ## toFile
Write output image data to a file. Write output image data to a file.
@@ -23,18 +10,31 @@ Note that raw pixel data is only supported for buffer output.
A `Promise` is returned when `callback` is not provided. A `Promise` is returned when `callback` is not provided.
**Parameters** ### Parameters
- `fileOut` **[String][11]** the path to write the image data to. - `fileOut` **[String][1]** the path to write the image data to.
- `callback` **[Function][12]?** called on completion with two arguments `(err, info)`. - `callback` **[Function][2]?** called on completion with two arguments `(err, info)`.
`info` contains the output image `format`, `size` (bytes), `width`, `height`, `info` contains the output image `format`, `size` (bytes), `width`, `height`,
`channels` and `premultiplied` (indicating if premultiplication was used). `channels` and `premultiplied` (indicating if premultiplication was used).
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`. When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
### Examples
- Throws **[Error][13]** Invalid parameters ```javascript
sharp(input)
.toFile('output.png', (err, info) => { ... });
```
Returns **[Promise][14]&lt;[Object][15]>** when no callback is provided ```javascript
sharp(input)
.toFile('output.png')
.then(info => { ... })
.catch(err => { ... });
```
- Throws **[Error][3]** Invalid parameters
Returns **[Promise][4]&lt;[Object][5]>** when no callback is provided
## toBuffer ## toBuffer
@@ -52,13 +52,34 @@ By default, the format will match the input image, except GIF and SVG input whic
A `Promise` is returned when `callback` is not provided. A `Promise` is returned when `callback` is not provided.
**Parameters** ### Parameters
- `options` **[Object][15]?** - `options` **[Object][5]?**
- `options.resolveWithObject` **[Boolean][16]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`. - `options.resolveWithObject` **[Boolean][6]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
- `callback` **[Function][12]?** - `callback` **[Function][2]?**
Returns **[Promise][14]&lt;[Buffer][17]>** when no callback is provided ### Examples
```javascript
sharp(input)
.toBuffer((err, data, info) => { ... });
```
```javascript
sharp(input)
.toBuffer()
.then(data => { ... })
.catch(err => { ... });
```
```javascript
sharp(input)
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => { ... })
.catch(err => { ... });
```
Returns **[Promise][4]&lt;[Buffer][7]>** when no callback is provided
## withMetadata ## withMetadata
@@ -66,13 +87,21 @@ Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space. The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
This will also convert to and add a web-friendly sRGB ICC profile. This will also convert to and add a web-friendly sRGB ICC profile.
**Parameters** ### Parameters
- `withMetadata` **[Object][15]?** - `withMetadata` **[Object][5]?**
- `withMetadata.orientation` **[Number][18]?** value between 1 and 8, used to update the EXIF `Orientation` tag. - `withMetadata.orientation` **[Number][8]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
### Examples
- Throws **[Error][13]** Invalid parameters ```javascript
sharp('input.jpg')
.withMetadata()
.toFile('output-with-metadata.jpg')
.then(info => { ... });
```
- Throws **[Error][3]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -80,20 +109,35 @@ Returns **Sharp**
Use these JPEG options for output image. Use these JPEG options for output image.
**Parameters** ### Parameters
- `options` **[Object][15]?** output options - `options` **[Object][5]?** output options
- `options.quality` **[Number][18]** quality, integer 1-100 (optional, default `80`) - `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
- `options.progressive` **[Boolean][16]** use progressive (interlace) scan (optional, default `false`) - `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
- `options.chromaSubsampling` **[String][11]** set to '4:4:4' to prevent chroma subsampling when quality &lt;= 90 (optional, default `'4:2:0'`) - `options.chromaSubsampling` **[String][1]** set to '4:4:4' to prevent chroma subsampling when quality &lt;= 90 (optional, default `'4:2:0'`)
- `options.trellisQuantisation` **[Boolean][16]** apply trellis quantisation, requires mozjpeg (optional, default `false`) - `options.trellisQuantisation` **[Boolean][6]** apply trellis quantisation, requires mozjpeg (optional, default `false`)
- `options.overshootDeringing` **[Boolean][16]** apply overshoot deringing, requires mozjpeg (optional, default `false`) - `options.overshootDeringing` **[Boolean][6]** apply overshoot deringing, requires mozjpeg (optional, default `false`)
- `options.optimiseScans` **[Boolean][16]** optimise progressive scans, forces progressive, requires mozjpeg (optional, default `false`) - `options.optimiseScans` **[Boolean][6]** optimise progressive scans, forces progressive, requires mozjpeg (optional, default `false`)
- `options.optimizeScans` **[Boolean][16]** alternative spelling of optimiseScans (optional, default `false`) - `options.optimizeScans` **[Boolean][6]** alternative spelling of optimiseScans (optional, default `false`)
- `options.force` **[Boolean][16]** force JPEG output, otherwise attempt to use input format (optional, default `true`) - `options.optimiseCoding` **[Boolean][6]** optimise Huffman coding tables (optional, default `true`)
- `options.optimizeCoding` **[Boolean][6]** alternative spelling of optimiseCoding (optional, default `true`)
- `options.quantisationTable` **[Number][8]** quantization table to use, integer 0-8, requires mozjpeg (optional, default `0`)
- `options.quantizationTable` **[Number][8]** alternative spelling of quantisationTable (optional, default `0`)
- `options.force` **[Boolean][6]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
### Examples
- Throws **[Error][13]** Invalid options ```javascript
// Convert any input to very high quality JPEG output
const data = await sharp(input)
.jpeg({
quality: 100,
chromaSubsampling: '4:4:4'
})
.toBuffer();
```
- Throws **[Error][3]** Invalid options
Returns **Sharp** Returns **Sharp**
@@ -101,16 +145,27 @@ Returns **Sharp**
Use these PNG options for output image. Use these PNG options for output image.
**Parameters** PNG output is always full colour at 8 or 16 bits per pixel.
Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
- `options` **[Object][15]?** ### Parameters
- `options.progressive` **[Boolean][16]** use progressive (interlace) scan (optional, default `false`)
- `options.compressionLevel` **[Number][18]** zlib compression level, 0-9 (optional, default `9`)
- `options.adaptiveFiltering` **[Boolean][16]** use adaptive row filtering (optional, default `false`)
- `options.force` **[Boolean][16]** force PNG output, otherwise attempt to use input format (optional, default `true`)
- `options` **[Object][5]?**
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
- `options.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
- `options.force` **[Boolean][6]** force PNG output, otherwise attempt to use input format (optional, default `true`)
- Throws **[Error][13]** Invalid options ### Examples
```javascript
// Convert any input to PNG output
const data = await sharp(input)
.png()
.toBuffer();
```
- Throws **[Error][3]** Invalid options
Returns **Sharp** Returns **Sharp**
@@ -118,17 +173,25 @@ Returns **Sharp**
Use these WebP options for output image. Use these WebP options for output image.
**Parameters** ### Parameters
- `options` **[Object][15]?** output options - `options` **[Object][5]?** output options
- `options.quality` **[Number][18]** quality, integer 1-100 (optional, default `80`) - `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
- `options.alphaQuality` **[Number][18]** quality of alpha layer, integer 0-100 (optional, default `100`) - `options.alphaQuality` **[Number][8]** quality of alpha layer, integer 0-100 (optional, default `100`)
- `options.lossless` **[Boolean][16]** use lossless compression mode (optional, default `false`) - `options.lossless` **[Boolean][6]** use lossless compression mode (optional, default `false`)
- `options.nearLossless` **[Boolean][16]** use near_lossless compression mode (optional, default `false`) - `options.nearLossless` **[Boolean][6]** use near_lossless compression mode (optional, default `false`)
- `options.force` **[Boolean][16]** force WebP output, otherwise attempt to use input format (optional, default `true`) - `options.force` **[Boolean][6]** force WebP output, otherwise attempt to use input format (optional, default `true`)
### Examples
- Throws **[Error][13]** Invalid options ```javascript
// Convert any input to lossless WebP output
const data = await sharp(input)
.webp({ lossless: true })
.toBuffer();
```
- Throws **[Error][3]** Invalid options
Returns **Sharp** Returns **Sharp**
@@ -136,19 +199,31 @@ Returns **Sharp**
Use these TIFF options for output image. Use these TIFF options for output image.
**Parameters** ### Parameters
- `options` **[Object][15]?** output options - `options` **[Object][5]?** output options
- `options.quality` **[Number][18]** quality, integer 1-100 (optional, default `80`) - `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
- `options.force` **[Boolean][16]** force TIFF output, otherwise attempt to use input format (optional, default `true`) - `options.force` **[Boolean][6]** force TIFF output, otherwise attempt to use input format (optional, default `true`)
- `options.compression` **[Boolean][16]** compression options: lzw, deflate, jpeg (optional, default `'jpeg'`) - `options.compression` **[Boolean][6]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
- `options.predictor` **[Boolean][16]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`) - `options.predictor` **[Boolean][6]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
- `options.xres` **[Number][18]** horizontal resolution in pixels/mm (optional, default `1.0`) - `options.xres` **[Number][8]** horizontal resolution in pixels/mm (optional, default `1.0`)
- `options.yres` **[Number][18]** vertical resolution in pixels/mm (optional, default `1.0`) - `options.yres` **[Number][8]** vertical resolution in pixels/mm (optional, default `1.0`)
- `options.squash` **[Boolean][16]** squash 8-bit images down to 1 bit (optional, default `false`) - `options.squash` **[Boolean][6]** squash 8-bit images down to 1 bit (optional, default `false`)
### Examples
- Throws **[Error][13]** Invalid options ```javascript
// Convert SVG input to LZW-compressed, 1 bit per pixel TIFF output
sharp('input.svg')
.tiff({
compression: 'lzw',
squash: true
})
.toFile('1-bpp-output.tiff')
.then(info => { ... });
```
- Throws **[Error][3]** Invalid options
Returns **Sharp** Returns **Sharp**
@@ -156,19 +231,36 @@ Returns **Sharp**
Force output to be raw, uncompressed uint8 pixel data. Force output to be raw, uncompressed uint8 pixel data.
### Examples
```javascript
// Extract raw RGB pixel data from JPEG input
const { data, info } = await sharp('input.jpg')
.raw()
.toBuffer({ resolveWithObject: true });
```
Returns **Sharp** Returns **Sharp**
## toFormat ## toFormat
Force output to a given format. Force output to a given format.
**Parameters** ### Parameters
- `format` **([String][11] \| [Object][15])** as a String or an Object with an 'id' attribute - `format` **([String][1] \| [Object][5])** as a String or an Object with an 'id' attribute
- `options` **[Object][15]** output options - `options` **[Object][5]** output options
### Examples
- Throws **[Error][13]** unsupported format or options ```javascript
// Convert any input to PNG output
const data = await sharp(input)
.toFormat('png')
.toBuffer();
```
- Throws **[Error][3]** unsupported format or options
Returns **Sharp** Returns **Sharp**
@@ -178,16 +270,19 @@ Use tile-based deep zoom (image pyramid) output.
Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions. Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format. Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
**Parameters** Warning: multiple sharp instances concurrently producing tile output can expose a possible race condition in some versions of libgsf.
- `tile` **[Object][15]?** ### Parameters
- `tile.size` **[Number][18]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
- `tile.overlap` **[Number][18]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
- `tile.angle` **[Number][18]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
- `tile.container` **[String][11]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
- `tile.layout` **[String][11]** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
**Examples** - `tile` **[Object][5]?**
- `tile.size` **[Number][8]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
- `tile.overlap` **[Number][8]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
- `tile.angle` **[Number][8]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
- `tile.depth` **[String][1]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
- `tile.container` **[String][1]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
- `tile.layout` **[String][1]** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
### Examples
```javascript ```javascript
sharp('input.tiff') sharp('input.tiff')
@@ -201,42 +296,22 @@ sharp('input.tiff')
}); });
``` ```
- Throws **[Error][13]** Invalid parameters - Throws **[Error][3]** Invalid parameters
Returns **Sharp** Returns **Sharp**
[1]: #tofile [1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[2]: #tobuffer [2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[3]: #withmetadata [3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[4]: #jpeg [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[5]: #png [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[6]: #webp [6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[7]: #tiff [7]: https://nodejs.org/api/buffer.html
[8]: #raw [8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[9]: #toformat
[10]: #tile
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[16]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[17]: https://nodejs.org/api/buffer.html
[18]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number

View File

@@ -1,15 +1,5 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [resize][1]
- [crop][2]
- [embed][3]
- [max][4]
- [min][5]
- [ignoreAspectRatio][6]
- [withoutEnlargement][7]
## resize ## resize
Resize image to `width` x `height`. Resize image to `width` x `height`.
@@ -17,20 +7,20 @@ By default, the resized image is centre cropped to the exact size specified.
Possible kernels are: Possible kernels are:
- `nearest`: Use [nearest neighbour interpolation][8]. - `nearest`: Use [nearest neighbour interpolation][1].
- `cubic`: Use a [Catmull-Rom spline][9]. - `cubic`: Use a [Catmull-Rom spline][2].
- `lanczos2`: Use a [Lanczos kernel][10] with `a=2`. - `lanczos2`: Use a [Lanczos kernel][3] with `a=2`.
- `lanczos3`: Use a Lanczos kernel with `a=3` (the default). - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
**Parameters** ### Parameters
- `width` **[Number][11]?** pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height. - `width` **[Number][4]?** pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
- `height` **[Number][11]?** pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width. - `height` **[Number][4]?** pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
- `options` **[Object][12]?** - `options` **[Object][5]?**
- `options.kernel` **[String][13]** the kernel to use for image reduction. (optional, default `'lanczos3'`) - `options.kernel` **[String][6]** the kernel to use for image reduction. (optional, default `'lanczos3'`)
- `options.fastShrinkOnLoad` **[Boolean][14]** take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern on some images. (optional, default `true`) - `options.fastShrinkOnLoad` **[Boolean][7]** take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern on some images. (optional, default `true`)
**Examples** ### Examples
```javascript ```javascript
sharp(inputBuffer) sharp(inputBuffer)
@@ -47,7 +37,7 @@ sharp(inputBuffer)
}); });
``` ```
- Throws **[Error][15]** Invalid parameters - Throws **[Error][8]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -61,14 +51,14 @@ Possible attributes of the optional `sharp.gravity` are `north`, `northeast`, `e
The experimental strategy-based approach resizes so one dimension is at its target length The experimental strategy-based approach resizes so one dimension is at its target length
then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy. then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
- `entropy`: focus on the region with the highest [Shannon entropy][16]. - `entropy`: focus on the region with the highest [Shannon entropy][9].
- `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones. - `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
**Parameters** ### Parameters
- `crop` **[String][13]** A member of `sharp.gravity` to crop to an edge/corner or `sharp.strategy` to crop dynamically. (optional, default `'centre'`) - `crop` **[String][6]** A member of `sharp.gravity` to crop to an edge/corner or `sharp.strategy` to crop dynamically. (optional, default `'centre'`)
**Examples** ### Examples
```javascript ```javascript
const transformer = sharp() const transformer = sharp()
@@ -82,7 +72,7 @@ const transformer = sharp()
readableStream.pipe(transformer).pipe(writableStream); readableStream.pipe(transformer).pipe(writableStream);
``` ```
- Throws **[Error][15]** Invalid parameters - Throws **[Error][8]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -94,11 +84,11 @@ then embed on a background of the exact `width` and `height` specified.
If the background contains an alpha value then WebP and PNG format output images will If the background contains an alpha value then WebP and PNG format output images will
contain an alpha channel, even when the input image does not. contain an alpha channel, even when the input image does not.
**Parameters** ### Parameters
- `embed` **[String][13]** A member of `sharp.gravity` to embed to an edge/corner. (optional, default `'centre'`) - `embed` **[String][6]** A member of `sharp.gravity` to embed to an edge/corner. (optional, default `'centre'`)
**Examples** ### Examples
```javascript ```javascript
sharp('input.gif') sharp('input.gif')
@@ -115,7 +105,7 @@ sharp('input.gif')
}); });
``` ```
- Throws **[Error][15]** Invalid parameters - Throws **[Error][8]** Invalid parameters
Returns **Sharp** Returns **Sharp**
@@ -126,7 +116,7 @@ while ensuring its dimensions are less than or equal to the `width` and `height`
Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`. Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
**Examples** ### Examples
```javascript ```javascript
sharp(inputBuffer) sharp(inputBuffer)
@@ -167,40 +157,26 @@ Use with `max()` to preserve the image's aspect ratio.
The default behaviour _before_ function call is `false`, meaning the image will be enlarged. The default behaviour _before_ function call is `false`, meaning the image will be enlarged.
**Parameters** ### Parameters
- `withoutEnlargement` **[Boolean][14]** (optional, default `true`) - `withoutEnlargement` **[Boolean][7]** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
[1]: #resize [1]: http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation
[2]: #crop [2]: https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline
[3]: #embed [3]: https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel
[4]: #max [4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[5]: #min [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[6]: #ignoreaspectratio [6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[7]: #withoutenlargement [7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[8]: http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation [8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[9]: https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline [9]: https://en.wikipedia.org/wiki/Entropy_%28information_theory%29
[10]: https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[16]: https://en.wikipedia.org/wiki/Entropy_%28information_theory%29

View File

@@ -1,12 +1,5 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### Table of Contents
- [cache][1]
- [concurrency][2]
- [counters][3]
- [simd][4]
## cache ## cache
Gets or, when options are provided, sets the limits of _libvips'_ operation cache. Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
@@ -14,14 +7,14 @@ Existing entries in the cache will be trimmed after any change in limits.
This method always returns cache statistics, This method always returns cache statistics,
useful for determining how much working memory is required for a particular task. useful for determining how much working memory is required for a particular task.
**Parameters** ### Parameters
- `options` **([Object][5] \| [Boolean][6])** Object with the following attributes, or Boolean where true uses default cache settings and false removes all caching (optional, default `true`) - `options` **([Object][1] \| [Boolean][2])** Object with the following attributes, or Boolean where true uses default cache settings and false removes all caching (optional, default `true`)
- `options.memory` **[Number][7]** is the maximum memory in MB to use for this cache (optional, default `50`) - `options.memory` **[Number][3]** is the maximum memory in MB to use for this cache (optional, default `50`)
- `options.files` **[Number][7]** is the maximum number of files to hold open (optional, default `20`) - `options.files` **[Number][3]** is the maximum number of files to hold open (optional, default `20`)
- `options.items` **[Number][7]** is the maximum number of operations to cache (optional, default `100`) - `options.items` **[Number][3]** is the maximum number of operations to cache (optional, default `100`)
**Examples** ### Examples
```javascript ```javascript
const stats = sharp.cache(); const stats = sharp.cache();
@@ -33,7 +26,7 @@ sharp.cache( { files: 0 } );
sharp.cache(false); sharp.cache(false);
``` ```
Returns **[Object][5]** Returns **[Object][1]**
## concurrency ## concurrency
@@ -47,11 +40,11 @@ is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
This method always returns the current concurrency. This method always returns the current concurrency.
**Parameters** ### Parameters
- `concurrency` **[Number][7]?** - `concurrency` **[Number][3]?**
**Examples** ### Examples
```javascript ```javascript
const threads = sharp.concurrency(); // 4 const threads = sharp.concurrency(); // 4
@@ -59,7 +52,7 @@ sharp.concurrency(2); // 2
sharp.concurrency(0); // 4 sharp.concurrency(0); // 4
``` ```
Returns **[Number][7]** concurrency Returns **[Number][3]** concurrency
## counters ## counters
@@ -68,13 +61,13 @@ Provides access to internal task counters.
- queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool. - queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
- process is the number of resize tasks currently being processed. - process is the number of resize tasks currently being processed.
**Examples** ### Examples
```javascript ```javascript
const counters = sharp.counters(); // { queue: 2, process: 4 } const counters = sharp.counters(); // { queue: 2, process: 4 }
``` ```
Returns **[Object][5]** Returns **[Object][1]**
## simd ## simd
@@ -87,11 +80,11 @@ by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM N
This feature is currently off by default but future versions may reverse this. This feature is currently off by default but future versions may reverse this.
Versions of liborc prior to 0.4.25 are known to segfault under heavy load. Versions of liborc prior to 0.4.25 are known to segfault under heavy load.
**Parameters** ### Parameters
- `simd` **[Boolean][6]** (optional, default `false`) - `simd` **[Boolean][2]** (optional, default `false`)
**Examples** ### Examples
```javascript ```javascript
const simd = sharp.simd(); const simd = sharp.simd();
@@ -103,18 +96,10 @@ const simd = sharp.simd(true);
// attempts to enable the use of SIMD, returning true if available // attempts to enable the use of SIMD, returning true if available
``` ```
Returns **[Boolean][6]** Returns **[Boolean][2]**
[1]: #cache [1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[2]: #concurrency [2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[3]: #counters [3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[4]: #simd
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number

View File

@@ -4,6 +4,95 @@
Requires libvips v8.6.1. Requires libvips v8.6.1.
#### v0.20.7 - 21<sup>st</sup> August 2018
* Use copy+unlink if rename operation fails during installation.
[#1345](https://github.com/lovell/sharp/issues/1345)
#### v0.20.6 - 20<sup>th</sup> August 2018
* Add removeAlpha operation to remove alpha channel, if any.
[#1248](https://github.com/lovell/sharp/issues/1248)
* Expose mozjpeg quant_table flag.
[#1285](https://github.com/lovell/sharp/pull/1285)
[@rexxars](https://github.com/rexxars)
* Allow full WebP alphaQuality range of 0-100.
[#1290](https://github.com/lovell/sharp/pull/1290)
[@sylvaindumont](https://github.com/sylvaindumont)
* Cache libvips binaries to reduce re-install time.
[#1301](https://github.com/lovell/sharp/issues/1301)
* Ensure vendor platform mismatch throws error at install time.
[#1303](https://github.com/lovell/sharp/issues/1303)
* Improve install time error messages for FreeBSD users.
[#1310](https://github.com/lovell/sharp/issues/1310)
* Ensure extractChannel works with 16-bit images.
[#1330](https://github.com/lovell/sharp/issues/1330)
* Expose depth option for tile-based output.
[#1342](https://github.com/lovell/sharp/pull/1342)
[@alundavies](https://github.com/alundavies)
* Add experimental entropy field to stats response.
#### v0.20.5 - 27<sup>th</sup> June 2018
* Expose libjpeg optimize_coding flag.
[#1265](https://github.com/lovell/sharp/pull/1265)
[@tomlokhorst](https://github.com/tomlokhorst)
#### v0.20.4 - 20<sup>th</sup> June 2018
* Prevent possible rounding error when using shrink-on-load and 90/270 degree rotation.
[#1241](https://github.com/lovell/sharp/issues/1241)
[@anahit42](https://github.com/anahit42)
* Ensure extractChannel sets correct single-channel colour space interpretation.
[#1257](https://github.com/lovell/sharp/issues/1257)
[@jeremychone](https://github.com/jeremychone)
#### v0.20.3 - 29<sup>th</sup> May 2018
* Fix tint operation by ensuring LAB interpretation and allowing negative values.
[#1235](https://github.com/lovell/sharp/issues/1235)
[@wezside](https://github.com/wezside)
#### v0.20.2 - 28<sup>th</sup> April 2018
* Add tint operation to set image chroma.
[#825](https://github.com/lovell/sharp/pull/825)
[@rikh42](https://github.com/rikh42)
* Add environment variable to ignore globally-installed libvips.
[#1165](https://github.com/lovell/sharp/pull/1165)
[@oncletom](https://github.com/oncletom)
* Add support for page selection with multi-page input (GIF/TIFF).
[#1204](https://github.com/lovell/sharp/pull/1204)
[@woolite64](https://github.com/woolite64)
* Add support for Group4 (CCITTFAX4) compression with TIFF output.
[#1208](https://github.com/lovell/sharp/pull/1208)
[@woolite64](https://github.com/woolite64)
#### v0.20.1 - 17<sup>th</sup> March 2018
* Improve installation experience when a globally-installed libvips below the minimum required version is found.
[#1148](https://github.com/lovell/sharp/issues/1148)
* Prevent smartcrop error when cumulative rounding is below target size.
[#1154](https://github.com/lovell/sharp/issues/1154)
[@ralrom](https://github.com/ralrom)
* Expose libvips' median filter operation.
[#1161](https://github.com/lovell/sharp/pull/1161)
[@BiancoA](https://github.com/BiancoA)
#### v0.20.0 - 5<sup>th</sup> March 2018 #### v0.20.0 - 5<sup>th</sup> March 2018
* Add support for prebuilt sharp binaries on common platforms. * Add support for prebuilt sharp binaries on common platforms.

5
docs/css/extra.css Normal file
View File

@@ -0,0 +1,5 @@
/* Nest document subheadings in navigation */
ul.subnav ul:not(.subnav) {
padding-left: 2em;
font-size: 80%;
}

View File

@@ -14,7 +14,7 @@ As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available. rotation, extraction, compositing and gamma correction are available.
Most 64-bit OS X, Windows and Linux (glibc) systems running Most 64-bit OS X, Windows and Linux (glibc) systems running
Node versions 4, 6, 8 and 9 Node versions 4, 6, 8 and 10
do not require any additional install or runtime dependencies. do not require any additional install or runtime dependencies.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master) [![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
@@ -109,6 +109,13 @@ the help and code contributions of the following people:
* [Oleh Aleinyk](https://github.com/oaleynik) * [Oleh Aleinyk](https://github.com/oaleynik)
* [Marcel Bretschneider](https://github.com/3epnm) * [Marcel Bretschneider](https://github.com/3epnm)
* [Andrea Bianco](https://github.com/BiancoA) * [Andrea Bianco](https://github.com/BiancoA)
* [Rik Heywood](https://github.com/rikh42)
* [Thomas Parisot](https://github.com/oncletom)
* [Nathan Graves](https://github.com/woolite64)
* [Tom Lokhorst](https://github.com/tomlokhorst)
* [Espen Hovlandsdal](https://github.com/rexxars)
* [Sylvain Dumont](https://github.com/sylvaindumont)
* [Alun Davies](https://github.com/alundavies)
Thank you! Thank you!

View File

@@ -15,7 +15,7 @@ yarn add sharp
### Building from source ### Building from source
Pre-compiled binaries for sharp are provided for use with Pre-compiled binaries for sharp are provided for use with
Node versions 4, 6, 8 and 9 on Node versions 4, 6, 8 and 10 on
64-bit Windows, OS X and Linux platforms. 64-bit Windows, OS X and Linux platforms.
Sharp will be built from source at install time when: Sharp will be built from source at install time when:
@@ -54,7 +54,7 @@ To use a globally-installed version of libvips instead of the provided binaries,
make sure it is at least the version listed under `config.libvips` in the `package.json` file 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`. and that it can be located using `pkg-config --modversion vips-cpp`.
If you are using non-stadard paths (anything other than `/usr` or `/usr/local`), If you are using non-standard paths (anything other than `/usr` or `/usr/local`),
you might need to set `PKG_CONFIG_PATH` during `npm install` you might need to set `PKG_CONFIG_PATH` during `npm install`
and `LD_LIBRARY_PATH` at runtime. and `LD_LIBRARY_PATH` at runtime.
@@ -161,6 +161,18 @@ Set the Lambda runtime to Node.js 6.10.
To get the best performance select the largest memory available. A 1536 MB function provides ~12x more CPU time than a 128 MB function. To get the best performance select the largest memory available. A 1536 MB function provides ~12x more CPU time than a 128 MB function.
### NW.js
Run the `nw-gyp` tool after installation.
```sh
cd node-modules/sharp
nw-gyp rebuild --arch=x64 --target=[your nw version]
node node_modules/sharp/install/dll-copy
```
See also http://docs.nwjs.io/en/latest/For%20Users/Advanced/Use%20Native%20Node%20Modules/
### Build tools ### Build tools
* [gulp-responsive](https://www.npmjs.com/package/gulp-responsive) * [gulp-responsive](https://www.npmjs.com/package/gulp-responsive)
@@ -211,10 +223,17 @@ to the directory containing the `policy.xml` file.
### Pre-compiled libvips binaries ### Pre-compiled libvips binaries
If a global installation of libvips that meets the This module will attempt to download a pre-compiled bundle of libvips
minimum version requirement cannot be found, and its dependencies on Linux and Windows machines under either of these
this module will attempt to download a pre-compiled bundle of libvips conditions:
and its dependencies on Linux and Windows machines.
1. If a global installation of libvips that meets the
minimum version requirement cannot be found;
1. If `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable is set.
```sh
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install sharp
```
Should you need to manually download and inspect these files, Should you need to manually download and inspect these files,
you can do so via https://github.com/lovell/sharp-libvips/releases you can do so via https://github.com/lovell/sharp-libvips/releases

View File

@@ -9,6 +9,7 @@ const npmLog = require('npmlog');
const semver = require('semver'); const semver = require('semver');
const simpleGet = require('simple-get'); const simpleGet = require('simple-get');
const tar = require('tar'); const tar = require('tar');
const copyFileSync = require('fs-copy-file-sync');
const agent = require('../lib/agent'); const agent = require('../lib/agent');
const libvips = require('../lib/libvips'); const libvips = require('../lib/libvips');
@@ -17,9 +18,26 @@ const platform = require('../lib/platform');
const minimumLibvipsVersion = libvips.minimumLibvipsVersion; const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
const distBaseUrl = process.env.SHARP_DIST_BASE_URL || `https://github.com/lovell/sharp-libvips/releases/download/v${minimumLibvipsVersion}/`; const distBaseUrl = process.env.SHARP_DIST_BASE_URL || `https://github.com/lovell/sharp-libvips/releases/download/v${minimumLibvipsVersion}/`;
const extractTarball = function (tarPath) {
const vendorPath = path.join(__dirname, '..', 'vendor');
if (!fs.existsSync(vendorPath)) {
fs.mkdirSync(vendorPath);
}
tar
.extract({
file: tarPath,
cwd: vendorPath,
strict: true
})
.catch(function (err) {
throw err;
});
};
try { try {
const useGlobalLibvips = libvips.useGlobalLibvips();
if (useGlobalLibvips) {
const globalLibvipsVersion = libvips.globalLibvipsVersion(); const globalLibvipsVersion = libvips.globalLibvipsVersion();
if (globalLibvipsVersion) {
npmLog.info('sharp', `Detected globally-installed libvips v${globalLibvipsVersion}`); npmLog.info('sharp', `Detected globally-installed libvips v${globalLibvipsVersion}`);
npmLog.info('sharp', 'Building from source via node-gyp'); npmLog.info('sharp', 'Building from source via node-gyp');
process.exit(1); process.exit(1);
@@ -28,8 +46,15 @@ try {
} else { } else {
// Is this arch/platform supported? // Is this arch/platform supported?
const arch = process.env.npm_config_arch || process.arch; const arch = process.env.npm_config_arch || process.arch;
const platformAndArch = platform();
if (platformAndArch === 'win32-ia32') {
throw new Error('Windows x86 (32-bit) node.exe is not supported');
}
if (arch === 'ia32') { if (arch === 'ia32') {
throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}\n`); throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
}
if (platformAndArch === 'freebsd-x64') {
throw new Error(`FreeBSD systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
} }
if (detectLibc.isNonGlibcLinux) { if (detectLibc.isNonGlibcLinux) {
throw new Error(`Use with ${detectLibc.family} libc requires manual installation of libvips >= ${minimumLibvipsVersion}`); throw new Error(`Use with ${detectLibc.family} libc requires manual installation of libvips >= ${minimumLibvipsVersion}`);
@@ -38,7 +63,12 @@ try {
throw new Error(`Use with glibc version ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`); throw new Error(`Use with glibc version ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
} }
// Download to per-process temporary file // Download to per-process temporary file
const tarFilename = ['libvips', minimumLibvipsVersion, platform()].join('-') + '.tar.gz'; const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.gz';
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
if (fs.existsSync(tarPathCache)) {
npmLog.info('sharp', `Using cached ${tarPathCache}`);
extractTarball(tarPathCache);
} else {
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`); const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
const tmpFile = fs.createWriteStream(tarPathTemp); const tmpFile = fs.createWriteStream(tarPathTemp);
const url = distBaseUrl + tarFilename; const url = distBaseUrl + tarFilename;
@@ -53,24 +83,18 @@ try {
response.pipe(tmpFile); response.pipe(tmpFile);
}); });
tmpFile.on('close', function () { tmpFile.on('close', function () {
const vendorPath = path.join(__dirname, '..', 'vendor');
fs.mkdirSync(vendorPath);
tar
.extract({
file: tarPathTemp,
cwd: vendorPath,
strict: true
})
.then(function () {
try { try {
// Attempt to rename
fs.renameSync(tarPathTemp, tarPathCache);
} catch (err) {
// Fall back to copy and unlink
copyFileSync(tarPathTemp, tarPathCache);
fs.unlinkSync(tarPathTemp); fs.unlinkSync(tarPathTemp);
} catch (err) {} }
}) extractTarball(tarPathCache);
.catch(function (err) {
throw err;
});
}); });
} }
}
} catch (err) { } catch (err) {
npmLog.error('sharp', err.message); npmLog.error('sharp', err.message);
npmLog.error('sharp', 'Please see http://sharp.pixelplumbing.com/page/install'); npmLog.error('sharp', 'Please see http://sharp.pixelplumbing.com/page/install');

View File

@@ -12,6 +12,23 @@ const bool = {
eor: 'eor' eor: 'eor'
}; };
/**
* Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
*
* @example
* sharp('rgba.png')
* .removeAlpha()
* .toFile('rgb.png', function(err, info) {
* // rgb.png is a 3 channel image without an alpha channel
* });
*
* @returns {Sharp}
*/
function removeAlpha () {
this.options.removeAlpha = true;
return this;
}
/** /**
* Extract a single channel from a multi-channel image. * Extract a single channel from a multi-channel image.
* *
@@ -102,6 +119,7 @@ function bandbool (boolOp) {
module.exports = function (Sharp) { module.exports = function (Sharp) {
// Public instance functions // Public instance functions
[ [
removeAlpha,
extractChannel, extractChannel,
joinChannel, joinChannel,
bandbool bandbool

View File

@@ -38,6 +38,21 @@ function background (rgba) {
return this; return this;
} }
/**
* 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.
*
* @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
*/
function tint (rgb) {
const colour = color(rgb);
this.options.tintA = colour.a();
this.options.tintB = colour.b();
return this;
}
/** /**
* Convert to 8-bit greyscale; 256 shades of grey. * Convert to 8-bit greyscale; 256 shades of grey.
* This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results. * This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
@@ -95,6 +110,7 @@ module.exports = function (Sharp) {
// Public instance functions // Public instance functions
[ [
background, background,
tint,
greyscale, greyscale,
grayscale, grayscale,
toColourspace, toColourspace,

View File

@@ -19,8 +19,7 @@ const is = require('./is');
* .overlayWith('overlay.png', { gravity: sharp.gravity.southeast } ) * .overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
* .sharpen() * .sharpen()
* .withMetadata() * .withMetadata()
* .quality(90) * .webp( { quality: 90 } )
* .webp()
* .toBuffer() * .toBuffer()
* .then(function(outputBuffer) { * .then(function(outputBuffer) {
* // outputBuffer contains upside down, 300px wide, alpha channel flattened * // outputBuffer contains upside down, 300px wide, alpha channel flattened

View File

@@ -81,7 +81,7 @@ const debuglog = util.debuglog('sharp');
* width: 300, * width: 300,
* height: 200, * height: 200,
* channels: 4, * channels: 4,
* background: { r: 255, g: 0, b: 0, alpha: 128 } * background: { r: 255, g: 0, b: 0, alpha: 0.5 }
* } * }
* }) * })
* .png() * .png()
@@ -97,6 +97,7 @@ const debuglog = util.debuglog('sharp');
* to decode images, even if the data is corrupt or invalid. Set this flag to true * to decode images, even if the data is corrupt or invalid. Set this flag to true
* if you'd rather halt processing and raise an error when loading invalid images. * if you'd rather halt processing and raise an error when loading invalid images.
* @param {Number} [options.density=72] - integral number representing the DPI for vector images. * @param {Number} [options.density=72] - integral number representing the DPI for vector images.
* @param {Number} [options.page=0] - page number to extract for multi-page input (GIF, TIFF)
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering. * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
* @param {Number} [options.raw.width] * @param {Number} [options.raw.width]
* @param {Number} [options.raw.height] * @param {Number} [options.raw.height]
@@ -151,8 +152,11 @@ const Sharp = function (input, options) {
fastShrinkOnLoad: true, fastShrinkOnLoad: true,
// operations // operations
background: [0, 0, 0, 255], background: [0, 0, 0, 255],
tintA: 128,
tintB: 128,
flatten: false, flatten: false,
negate: false, negate: false,
medianSize: 0,
blurSigma: 0, blurSigma: 0,
sharpenSigma: 0, sharpenSigma: 0,
sharpenFlat: 1, sharpenFlat: 1,
@@ -167,6 +171,7 @@ const Sharp = function (input, options) {
booleanFileIn: '', booleanFileIn: '',
joinChannelIn: [], joinChannelIn: [],
extractChannel: -1, extractChannel: -1,
removeAlpha: false,
colourspace: 'srgb', colourspace: 'srgb',
// overlay // overlay
overlayGravity: 0, overlayGravity: 0,
@@ -188,6 +193,8 @@ const Sharp = function (input, options) {
jpegTrellisQuantisation: false, jpegTrellisQuantisation: false,
jpegOvershootDeringing: false, jpegOvershootDeringing: false,
jpegOptimiseScans: false, jpegOptimiseScans: false,
jpegOptimiseCoding: true,
jpegQuantisationTable: 0,
pngProgressive: false, pngProgressive: false,
pngCompressionLevel: 9, pngCompressionLevel: 9,
pngAdaptiveFiltering: false, pngAdaptiveFiltering: false,

View File

@@ -57,6 +57,12 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
throw new Error('Expected width, height and channels for raw pixel input'); throw new Error('Expected width, height and channels for raw pixel input');
} }
} }
// Page input for multi-page TIFF
if (is.defined(inputOptions.page)) {
if (is.integer(inputOptions.page) && is.inRange(inputOptions.page, 0, 100000)) {
inputDescriptor.page = inputOptions.page;
}
}
// Create new image // Create new image
if (is.defined(inputOptions.create)) { if (is.defined(inputOptions.create)) {
if ( if (
@@ -258,6 +264,7 @@ function metadata (callback) {
* - `maxX` (x-coordinate of one of the pixel where the maximum lies) * - `maxX` (x-coordinate of one of the pixel where the maximum lies)
* - `maxY` (y-coordinate of one of the pixel where the maximum lies) * - `maxY` (y-coordinate of one of the pixel where the maximum lies)
* - `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel * - `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
* *
* @example * @example
* const image = sharp(inputJpg); * const image = sharp(inputJpg);

View File

@@ -1,17 +1,32 @@
'use strict'; 'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path'); const path = require('path');
const spawnSync = require('child_process').spawnSync; const spawnSync = require('child_process').spawnSync;
const semver = require('semver'); const semver = require('semver');
const platform = require('./platform'); const platform = require('./platform');
const minimumLibvipsVersion = process.env.npm_package_config_libvips || require('../package.json').config.libvips; const env = process.env;
const minimumLibvipsVersion = env.npm_package_config_libvips || require('../package.json').config.libvips;
const spawnSyncOptions = { const spawnSyncOptions = {
encoding: 'utf8', encoding: 'utf8',
shell: true shell: true
}; };
const cachePath = function () {
const npmCachePath = env.npm_config_cache || (env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
if (!fs.existsSync(npmCachePath)) {
fs.mkdirSync(npmCachePath);
}
const libvipsCachePath = path.join(npmCachePath, '_libvips');
if (!fs.existsSync(libvipsCachePath)) {
fs.mkdirSync(libvipsCachePath);
}
return libvipsCachePath;
};
const globalLibvipsVersion = function () { const globalLibvipsVersion = function () {
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout || ''; const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout || '';
@@ -23,21 +38,24 @@ const globalLibvipsVersion = function () {
const hasVendoredLibvips = function () { const hasVendoredLibvips = function () {
const currentPlatformId = platform(); const currentPlatformId = platform();
let vendorPlatformId;
try { try {
const vendorPlatformId = require(path.join(__dirname, '..', 'vendor', 'platform.json')); vendorPlatformId = require(path.join(__dirname, '..', 'vendor', 'platform.json'));
} catch (err) {}
if (vendorPlatformId) {
if (currentPlatformId === vendorPlatformId) { if (currentPlatformId === vendorPlatformId) {
return true; return true;
} else { } else {
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`); throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
} }
} catch (err) {} }
return false; return false;
}; };
const pkgConfigPath = function () { const pkgConfigPath = function () {
if (process.platform !== 'win32') { 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 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
return [brewPkgConfigPath.trim(), process.env.PKG_CONFIG_PATH, '/usr/local/lib/pkgconfig', '/usr/lib/pkgconfig'] return [brewPkgConfigPath.trim(), env.PKG_CONFIG_PATH, '/usr/local/lib/pkgconfig', '/usr/lib/pkgconfig']
.filter(function (p) { return !!p; }) .filter(function (p) { return !!p; })
.join(':'); .join(':');
} else { } else {
@@ -46,12 +64,17 @@ const pkgConfigPath = function () {
}; };
const useGlobalLibvips = function () { const useGlobalLibvips = function () {
if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
return false;
}
const globalVipsVersion = globalLibvipsVersion(); const globalVipsVersion = globalLibvipsVersion();
return !!globalVipsVersion && semver.gte(globalVipsVersion, minimumLibvipsVersion); return !!globalVipsVersion && semver.gte(globalVipsVersion, minimumLibvipsVersion);
}; };
module.exports = { module.exports = {
minimumLibvipsVersion: minimumLibvipsVersion, minimumLibvipsVersion: minimumLibvipsVersion,
cachePath: cachePath,
globalLibvipsVersion: globalLibvipsVersion, globalLibvipsVersion: globalLibvipsVersion,
hasVendoredLibvips: hasVendoredLibvips, hasVendoredLibvips: hasVendoredLibvips,
pkgConfigPath: pkgConfigPath, pkgConfigPath: pkgConfigPath,

View File

@@ -156,6 +156,26 @@ function sharpen (sigma, flat, jagged) {
return this; return this;
} }
/**
* Apply median filter.
* When used without parameters the default window is 3x3.
* @param {Number} [size=3] square mask size: size x size
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function median (size) {
if (!is.defined(size)) {
// No arguments: default to 3x3
this.options.medianSize = 3;
} else if (is.integer(size) && is.inRange(size, 1, 1000)) {
// Numeric argument: specific sigma
this.options.medianSize = size;
} else {
throw new Error('Invalid median size ' + size);
}
return this;
}
/** /**
* Blur the image. * Blur the image.
* When used without parameters, performs a fast, mild blur of the output image. * When used without parameters, performs a fast, mild blur of the output image.
@@ -444,6 +464,7 @@ module.exports = function (Sharp) {
flip, flip,
flop, flop,
sharpen, sharpen,
median,
blur, blur,
extend, extend,
flatten, flatten,

View File

@@ -12,6 +12,16 @@ const sharp = require('../build/Release/sharp.node');
* *
* A `Promise` is returned when `callback` is not provided. * A `Promise` is returned when `callback` is not provided.
* *
* @example
* sharp(input)
* .toFile('output.png', (err, info) => { ... });
*
* @example
* sharp(input)
* .toFile('output.png')
* .then(info => { ... })
* .catch(err => { ... });
*
* @param {String} fileOut - the path to write the image data to. * @param {String} fileOut - the path to write the image data to.
* @param {Function} [callback] - called on completion with two arguments `(err, info)`. * @param {Function} [callback] - called on completion with two arguments `(err, info)`.
* `info` contains the output image `format`, `size` (bytes), `width`, `height`, * `info` contains the output image `format`, `size` (bytes), `width`, `height`,
@@ -58,6 +68,22 @@ function toFile (fileOut, callback) {
* *
* A `Promise` is returned when `callback` is not provided. * A `Promise` is returned when `callback` is not provided.
* *
* @example
* sharp(input)
* .toBuffer((err, data, info) => { ... });
*
* @example
* sharp(input)
* .toBuffer()
* .then(data => { ... })
* .catch(err => { ... });
*
* @example
* sharp(input)
* .toBuffer({ resolveWithObject: true })
* .then(({ data, info }) => { ... })
* .catch(err => { ... });
*
* @param {Object} [options] * @param {Object} [options]
* @param {Boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`. * @param {Boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
* @param {Function} [callback] * @param {Function} [callback]
@@ -76,6 +102,13 @@ function toBuffer (options, callback) {
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image. * Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
* The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space. * The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
* This will also convert to and add a web-friendly sRGB ICC profile. * This will also convert to and add a web-friendly sRGB ICC profile.
*
* @example
* sharp('input.jpg')
* .withMetadata()
* .toFile('output-with-metadata.jpg')
* .then(info => { ... });
*
* @param {Object} [withMetadata] * @param {Object} [withMetadata]
* @param {Number} [withMetadata.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag. * @param {Number} [withMetadata.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
* @returns {Sharp} * @returns {Sharp}
@@ -97,6 +130,16 @@ function withMetadata (withMetadata) {
/** /**
* Use these JPEG options for output image. * Use these JPEG options for output image.
*
* @example
* // Convert any input to very high quality JPEG output
* const data = await sharp(input)
* .jpeg({
* quality: 100,
* chromaSubsampling: '4:4:4'
* })
* .toBuffer();
*
* @param {Object} [options] - output options * @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100 * @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan * @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
@@ -105,6 +148,10 @@ function withMetadata (withMetadata) {
* @param {Boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires mozjpeg * @param {Boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires mozjpeg
* @param {Boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires mozjpeg * @param {Boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires mozjpeg
* @param {Boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans * @param {Boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans
* @param {Boolean} [options.optimiseCoding=true] - optimise Huffman coding tables
* @param {Boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
* @param {Number} [options.quantisationTable=0] - quantization table to use, integer 0-8, requires mozjpeg
* @param {Number} [options.quantizationTable=0] - alternative spelling of quantisationTable
* @param {Boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format * @param {Boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid options * @throws {Error} Invalid options
@@ -142,12 +189,34 @@ function jpeg (options) {
this.options.jpegProgressive = true; this.options.jpegProgressive = true;
} }
} }
options.optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
if (is.defined(options.optimiseCoding)) {
this._setBooleanOption('jpegOptimiseCoding', options.optimiseCoding);
}
options.quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
if (is.defined(options.quantisationTable)) {
if (is.integer(options.quantisationTable) && is.inRange(options.quantisationTable, 0, 8)) {
this.options.jpegQuantisationTable = options.quantisationTable;
} else {
throw new Error('Invalid quantisation table (integer, 0-8) ' + options.quantisationTable);
}
}
} }
return this._updateFormatOut('jpeg', options); return this._updateFormatOut('jpeg', options);
} }
/** /**
* Use these PNG options for output image. * Use these PNG options for output image.
*
* PNG output is always full colour at 8 or 16 bits per pixel.
* Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
*
* @example
* // Convert any input to PNG output
* const data = await sharp(input)
* .png()
* .toBuffer();
*
* @param {Object} [options] * @param {Object} [options]
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan * @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9 * @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9
@@ -177,6 +246,13 @@ function png (options) {
/** /**
* Use these WebP options for output image. * Use these WebP options for output image.
*
* @example
* // Convert any input to lossless WebP output
* const data = await sharp(input)
* .webp({ lossless: true })
* .toBuffer();
*
* @param {Object} [options] - output options * @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100 * @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100 * @param {Number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
@@ -195,10 +271,10 @@ function webp (options) {
} }
} }
if (is.object(options) && is.defined(options.alphaQuality)) { if (is.object(options) && is.defined(options.alphaQuality)) {
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 1, 100)) { if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
this.options.webpAlphaQuality = options.alphaQuality; this.options.webpAlphaQuality = options.alphaQuality;
} else { } else {
throw new Error('Invalid webp alpha quality (integer, 1-100) ' + options.alphaQuality); throw new Error('Invalid webp alpha quality (integer, 0-100) ' + options.alphaQuality);
} }
} }
if (is.object(options) && is.defined(options.lossless)) { if (is.object(options) && is.defined(options.lossless)) {
@@ -212,10 +288,21 @@ function webp (options) {
/** /**
* Use these TIFF options for output image. * Use these TIFF options for output image.
*
* @example
* // Convert SVG input to LZW-compressed, 1 bit per pixel TIFF output
* sharp('input.svg')
* .tiff({
* compression: 'lzw',
* squash: true
* })
* .toFile('1-bpp-output.tiff')
* .then(info => { ... });
*
* @param {Object} [options] - output options * @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100 * @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format * @param {Boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
* @param {Boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg * @param {Boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
* @param {Boolean} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float * @param {Boolean} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
* @param {Number} [options.xres=1.0] - horizontal resolution in pixels/mm * @param {Number} [options.xres=1.0] - horizontal resolution in pixels/mm
* @param {Number} [options.yres=1.0] - vertical resolution in pixels/mm * @param {Number} [options.yres=1.0] - vertical resolution in pixels/mm
@@ -255,10 +342,10 @@ function tiff (options) {
} }
// compression // compression
if (is.defined(options) && is.defined(options.compression)) { if (is.defined(options) && is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'none'])) { if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
this.options.tiffCompression = options.compression; this.options.tiffCompression = options.compression;
} else { } else {
const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, none`; const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, ccittfax4, none`;
throw new Error(message); throw new Error(message);
} }
} }
@@ -276,6 +363,13 @@ function tiff (options) {
/** /**
* Force output to be raw, uncompressed uint8 pixel data. * Force output to be raw, uncompressed uint8 pixel data.
*
* @example
* // Extract raw RGB pixel data from JPEG input
* const { data, info } = await sharp('input.jpg')
* .raw()
* .toBuffer({ resolveWithObject: true });
*
* @returns {Sharp} * @returns {Sharp}
*/ */
function raw () { function raw () {
@@ -284,6 +378,13 @@ function raw () {
/** /**
* Force output to a given format. * Force output to a given format.
*
* @example
* // Convert any input to PNG output
* const data = await sharp(input)
* .toFormat('png')
* .toBuffer();
*
* @param {(String|Object)} format - as a String or an Object with an 'id' attribute * @param {(String|Object)} format - as a String or an Object with an 'id' attribute
* @param {Object} options - output options * @param {Object} options - output options
* @returns {Sharp} * @returns {Sharp}
@@ -305,6 +406,8 @@ function toFormat (format, options) {
* Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions. * Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
* Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format. * Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
* *
* Warning: multiple sharp instances concurrently producing tile output can expose a possible race condition in some versions of libgsf.
*
* @example * @example
* sharp('input.tiff') * sharp('input.tiff')
* .png() * .png()
@@ -320,6 +423,7 @@ function toFormat (format, options) {
* @param {Number} [tile.size=256] tile size in pixels, a value between 1 and 8192. * @param {Number} [tile.size=256] tile size in pixels, a value between 1 and 8192.
* @param {Number} [tile.overlap=0] tile overlap in pixels, a value between 0 and 8192. * @param {Number} [tile.overlap=0] tile overlap in pixels, a value between 0 and 8192.
* @param {Number} [tile.angle=0] tile angle of rotation, must be a multiple of 90. * @param {Number} [tile.angle=0] tile angle of rotation, must be a multiple of 90.
* @param {String} [tile.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
* @param {String} [tile.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file). * @param {String} [tile.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
* @param {String} [tile.layout='dz'] filesystem layout, possible values are `dz`, `zoomify` or `google`. * @param {String} [tile.layout='dz'] filesystem layout, possible values are `dz`, `zoomify` or `google`.
* @returns {Sharp} * @returns {Sharp}
@@ -371,6 +475,15 @@ function tile (tile) {
throw new Error('Unsupported angle: angle must be a positive/negative multiple of 90 ' + tile.angle); throw new Error('Unsupported angle: angle must be a positive/negative multiple of 90 ' + tile.angle);
} }
} }
// Depth of tiles
if (is.defined(tile.depth)) {
if (is.string(tile.depth) && is.inArray(tile.depth, ['onepixel', 'onetile', 'one'])) {
this.options.tileDepth = tile.depth;
} else {
throw new Error("Invalid tile depth '" + tile.depth + "', should be one of 'onepixel', 'onetile' or 'one'");
}
}
} }
// Format // Format
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) { if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {

View File

@@ -5,6 +5,8 @@ site_description: High performance Node.js image processing, the fastest module
copyright: <a href="https://pixelplumbing.com/">pixelplumbing.com</a> copyright: <a href="https://pixelplumbing.com/">pixelplumbing.com</a>
google_analytics: ['UA-13034748-12', 'sharp.pixelplumbing.com'] google_analytics: ['UA-13034748-12', 'sharp.pixelplumbing.com']
theme: readthedocs theme: readthedocs
extra_css:
- css/extra.css
markdown_extensions: markdown_extensions:
- toc: - toc:
permalink: True permalink: True

View File

@@ -1,7 +1,7 @@
{ {
"name": "sharp", "name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.20.0", "version": "0.20.7",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp", "homepage": "https://github.com/lovell/sharp",
"contributors": [ "contributors": [
@@ -45,7 +45,14 @@
"Kenric D'Souza <kenric.dsouza@gmail.com>", "Kenric D'Souza <kenric.dsouza@gmail.com>",
"Oleh Aleinyk <oleg.aleynik@gmail.com>", "Oleh Aleinyk <oleg.aleynik@gmail.com>",
"Marcel Bretschneider <marcel.bretschneider@gmail.com>", "Marcel Bretschneider <marcel.bretschneider@gmail.com>",
"Andrea Bianco <andrea.bianco@unibas.ch>" "Andrea Bianco <andrea.bianco@unibas.ch>",
"Rik Heywood <rik@rik.org>",
"Thomas Parisot <hi@oncletom.io>",
"Nathan Graves <nathanrgraves+github@gmail.com>",
"Tom Lokhorst <tom@lokhorst.eu>",
"Espen Hovlandsdal <espen@hovlandsdal.com>",
"Sylvain Dumont <sylvain.dumont35@gmail.com>",
"Alun Davies <alun.owain.davies@googlemail.com>"
], ],
"scripts": { "scripts": {
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)", "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
@@ -53,7 +60,7 @@
"test": "semistandard && cc && nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js && prebuild-ci", "test": "semistandard && cc && nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js && prebuild-ci",
"coverage": "./test/coverage/report.sh", "coverage": "./test/coverage/report.sh",
"test-leak": "./test/leak/leak.sh", "test-leak": "./test/leak/leak.sh",
"docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md lib/$m.js >docs/api-$m.md; done" "docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done"
}, },
"main": "lib/index.js", "main": "lib/index.js",
"repository": { "repository": {
@@ -79,28 +86,28 @@
"dependencies": { "dependencies": {
"color": "^3.0.0", "color": "^3.0.0",
"detect-libc": "^1.0.3", "detect-libc": "^1.0.3",
"nan": "^2.9.2", "nan": "^2.10.0",
"fs-copy-file-sync": "^1.0.1", "fs-copy-file-sync": "^1.1.1",
"npmlog": "^4.1.2", "npmlog": "^4.1.2",
"prebuild-install": "^2.5.1", "prebuild-install": "^4.0.0",
"semver": "^5.5.0", "semver": "^5.5.1",
"simple-get": "^2.7.0", "simple-get": "^2.8.1",
"tar": "^4.4.0", "tar": "^4.4.6",
"tunnel-agent": "^0.6.0" "tunnel-agent": "^0.6.0"
}, },
"devDependencies": { "devDependencies": {
"async": "^2.6.0", "async": "^2.6.1",
"cc": "^1.0.1", "cc": "^1.0.2",
"documentation": "^6.0.0", "decompress-zip": "^0.3.1",
"documentation": "^8.1.1",
"exif-reader": "^1.0.2", "exif-reader": "^1.0.2",
"icc": "^1.0.0", "icc": "^1.0.0",
"mocha": "^5.0.1", "mocha": "^5.2.0",
"nyc": "^11.5.0", "nyc": "^12.0.2",
"prebuild": "^7.4.0", "prebuild": "^7.6.2",
"prebuild-ci": "^2.2.3", "prebuild-ci": "^2.2.3",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",
"semistandard": "^12.0.1", "semistandard": "^12.0.1"
"unzip": "^0.1.11"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"config": { "config": {

View File

@@ -63,6 +63,10 @@ namespace sharp {
descriptor->rawWidth = AttrTo<uint32_t>(input, "rawWidth"); descriptor->rawWidth = AttrTo<uint32_t>(input, "rawWidth");
descriptor->rawHeight = AttrTo<uint32_t>(input, "rawHeight"); descriptor->rawHeight = AttrTo<uint32_t>(input, "rawHeight");
} }
// Page input for multi-page TIFF
if (HasAttr(input, "page")) {
descriptor->page = AttrTo<uint32_t>(input, "page");
}
// Create new image // Create new image
if (HasAttr(input, "createChannels")) { if (HasAttr(input, "createChannels")) {
descriptor->createChannels = AttrTo<uint32_t>(input, "createChannels"); descriptor->createChannels = AttrTo<uint32_t>(input, "createChannels");
@@ -229,6 +233,9 @@ namespace sharp {
if (imageType == ImageType::MAGICK) { if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data()); option->set("density", std::to_string(descriptor->density).data());
} }
if (imageType == ImageType::TIFF) {
option->set("page", descriptor->page);
}
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option); image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) { if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density); SetDensity(image, descriptor->density);
@@ -268,6 +275,9 @@ namespace sharp {
if (imageType == ImageType::MAGICK) { if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data()); option->set("density", std::to_string(descriptor->density).data());
} }
if (imageType == ImageType::TIFF) {
option->set("page", descriptor->page);
}
image = VImage::new_from_file(descriptor->file.data(), option); image = VImage::new_from_file(descriptor->file.data(), option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) { if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density); SetDensity(image, descriptor->density);

View File

@@ -53,6 +53,7 @@ namespace sharp {
int rawChannels; int rawChannels;
int rawWidth; int rawWidth;
int rawHeight; int rawHeight;
int page;
int createChannels; int createChannels;
int createWidth; int createWidth;
int createHeight; int createHeight;
@@ -66,6 +67,7 @@ namespace sharp {
rawChannels(0), rawChannels(0),
rawWidth(0), rawWidth(0),
rawHeight(0), rawHeight(0),
page(0),
createChannels(0), createChannels(0),
createWidth(0), createWidth(0),
createHeight(0) { createHeight(0) {

View File

@@ -83,9 +83,9 @@ class MetadataWorker : public Nan::AsyncWorker {
baton->iccLength = iccLength; baton->iccLength = iccLength;
} }
// IPTC // IPTC
if (image.get_typeof(VIPS_META_IPCT_NAME) == VIPS_TYPE_BLOB) { if (image.get_typeof(VIPS_META_IPTC_NAME) == VIPS_TYPE_BLOB) {
size_t iptcLength; size_t iptcLength;
void const *iptc = image.get_blob(VIPS_META_IPCT_NAME, &iptcLength); void const *iptc = image.get_blob(VIPS_META_IPTC_NAME, &iptcLength);
baton->iptc = static_cast<char *>(g_malloc(iptcLength)); baton->iptc = static_cast<char *>(g_malloc(iptcLength));
memcpy(baton->iptc, iptc, iptcLength); memcpy(baton->iptc, iptc, iptcLength);
baton->iptcLength = iptcLength; baton->iptcLength = iptcLength;

View File

@@ -28,6 +28,16 @@ using vips::VError;
namespace sharp { namespace sharp {
/*
Removes alpha channel, if any.
*/
VImage RemoveAlpha(VImage image) {
if (HasAlpha(image)) {
image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));
}
return image;
}
/* /*
Composite overlayImage over image at given position Composite overlayImage over image at given position
Assumes alpha channels are already premultiplied and will be unpremultiplied after Assumes alpha channels are already premultiplied and will be unpremultiplied after
@@ -152,6 +162,33 @@ namespace sharp {
return dst.bandjoin(mask.cast(dst.format())); return dst.bandjoin(mask.cast(dst.format()));
} }
/*
* Tint an image using the specified chroma, preserving the original image luminance
*/
VImage Tint(VImage image, double const a, double const b) {
// Get original colourspace
VipsInterpretation typeBeforeTint = image.interpretation();
if (typeBeforeTint == VIPS_INTERPRETATION_RGB) {
typeBeforeTint = VIPS_INTERPRETATION_sRGB;
}
// Extract luminance
VImage luminance = image.colourspace(VIPS_INTERPRETATION_LAB)[0];
// Create the tinted version by combining the L from the original and the chroma from the tint
std::vector<double> chroma {a, b};
VImage tinted = luminance
.bandjoin(chroma)
.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_LAB))
.colourspace(typeBeforeTint);
// Attach original alpha channel, if any
if (HasAlpha(image)) {
// Extract original alpha channel
VImage alpha = image[image.bands() - 1];
// Join alpha channel to normalised image
tinted = tinted.bandjoin(alpha);
}
return tinted;
}
/* /*
* Stretch luminance to cover full dynamic range. * Stretch luminance to cover full dynamic range.
*/ */
@@ -196,10 +233,8 @@ namespace sharp {
VImage Gamma(VImage image, double const exponent) { VImage Gamma(VImage image, double const exponent) {
if (HasAlpha(image)) { if (HasAlpha(image)) {
// Separate alpha channel // Separate alpha channel
VImage imageWithoutAlpha = image.extract_band(0,
VImage::option()->set("n", image.bands() - 1));
VImage alpha = image[image.bands() - 1]; VImage alpha = image[image.bands() - 1];
return imageWithoutAlpha.gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha); return RemoveAlpha(image).gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
} else { } else {
return image.gamma(VImage::option()->set("exponent", exponent)); return image.gamma(VImage::option()->set("exponent", exponent));
} }
@@ -347,10 +382,8 @@ namespace sharp {
VImage Linear(VImage image, double const a, double const b) { VImage Linear(VImage image, double const a, double const b) {
if (HasAlpha(image)) { if (HasAlpha(image)) {
// Separate alpha channel // Separate alpha channel
VImage imageWithoutAlpha = image.extract_band(0,
VImage::option()->set("n", image.bands() - 1));
VImage alpha = image[image.bands() - 1]; VImage alpha = image[image.bands() - 1];
return imageWithoutAlpha.linear(a, b).bandjoin(alpha); return RemoveAlpha(image).linear(a, b).bandjoin(alpha);
} else { } else {
return image.linear(a, b); return image.linear(a, b);
} }

View File

@@ -25,6 +25,11 @@ using vips::VImage;
namespace sharp { namespace sharp {
/*
Removes alpha channel, if any.
*/
VImage RemoveAlpha(VImage image);
/* /*
Alpha composite src over dst with given gravity. Alpha composite src over dst with given gravity.
Assumes alpha channels are already premultiplied and will be unpremultiplied after. Assumes alpha channels are already premultiplied and will be unpremultiplied after.
@@ -46,6 +51,11 @@ namespace sharp {
*/ */
VImage Cutout(VImage src, VImage dst, const int gravity); VImage Cutout(VImage src, VImage dst, const int gravity);
/*
* Tint an image using the specified chroma, preserving the original image luminance
*/
VImage Tint(VImage image, double const a, double const b);
/* /*
* Stretch luminance to cover full dynamic range. * Stretch luminance to cover full dynamic range.
*/ */

View File

@@ -281,16 +281,18 @@ class PipelineWorker : public Nan::AsyncWorker {
} }
} }
// Recalculate integral shrink and double residual // Recalculate integral shrink and double residual
int shrunkOnLoadWidth = image.width(); int const shrunkOnLoadWidth = image.width();
int shrunkOnLoadHeight = image.height(); int const shrunkOnLoadHeight = image.height();
if (!baton->rotateBeforePreExtract && if (!baton->rotateBeforePreExtract &&
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270)) { (rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270)) {
// Swap input output width and height when rotating by 90 or 270 degrees // Swap when rotating by 90 or 270 degrees
std::swap(shrunkOnLoadWidth, shrunkOnLoadHeight); xfactor = static_cast<double>(shrunkOnLoadWidth) / static_cast<double>(targetResizeHeight);
} yfactor = static_cast<double>(shrunkOnLoadHeight) / static_cast<double>(targetResizeWidth);
} else {
xfactor = static_cast<double>(shrunkOnLoadWidth) / static_cast<double>(targetResizeWidth); xfactor = static_cast<double>(shrunkOnLoadWidth) / static_cast<double>(targetResizeWidth);
yfactor = static_cast<double>(shrunkOnLoadHeight) / static_cast<double>(targetResizeHeight); yfactor = static_cast<double>(shrunkOnLoadHeight) / static_cast<double>(targetResizeHeight);
} }
}
// Ensure we're using a device-independent colour space // Ensure we're using a device-independent colour space
if (sharp::HasProfile(image)) { if (sharp::HasProfile(image)) {
@@ -359,6 +361,8 @@ class PipelineWorker : public Nan::AsyncWorker {
bool const shouldBlur = baton->blurSigma != 0.0; bool const shouldBlur = baton->blurSigma != 0.0;
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0; bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
bool const shouldSharpen = baton->sharpenSigma != 0.0; bool const shouldSharpen = baton->sharpenSigma != 0.0;
bool const shouldApplyMedian = baton->medianSize > 0;
bool const shouldPremultiplyAlpha = HasAlpha(image) && bool const shouldPremultiplyAlpha = HasAlpha(image) &&
(shouldResize || shouldBlur || shouldConv || shouldSharpen || shouldOverlayWithAlpha); (shouldResize || shouldBlur || shouldConv || shouldSharpen || shouldOverlayWithAlpha);
@@ -379,7 +383,6 @@ class PipelineWorker : public Nan::AsyncWorker {
) { ) {
throw vips::VError("Unknown kernel"); throw vips::VError("Unknown kernel");
} }
image = image.resize(1.0 / xfactor, VImage::option() image = image.resize(1.0 / xfactor, VImage::option()
->set("vscale", 1.0 / yfactor) ->set("vscale", 1.0 / yfactor)
->set("kernel", kernel)); ->set("kernel", kernel));
@@ -482,6 +485,12 @@ class PipelineWorker : public Nan::AsyncWorker {
image = image.extract_area(left, top, width, height); image = image.extract_area(left, top, width, height);
} else { } else {
// Attention-based or Entropy-based crop // Attention-based or Entropy-based crop
if (baton->width > image.width()) {
baton->width = image.width();
}
if (baton->height > image.height()) {
baton->height = image.height();
}
image = image.tilecache(VImage::option() image = image.tilecache(VImage::option()
->set("access", baton->accessMethod) ->set("access", baton->accessMethod)
->set("threaded", TRUE)); ->set("threaded", TRUE));
@@ -538,7 +547,10 @@ class PipelineWorker : public Nan::AsyncWorker {
image = image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height, image = image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
VImage::option()->set("extend", VIPS_EXTEND_BACKGROUND)->set("background", background)); VImage::option()->set("extend", VIPS_EXTEND_BACKGROUND)->set("background", background));
} }
// Median - must happen before blurring, due to the utility of blurring after thresholding
if (shouldApplyMedian) {
image = image.median(baton->medianSize);
}
// Threshold - must happen before blurring, due to the utility of blurring after thresholding // Threshold - must happen before blurring, due to the utility of blurring after thresholding
if (baton->threshold != 0) { if (baton->threshold != 0) {
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale); image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
@@ -671,14 +683,30 @@ class PipelineWorker : public Nan::AsyncWorker {
image = sharp::Bandbool(image, baton->bandBoolOp); image = sharp::Bandbool(image, baton->bandBoolOp);
} }
// Tint the image
if (baton->tintA < 128.0 || baton->tintB < 128.0) {
image = sharp::Tint(image, baton->tintA, baton->tintB);
}
// Extract an image channel (aka vips band) // Extract an image channel (aka vips band)
if (baton->extractChannel > -1) { if (baton->extractChannel > -1) {
if (baton->extractChannel >= image.bands()) { if (baton->extractChannel >= image.bands()) {
(baton->err).append("Cannot extract channel from image. Too few channels in image."); (baton->err).append("Cannot extract channel from image. Too few channels in image.");
return Error(); return Error();
} }
image = image.extract_band(baton->extractChannel); VipsInterpretation const interpretation = sharp::Is16Bit(image.interpretation())
? VIPS_INTERPRETATION_GREY16
: VIPS_INTERPRETATION_B_W;
image = image
.extract_band(baton->extractChannel)
.copy(VImage::option()->set("interpretation", interpretation));
} }
// Remove alpha channel, if any
if (baton->removeAlpha) {
image = sharp::RemoveAlpha(image);
}
// Convert image to sRGB, if not already // Convert image to sRGB, if not already
if (sharp::Is16Bit(image.interpretation())) { if (sharp::Is16Bit(image.interpretation())) {
image = image.cast(VIPS_FORMAT_USHORT); image = image.cast(VIPS_FORMAT_USHORT);
@@ -714,9 +742,10 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("interlace", baton->jpegProgressive) ->set("interlace", baton->jpegProgressive)
->set("no_subsample", baton->jpegChromaSubsampling == "4:4:4") ->set("no_subsample", baton->jpegChromaSubsampling == "4:4:4")
->set("trellis_quant", baton->jpegTrellisQuantisation) ->set("trellis_quant", baton->jpegTrellisQuantisation)
->set("quant_table", baton->jpegQuantisationTable)
->set("overshoot_deringing", baton->jpegOvershootDeringing) ->set("overshoot_deringing", baton->jpegOvershootDeringing)
->set("optimize_scans", baton->jpegOptimiseScans) ->set("optimize_scans", baton->jpegOptimiseScans)
->set("optimize_coding", TRUE))); ->set("optimize_coding", baton->jpegOptimiseCoding)));
baton->bufferOut = static_cast<char*>(area->data); baton->bufferOut = static_cast<char*>(area->data);
baton->bufferOutLength = area->length; baton->bufferOutLength = area->length;
area->free_fn = nullptr; area->free_fn = nullptr;
@@ -829,9 +858,10 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("interlace", baton->jpegProgressive) ->set("interlace", baton->jpegProgressive)
->set("no_subsample", baton->jpegChromaSubsampling == "4:4:4") ->set("no_subsample", baton->jpegChromaSubsampling == "4:4:4")
->set("trellis_quant", baton->jpegTrellisQuantisation) ->set("trellis_quant", baton->jpegTrellisQuantisation)
->set("quant_table", baton->jpegQuantisationTable)
->set("overshoot_deringing", baton->jpegOvershootDeringing) ->set("overshoot_deringing", baton->jpegOvershootDeringing)
->set("optimize_scans", baton->jpegOptimiseScans) ->set("optimize_scans", baton->jpegOptimiseScans)
->set("optimize_coding", TRUE)); ->set("optimize_coding", baton->jpegOptimiseCoding));
baton->formatOut = "jpeg"; baton->formatOut = "jpeg";
baton->channels = std::min(baton->channels, 3); baton->channels = std::min(baton->channels, 3);
} else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput && } else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
@@ -908,21 +938,30 @@ class PipelineWorker : public Nan::AsyncWorker {
{"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"}, {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
{"no_subsample", baton->jpegChromaSubsampling == "4:4:4" ? "TRUE": "FALSE"}, {"no_subsample", baton->jpegChromaSubsampling == "4:4:4" ? "TRUE": "FALSE"},
{"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"}, {"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
{"quant_table", std::to_string(baton->jpegQuantisationTable)},
{"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"}, {"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
{"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"}, {"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
{"optimize_coding", "TRUE"} {"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
}; };
suffix = AssembleSuffixString(extname, options); suffix = AssembleSuffixString(extname, options);
} }
// Write DZ to file // Write DZ to file
image.dzsave(const_cast<char*>(baton->fileOut.data()), VImage::option() vips::VOption *options = VImage::option()
->set("strip", !baton->withMetadata) ->set("strip", !baton->withMetadata)
->set("tile_size", baton->tileSize) ->set("tile_size", baton->tileSize)
->set("overlap", baton->tileOverlap) ->set("overlap", baton->tileOverlap)
->set("container", baton->tileContainer) ->set("container", baton->tileContainer)
->set("layout", baton->tileLayout) ->set("layout", baton->tileLayout)
->set("suffix", const_cast<char*>(suffix.data())) ->set("suffix", const_cast<char*>(suffix.data()))
->set("angle", CalculateAngleRotation(baton->tileAngle))); ->set("angle", CalculateAngleRotation(baton->tileAngle));
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
// not passing anything - libvips will handle choice
if (baton->tileDepth < VIPS_FOREIGN_DZ_DEPTH_LAST) {
options->set("depth", baton->tileDepth);
}
image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
baton->formatOut = "dz"; baton->formatOut = "dz";
} else if (baton->formatOut == "v" || (mightMatchInput && isV) || } else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
(willMatchInput && inputImageType == ImageType::VIPS)) { (willMatchInput && inputImageType == ImageType::VIPS)) {
@@ -1156,6 +1195,9 @@ NAN_METHOD(pipeline) {
for (unsigned int i = 0; i < 4; i++) { for (unsigned int i = 0; i < 4; i++) {
baton->background[i] = AttrTo<double>(background, i); baton->background[i] = AttrTo<double>(background, i);
} }
// Tint chroma
baton->tintA = AttrTo<double>(options, "tintA");
baton->tintB = AttrTo<double>(options, "tintB");
// Overlay options // Overlay options
if (HasAttr(options, "overlay")) { if (HasAttr(options, "overlay")) {
baton->overlay = CreateInputDescriptor(AttrAs<v8::Object>(options, "overlay"), buffersToPersist); baton->overlay = CreateInputDescriptor(AttrAs<v8::Object>(options, "overlay"), buffersToPersist);
@@ -1188,6 +1230,7 @@ NAN_METHOD(pipeline) {
baton->flatten = AttrTo<bool>(options, "flatten"); baton->flatten = AttrTo<bool>(options, "flatten");
baton->negate = AttrTo<bool>(options, "negate"); baton->negate = AttrTo<bool>(options, "negate");
baton->blurSigma = AttrTo<double>(options, "blurSigma"); baton->blurSigma = AttrTo<double>(options, "blurSigma");
baton->medianSize = AttrTo<uint32_t>(options, "medianSize");
baton->sharpenSigma = AttrTo<double>(options, "sharpenSigma"); baton->sharpenSigma = AttrTo<double>(options, "sharpenSigma");
baton->sharpenFlat = AttrTo<double>(options, "sharpenFlat"); baton->sharpenFlat = AttrTo<double>(options, "sharpenFlat");
baton->sharpenJagged = AttrTo<double>(options, "sharpenJagged"); baton->sharpenJagged = AttrTo<double>(options, "sharpenJagged");
@@ -1209,6 +1252,7 @@ NAN_METHOD(pipeline) {
baton->extendLeft = AttrTo<int32_t>(options, "extendLeft"); baton->extendLeft = AttrTo<int32_t>(options, "extendLeft");
baton->extendRight = AttrTo<int32_t>(options, "extendRight"); baton->extendRight = AttrTo<int32_t>(options, "extendRight");
baton->extractChannel = AttrTo<int32_t>(options, "extractChannel"); baton->extractChannel = AttrTo<int32_t>(options, "extractChannel");
baton->removeAlpha = AttrTo<bool>(options, "removeAlpha");
if (HasAttr(options, "boolean")) { if (HasAttr(options, "boolean")) {
baton->boolean = CreateInputDescriptor(AttrAs<v8::Object>(options, "boolean"), buffersToPersist); baton->boolean = CreateInputDescriptor(AttrAs<v8::Object>(options, "boolean"), buffersToPersist);
baton->booleanOp = sharp::GetBooleanOperation(AttrAsStr(options, "booleanOp")); baton->booleanOp = sharp::GetBooleanOperation(AttrAsStr(options, "booleanOp"));
@@ -1243,8 +1287,10 @@ NAN_METHOD(pipeline) {
baton->jpegProgressive = AttrTo<bool>(options, "jpegProgressive"); baton->jpegProgressive = AttrTo<bool>(options, "jpegProgressive");
baton->jpegChromaSubsampling = AttrAsStr(options, "jpegChromaSubsampling"); baton->jpegChromaSubsampling = AttrAsStr(options, "jpegChromaSubsampling");
baton->jpegTrellisQuantisation = AttrTo<bool>(options, "jpegTrellisQuantisation"); baton->jpegTrellisQuantisation = AttrTo<bool>(options, "jpegTrellisQuantisation");
baton->jpegQuantisationTable = AttrTo<uint32_t>(options, "jpegQuantisationTable");
baton->jpegOvershootDeringing = AttrTo<bool>(options, "jpegOvershootDeringing"); baton->jpegOvershootDeringing = AttrTo<bool>(options, "jpegOvershootDeringing");
baton->jpegOptimiseScans = AttrTo<bool>(options, "jpegOptimiseScans"); baton->jpegOptimiseScans = AttrTo<bool>(options, "jpegOptimiseScans");
baton->jpegOptimiseCoding = AttrTo<bool>(options, "jpegOptimiseCoding");
baton->pngProgressive = AttrTo<bool>(options, "pngProgressive"); baton->pngProgressive = AttrTo<bool>(options, "pngProgressive");
baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel"); baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering"); baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering");
@@ -1283,6 +1329,17 @@ NAN_METHOD(pipeline) {
baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_DZ; baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_DZ;
} }
baton->tileFormat = AttrAsStr(options, "tileFormat"); baton->tileFormat = AttrAsStr(options, "tileFormat");
std::string tileDepth = AttrAsStr(options, "tileDepth");
if (tileDepth == "onetile") {
baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_ONETILE;
} else if (tileDepth == "one") {
baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_ONE;
} else if (tileDepth == "onepixel") {
baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_ONEPIXEL;
} else {
// signal that we do not want to pass any value to dzSave
baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_LAST;
}
// Force random access for certain operations // Force random access for certain operations
if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL && ( if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL && (
baton->trimTolerance != 0 || baton->normalise || baton->trimTolerance != 0 || baton->normalise ||

View File

@@ -70,9 +70,12 @@ struct PipelineBaton {
std::string kernel; std::string kernel;
bool fastShrinkOnLoad; bool fastShrinkOnLoad;
double background[4]; double background[4];
double tintA;
double tintB;
bool flatten; bool flatten;
bool negate; bool negate;
double blurSigma; double blurSigma;
int medianSize;
double sharpenSigma; double sharpenSigma;
double sharpenFlat; double sharpenFlat;
double sharpenJagged; double sharpenJagged;
@@ -99,8 +102,10 @@ struct PipelineBaton {
bool jpegProgressive; bool jpegProgressive;
std::string jpegChromaSubsampling; std::string jpegChromaSubsampling;
bool jpegTrellisQuantisation; bool jpegTrellisQuantisation;
int jpegQuantisationTable;
bool jpegOvershootDeringing; bool jpegOvershootDeringing;
bool jpegOptimiseScans; bool jpegOptimiseScans;
bool jpegOptimiseCoding;
bool pngProgressive; bool pngProgressive;
int pngCompressionLevel; int pngCompressionLevel;
bool pngAdaptiveFiltering; bool pngAdaptiveFiltering;
@@ -126,6 +131,7 @@ struct PipelineBaton {
VipsOperationBoolean booleanOp; VipsOperationBoolean booleanOp;
VipsOperationBoolean bandBoolOp; VipsOperationBoolean bandBoolOp;
int extractChannel; int extractChannel;
bool removeAlpha;
VipsInterpretation colourspace; VipsInterpretation colourspace;
int tileSize; int tileSize;
int tileOverlap; int tileOverlap;
@@ -133,6 +139,7 @@ struct PipelineBaton {
VipsForeignDzLayout tileLayout; VipsForeignDzLayout tileLayout;
std::string tileFormat; std::string tileFormat;
int tileAngle; int tileAngle;
VipsForeignDzDepth tileDepth;
PipelineBaton(): PipelineBaton():
input(nullptr), input(nullptr),
@@ -154,9 +161,12 @@ struct PipelineBaton {
cropOffsetLeft(0), cropOffsetLeft(0),
cropOffsetTop(0), cropOffsetTop(0),
premultiplied(false), premultiplied(false),
tintA(128.0),
tintB(128.0),
flatten(false), flatten(false),
negate(false), negate(false),
blurSigma(0.0), blurSigma(0.0),
medianSize(0),
sharpenSigma(0.0), sharpenSigma(0.0),
sharpenFlat(1.0), sharpenFlat(1.0),
sharpenJagged(2.0), sharpenJagged(2.0),
@@ -181,8 +191,10 @@ struct PipelineBaton {
jpegProgressive(false), jpegProgressive(false),
jpegChromaSubsampling("4:2:0"), jpegChromaSubsampling("4:2:0"),
jpegTrellisQuantisation(false), jpegTrellisQuantisation(false),
jpegQuantisationTable(0),
jpegOvershootDeringing(false), jpegOvershootDeringing(false),
jpegOptimiseScans(false), jpegOptimiseScans(false),
jpegOptimiseCoding(true),
pngProgressive(false), pngProgressive(false),
pngCompressionLevel(9), pngCompressionLevel(9),
pngAdaptiveFiltering(false), pngAdaptiveFiltering(false),
@@ -203,12 +215,14 @@ struct PipelineBaton {
booleanOp(VIPS_OPERATION_BOOLEAN_LAST), booleanOp(VIPS_OPERATION_BOOLEAN_LAST),
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST), bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
extractChannel(-1), extractChannel(-1),
removeAlpha(false),
colourspace(VIPS_INTERPRETATION_LAST), colourspace(VIPS_INTERPRETATION_LAST),
tileSize(256), tileSize(256),
tileOverlap(0), tileOverlap(0),
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS), tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ), tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ),
tileAngle(0){ tileAngle(0),
tileDepth(VIPS_FOREIGN_DZ_DEPTH_LAST){
background[0] = 0.0; background[0] = 0.0;
background[1] = 0.0; background[1] = 0.0;
background[2] = 0.0; background[2] = 0.0;

View File

@@ -59,7 +59,6 @@ class StatsWorker : public Nan::AsyncWorker {
using sharp::MaximumImageAlpha; using sharp::MaximumImageAlpha;
vips::VImage image; vips::VImage image;
vips::VImage stats;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN; sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try { try {
@@ -69,9 +68,8 @@ class StatsWorker : public Nan::AsyncWorker {
} }
if (imageType != sharp::ImageType::UNKNOWN) { if (imageType != sharp::ImageType::UNKNOWN) {
try { try {
stats = image.stats(); vips::VImage stats = image.stats();
int bands = image.bands(); int const bands = image.bands();
double const max = MaximumImageAlpha(image.interpretation());
for (int b = 1; b <= bands; b++) { for (int b = 1; b <= bands; b++) {
ChannelStats cStats(static_cast<int>(stats.getpoint(STAT_MIN_INDEX, b).front()), ChannelStats cStats(static_cast<int>(stats.getpoint(STAT_MIN_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAX_INDEX, b).front()), static_cast<int>(stats.getpoint(STAT_MAX_INDEX, b).front()),
@@ -83,11 +81,15 @@ class StatsWorker : public Nan::AsyncWorker {
static_cast<int>(stats.getpoint(STAT_MAXY_INDEX, b).front())); static_cast<int>(stats.getpoint(STAT_MAXY_INDEX, b).front()));
baton->channelStats.push_back(cStats); baton->channelStats.push_back(cStats);
} }
// Image is not opaque when alpha layer is present and contains a non-mamixa value
// alpha layer is there and the last band i.e. alpha has its max value greater than 0) if (sharp::HasAlpha(image)) {
if (sharp::HasAlpha(image) && stats.getpoint(STAT_MIN_INDEX, bands).front() != max) { double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
if (minAlpha != MaximumImageAlpha(image.interpretation())) {
baton->isOpaque = false; baton->isOpaque = false;
} }
}
// Estimate entropy via histogram of greyscale value frequency
baton->entropy = std::abs(image.colourspace(VIPS_INTERPRETATION_B_W)[0].hist_find().hist_entropy());
} catch (vips::VError const &err) { } catch (vips::VError const &err) {
(baton->err).append(err.what()); (baton->err).append(err.what());
} }
@@ -130,6 +132,7 @@ class StatsWorker : public Nan::AsyncWorker {
Set(info, New("channels").ToLocalChecked(), channels); Set(info, New("channels").ToLocalChecked(), channels);
Set(info, New("isOpaque").ToLocalChecked(), New<v8::Boolean>(baton->isOpaque)); Set(info, New("isOpaque").ToLocalChecked(), New<v8::Boolean>(baton->isOpaque));
Set(info, New("entropy").ToLocalChecked(), New<v8::Number>(baton->entropy));
argv[1] = info; argv[1] = info;
} }

View File

@@ -51,12 +51,14 @@ struct StatsBaton {
// Output // Output
std::vector<ChannelStats> channelStats; std::vector<ChannelStats> channelStats;
bool isOpaque; bool isOpaque;
double entropy;
std::string err; std::string err;
StatsBaton(): StatsBaton():
input(nullptr), input(nullptr),
isOpaque(true) isOpaque(true),
entropy(0.0)
{} {}
}; };

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 MiB

BIN
test/fixtures/G31D_MULTI.TIF vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

BIN
test/fixtures/expected/extract-lch.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
test/fixtures/expected/median_1.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
test/fixtures/expected/median_3.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

BIN
test/fixtures/expected/median_5.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

BIN
test/fixtures/expected/median_color.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
test/fixtures/expected/tint-alpha.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

BIN
test/fixtures/expected/tint-blue.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
test/fixtures/expected/tint-cmyk.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
test/fixtures/expected/tint-green.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
test/fixtures/expected/tint-red.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
test/fixtures/expected/tint-sepia.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -69,6 +69,8 @@ module.exports = {
inputJpgOverlayLayer2: getPath('alpha-layer-2-ink.jpg'), inputJpgOverlayLayer2: getPath('alpha-layer-2-ink.jpg'),
inputJpgTruncated: getPath('truncated.jpg'), // head -c 10000 2569067123_aca715a2ee_o.jpg > truncated.jpg inputJpgTruncated: getPath('truncated.jpg'), // head -c 10000 2569067123_aca715a2ee_o.jpg > truncated.jpg
inputJpgCenteredImage: getPath('centered_image.jpeg'), inputJpgCenteredImage: getPath('centered_image.jpeg'),
inputJpgRandom: getPath('random.jpg'), // convert -size 200x200 xc: +noise Random random.jpg
inputJpgThRandom: getPath('thRandom.jpg'), // convert random.jpg -channel G -threshold 5% -separate +channel -negate thRandom.jpg
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
inputPngWithTransparency: getPath('blackbug.png'), // public domain inputPngWithTransparency: getPath('blackbug.png'), // public domain
@@ -87,10 +89,12 @@ module.exports = {
inputPngTestJoinChannel: getPath('testJoinChannel.png'), inputPngTestJoinChannel: getPath('testJoinChannel.png'),
inputPngTruncated: getPath('truncated.png'), // gm convert 2569067123_aca715a2ee_o.jpg -resize 320x240 saw.png ; head -c 10000 saw.png > truncated.png inputPngTruncated: getPath('truncated.png'), // gm convert 2569067123_aca715a2ee_o.jpg -resize 320x240 saw.png ; head -c 10000 saw.png > truncated.png
inputPngEmbed: getPath('embedgravitybird.png'), // Released to sharp under a CC BY 4.0 inputPngEmbed: getPath('embedgravitybird.png'), // Released to sharp under a CC BY 4.0
inputPngRGBWithAlpha: getPath('2569067123_aca715a2ee_o.png'), // http://www.flickr.com/photos/grizdave/2569067123/ (same as inputJpg)
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
inputTiffMultipage: getPath('G31D_MULTI.TIF'), // gm convert G31D.TIF -resize 50% G31D_2.TIF ; tiffcp G31D.TIF G31D_2.TIF G31D_MULTI.TIF
inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646 inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646
inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif
inputTiff8BitDepth: getPath('8bit_depth.tiff'), inputTiff8BitDepth: getPath('8bit_depth.tiff'),

BIN
test/fixtures/random.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
test/fixtures/thRandom.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -81,35 +81,45 @@ describe('Alpha transparency', function () {
}); });
}); });
it('Enlargement with non-nearest neighbor interpolation shouldnt cause dark edges', function (done) { it('Enlargement with non-nearest neighbor interpolation shouldnt cause dark edges', function () {
const base = 'alpha-premultiply-enlargement-2048x1536-paper.png'; const base = 'alpha-premultiply-enlargement-2048x1536-paper.png';
const actual = fixtures.path('output.' + base); const actual = fixtures.path('output.' + base);
const expected = fixtures.expected(base); const expected = fixtures.expected(base);
sharp(fixtures.inputPngAlphaPremultiplicationSmall) return sharp(fixtures.inputPngAlphaPremultiplicationSmall)
.resize(2048, 1536) .resize(2048, 1536)
.toFile(actual, function (err) { .toFile(actual)
if (err) { .then(function () {
done(err);
} else {
fixtures.assertMaxColourDistance(actual, expected, 102); fixtures.assertMaxColourDistance(actual, expected, 102);
done();
}
}); });
}); });
it('Reduction with non-nearest neighbor interpolation shouldnt cause dark edges', function (done) { it('Reduction with non-nearest neighbor interpolation shouldnt cause dark edges', function () {
const base = 'alpha-premultiply-reduction-1024x768-paper.png'; const base = 'alpha-premultiply-reduction-1024x768-paper.png';
const actual = fixtures.path('output.' + base); const actual = fixtures.path('output.' + base);
const expected = fixtures.expected(base); const expected = fixtures.expected(base);
sharp(fixtures.inputPngAlphaPremultiplicationLarge) return sharp(fixtures.inputPngAlphaPremultiplicationLarge)
.resize(1024, 768) .resize(1024, 768)
.toFile(actual, function (err) { .toFile(actual)
if (err) { .then(function () {
done(err);
} else {
fixtures.assertMaxColourDistance(actual, expected, 102); fixtures.assertMaxColourDistance(actual, expected, 102);
done();
}
}); });
}); });
it('Removes alpha from fixtures with transparency, ignores those without', function () {
return Promise.all([
fixtures.inputPngWithTransparency,
fixtures.inputPngWithTransparency16bit,
fixtures.inputWebPWithTransparency,
fixtures.inputJpg,
fixtures.inputPng,
fixtures.inputWebP
].map(function (input) {
return sharp(input)
.removeAlpha()
.toBuffer({ resolveWithObject: true })
.then(function (result) {
assert.strictEqual(3, result.info.channels);
});
}));
});
}); });

View File

@@ -159,7 +159,7 @@ describe('Crop', function () {
}); });
}); });
it('Skip crop when post-resize dimensions are at or below target dimensions', function () { it('Skip crop when post-resize dimensions are at target', function () {
return sharp(fixtures.inputJpg) return sharp(fixtures.inputJpg)
.resize(1600, 1200) .resize(1600, 1200)
.toBuffer() .toBuffer()

View File

@@ -54,6 +54,32 @@ describe('Image channel extraction', function () {
}); });
}); });
it('With colorspace conversion', function (done) {
const output = fixtures.path('output.extract-lch.jpg');
sharp(fixtures.inputJpg)
.toColourspace('lch')
.extractChannel(1)
.resize(320, 240)
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertMaxColourDistance(output, fixtures.expected('extract-lch.jpg'));
done();
});
});
it('Alpha from 16-bit PNG', function (done) {
const output = fixtures.path('output.extract-alpha-16bit.jpg');
sharp(fixtures.inputPngWithTransparency16bit)
.extractChannel(3)
.toFile(output, function (err, info) {
if (err) throw err;
fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-16bit.jpg'));
done();
});
});
it('Invalid channel number', function () { it('Invalid channel number', function () {
assert.throws(function () { assert.throws(function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)

View File

@@ -389,6 +389,16 @@ describe('Input/output', function () {
}); });
}); });
describe('Invalid JPEG quantisation table', function () {
[-1, 88.2, 'test'].forEach(function (table) {
it(table.toString(), function () {
assert.throws(function () {
sharp().jpeg({ quantisationTable: table });
});
});
});
});
it('Progressive JPEG image', function (done) { it('Progressive JPEG image', function (done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
@@ -528,6 +538,22 @@ describe('Input/output', function () {
}); });
}); });
it('TIFF file input with invalid page fails gracefully', function (done) {
sharp(fixtures.inputTiffMultipage, { page: 2 })
.toBuffer(function (err) {
assert.strictEqual(true, !!err);
done();
});
});
it('TIFF buffer input with invalid page fails gracefully', function (done) {
sharp(fs.readFileSync(fixtures.inputTiffMultipage), { page: 2 })
.toBuffer(function (err) {
assert.strictEqual(true, !!err);
done();
});
});
describe('Output filename with unknown extension', function () { describe('Output filename with unknown extension', function () {
it('Match JPEG input', function (done) { it('Match JPEG input', function (done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
@@ -810,6 +836,67 @@ describe('Input/output', function () {
}); });
}); });
it('Optimise coding generates smaller output length', function (done) {
// First generate with optimize coding enabled (default)
sharp(fixtures.inputJpg)
.resize(320, 240)
.jpeg()
.toBuffer(function (err, withOptimiseCoding, withInfo) {
if (err) throw err;
assert.strictEqual(true, withOptimiseCoding.length > 0);
assert.strictEqual(withOptimiseCoding.length, withInfo.size);
assert.strictEqual('jpeg', withInfo.format);
assert.strictEqual(320, withInfo.width);
assert.strictEqual(240, withInfo.height);
// Then generate with coding disabled
sharp(fixtures.inputJpg)
.resize(320, 240)
.jpeg({ optimizeCoding: false })
.toBuffer(function (err, withoutOptimiseCoding, withoutInfo) {
if (err) throw err;
assert.strictEqual(true, withoutOptimiseCoding.length > 0);
assert.strictEqual(withoutOptimiseCoding.length, withoutInfo.size);
assert.strictEqual('jpeg', withoutInfo.format);
assert.strictEqual(320, withoutInfo.width);
assert.strictEqual(240, withoutInfo.height);
// Verify optimised image is of a smaller size
assert.strictEqual(true, withOptimiseCoding.length < withoutOptimiseCoding.length);
done();
});
});
});
it('Specifying quantisation table provides different JPEG', function (done) {
// First generate with default quantisation table
sharp(fixtures.inputJpg)
.resize(320, 240)
.jpeg({ optimiseCoding: false })
.toBuffer(function (err, withDefaultQuantisationTable, withInfo) {
if (err) throw err;
assert.strictEqual(true, withDefaultQuantisationTable.length > 0);
assert.strictEqual(withDefaultQuantisationTable.length, withInfo.size);
assert.strictEqual('jpeg', withInfo.format);
assert.strictEqual(320, withInfo.width);
assert.strictEqual(240, withInfo.height);
// Then generate with different quantisation table
sharp(fixtures.inputJpg)
.resize(320, 240)
.jpeg({ optimiseCoding: false, quantisationTable: 3 })
.toBuffer(function (err, withQuantTable3, withoutInfo) {
if (err) throw err;
assert.strictEqual(true, withQuantTable3.length > 0);
assert.strictEqual(withQuantTable3.length, withoutInfo.size);
assert.strictEqual('jpeg', withoutInfo.format);
assert.strictEqual(320, withoutInfo.width);
assert.strictEqual(240, withoutInfo.height);
// Verify image is same (as mozjpeg may not be present) size or less
assert.strictEqual(true, withQuantTable3.length <= withDefaultQuantisationTable.length);
done();
});
});
});
it('Convert SVG to PNG at default 72DPI', function (done) { it('Convert SVG to PNG at default 72DPI', function (done) {
sharp(fixtures.inputSvg) sharp(fixtures.inputSvg)
.resize(1024) .resize(1024)
@@ -880,6 +967,53 @@ describe('Input/output', function () {
}); });
}); });
it('Load multi-page TIFF\'s from file', function (done) {
sharp(fixtures.inputTiffMultipage) // defaults to page 0
.jpeg()
.toBuffer(function (err, defaultData, defaultInfo) {
if (err) throw err;
assert.strictEqual(true, defaultData.length > 0);
assert.strictEqual(defaultData.length, defaultInfo.size);
assert.strictEqual('jpeg', defaultInfo.format);
sharp(fixtures.inputTiffMultipage, { page: 1 }) // 50%-scale copy of page 0
.jpeg()
.toBuffer(function (err, scaledData, scaledInfo) {
if (err) throw err;
assert.strictEqual(true, scaledData.length > 0);
assert.strictEqual(scaledData.length, scaledInfo.size);
assert.strictEqual('jpeg', scaledInfo.format);
assert.strictEqual(defaultInfo.width, scaledInfo.width * 2);
assert.strictEqual(defaultInfo.height, scaledInfo.height * 2);
done();
});
});
});
it('Load multi-page TIFF\'s from Buffer', function (done) {
const inputTiffBuffer = fs.readFileSync(fixtures.inputTiffMultipage);
sharp(inputTiffBuffer) // defaults to page 0
.jpeg()
.toBuffer(function (err, defaultData, defaultInfo) {
if (err) throw err;
assert.strictEqual(true, defaultData.length > 0);
assert.strictEqual(defaultData.length, defaultInfo.size);
assert.strictEqual('jpeg', defaultInfo.format);
sharp(inputTiffBuffer, { page: 1 }) // 50%-scale copy of page 0
.jpeg()
.toBuffer(function (err, scaledData, scaledInfo) {
if (err) throw err;
assert.strictEqual(true, scaledData.length > 0);
assert.strictEqual(scaledData.length, scaledInfo.size);
assert.strictEqual('jpeg', scaledInfo.format);
assert.strictEqual(defaultInfo.width, scaledInfo.width * 2);
assert.strictEqual(defaultInfo.height, scaledInfo.height * 2);
done();
});
});
});
it('Save TIFF to Buffer', function (done) { it('Save TIFF to Buffer', function (done) {
sharp(fixtures.inputTiff) sharp(fixtures.inputTiff)
.resize(320, 240) .resize(320, 240)
@@ -1020,6 +1154,22 @@ describe('Input/output', function () {
}); });
}); });
it('TIFF ccittfax4 compression shrinks b-w test file', function (done) {
const startSize = fs.statSync(fixtures.inputTiff).size;
sharp(fixtures.inputTiff)
.toColourspace('b-w')
.tiff({
squash: true,
compression: 'ccittfax4'
})
.toFile(fixtures.outputTiff, (err, info) => {
if (err) throw err;
assert.strictEqual('tiff', info.format);
assert(info.size < startSize);
fs.unlink(fixtures.outputTiff, done);
});
});
it('TIFF deflate compression with horizontal predictor shrinks test file', function (done) { it('TIFF deflate compression with horizontal predictor shrinks test file', function (done) {
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size; const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
sharp(fixtures.inputTiffUncompressed) sharp(fixtures.inputTiffUncompressed)

View File

@@ -1,6 +1,7 @@
'use strict'; 'use strict';
const assert = require('assert'); const assert = require('assert');
const fs = require('fs');
const semver = require('semver'); const semver = require('semver');
const libvips = require('../../lib/libvips'); const libvips = require('../../lib/libvips');
@@ -58,5 +59,19 @@ describe('libvips binaries', function () {
const hasVendoredLibvips = libvips.hasVendoredLibvips(); const hasVendoredLibvips = libvips.hasVendoredLibvips();
assert.strictEqual('boolean', typeof hasVendoredLibvips); assert.strictEqual('boolean', typeof hasVendoredLibvips);
}); });
it('useGlobalLibvips can be ignored via an env var', function () {
process.env.SHARP_IGNORE_GLOBAL_LIBVIPS = 1;
const useGlobalLibvips = libvips.useGlobalLibvips();
assert.strictEqual(false, useGlobalLibvips);
delete process.env.SHARP_IGNORE_GLOBAL_LIBVIPS;
});
it('cachePath returns a valid path ending with _libvips', function () {
const cachePath = libvips.cachePath();
assert.strictEqual('string', typeof cachePath);
assert.strictEqual('_libvips', cachePath.substr(-8));
assert.strictEqual(true, fs.existsSync(cachePath));
});
}); });
}); });

72
test/unit/median.js Normal file
View File

@@ -0,0 +1,72 @@
'use strict';
const assert = require('assert');
const sharp = require('../../');
const fixtures = require('../fixtures');
describe('Median filter', function () {
it('1x1 window', function (done) {
sharp(fixtures.inputJpgThRandom)
.median(1)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(200, info.width);
assert.strictEqual(200, info.height);
fixtures.assertSimilar(fixtures.expected('median_1.jpg'), data, done);
});
});
it('3x3 window', function (done) {
sharp(fixtures.inputJpgThRandom)
.median(3)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(200, info.width);
assert.strictEqual(200, info.height);
fixtures.assertSimilar(fixtures.expected('median_3.jpg'), data, done);
});
});
it('5x5 window', function (done) {
sharp(fixtures.inputJpgThRandom)
.median(5)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(200, info.width);
assert.strictEqual(200, info.height);
fixtures.assertSimilar(fixtures.expected('median_5.jpg'), data, done);
});
});
it('color image', function (done) {
sharp(fixtures.inputJpgRandom)
.median(5)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(200, info.width);
assert.strictEqual(200, info.height);
fixtures.assertSimilar(fixtures.expected('median_color.jpg'), data, done);
});
});
it('no windows size', function (done) {
sharp(fixtures.inputJpgThRandom)
.median()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(200, info.width);
assert.strictEqual(200, info.height);
fixtures.assertSimilar(fixtures.expected('median_3.jpg'), data, done);
});
});
it('invalid radius', function () {
assert.throws(function () {
sharp(fixtures.inputJpg).median(0.1);
});
});
});

View File

@@ -130,6 +130,25 @@ describe('Resize dimensions', function () {
}); });
}); });
it('JPEG shrink-on-load with 90 degree rotation, ensure recalculation is correct', function (done) {
sharp(fixtures.inputJpg)
.resize(1920, 1280)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(1920, info.width);
assert.strictEqual(1280, info.height);
sharp(data)
.rotate(90)
.resize(533, 800)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(533, info.width);
assert.strictEqual(800, info.height);
done();
});
});
});
it('TIFF embed known to cause rounding errors', function (done) { it('TIFF embed known to cause rounding errors', function (done) {
sharp(fixtures.inputTiff) sharp(fixtures.inputTiff)
.resize(240, 320) .resize(240, 320)

View File

@@ -24,6 +24,7 @@ describe('Image Stats', function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, stats.isOpaque); assert.strictEqual(true, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
// red channel // red channel
assert.strictEqual(0, stats.channels[0]['min']); assert.strictEqual(0, stats.channels[0]['min']);
@@ -82,6 +83,7 @@ describe('Image Stats', function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, stats.isOpaque); assert.strictEqual(true, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3409031108021736));
// red channel // red channel
assert.strictEqual(0, stats.channels[0]['min']); assert.strictEqual(0, stats.channels[0]['min']);
@@ -105,7 +107,9 @@ describe('Image Stats', function () {
it('PNG with transparency', function (done) { it('PNG with transparency', function (done) {
sharp(fixtures.inputPngWithTransparency).stats(function (err, stats) { sharp(fixtures.inputPngWithTransparency).stats(function (err, stats) {
if (err) throw err; if (err) throw err;
assert.strictEqual(false, stats.isOpaque); assert.strictEqual(false, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.06778064835816622));
// red channel // red channel
assert.strictEqual(0, stats.channels[0]['min']); assert.strictEqual(0, stats.channels[0]['min']);
@@ -180,6 +184,7 @@ describe('Image Stats', function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(false, stats.isOpaque); assert.strictEqual(false, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0));
// alpha channel // alpha channel
assert.strictEqual(0, stats.channels[3]['min']); assert.strictEqual(0, stats.channels[3]['min']);
@@ -204,7 +209,9 @@ describe('Image Stats', function () {
it('Tiff', function (done) { it('Tiff', function (done) {
sharp(fixtures.inputTiff).stats(function (err, stats) { sharp(fixtures.inputTiff).stats(function (err, stats) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, stats.isOpaque); assert.strictEqual(true, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3851250782608986));
// red channel // red channel
assert.strictEqual(0, stats.channels[0]['min']); assert.strictEqual(0, stats.channels[0]['min']);
@@ -231,6 +238,7 @@ describe('Image Stats', function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, stats.isOpaque); assert.strictEqual(true, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.51758075132966));
// red channel // red channel
assert.strictEqual(0, stats.channels[0]['min']); assert.strictEqual(0, stats.channels[0]['min']);
@@ -289,6 +297,7 @@ describe('Image Stats', function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, stats.isOpaque); assert.strictEqual(true, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 6.087309412541799));
// red channel // red channel
assert.strictEqual(35, stats.channels[0]['min']); assert.strictEqual(35, stats.channels[0]['min']);
@@ -345,7 +354,9 @@ describe('Image Stats', function () {
it('Grayscale GIF with alpha', function (done) { it('Grayscale GIF with alpha', function (done) {
sharp(fixtures.inputGifGreyPlusAlpha).stats(function (err, stats) { sharp(fixtures.inputGifGreyPlusAlpha).stats(function (err, stats) {
if (err) throw err; if (err) throw err;
assert.strictEqual(false, stats.isOpaque); assert.strictEqual(false, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 1));
// gray channel // gray channel
assert.strictEqual(0, stats.channels[0]['min']); assert.strictEqual(0, stats.channels[0]['min']);
@@ -387,7 +398,9 @@ describe('Image Stats', function () {
const readable = fs.createReadStream(fixtures.inputJpg); const readable = fs.createReadStream(fixtures.inputJpg);
const pipeline = sharp().stats(function (err, stats) { const pipeline = sharp().stats(function (err, stats) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, stats.isOpaque); assert.strictEqual(true, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
// red channel // red channel
assert.strictEqual(0, stats.channels[0]['min']); assert.strictEqual(0, stats.channels[0]['min']);
@@ -449,6 +462,7 @@ describe('Image Stats', function () {
return pipeline.stats().then(function (stats) { return pipeline.stats().then(function (stats) {
assert.strictEqual(true, stats.isOpaque); assert.strictEqual(true, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
// red channel // red channel
assert.strictEqual(0, stats.channels[0]['min']); assert.strictEqual(0, stats.channels[0]['min']);
@@ -505,6 +519,7 @@ describe('Image Stats', function () {
it('File in, Promise out', function () { it('File in, Promise out', function () {
return sharp(fixtures.inputJpg).stats().then(function (stats) { return sharp(fixtures.inputJpg).stats().then(function (stats) {
assert.strictEqual(true, stats.isOpaque); assert.strictEqual(true, stats.isOpaque);
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
// red channel // red channel
assert.strictEqual(0, stats.channels[0]['min']); assert.strictEqual(0, stats.channels[0]['min']);

View File

@@ -6,7 +6,7 @@ const assert = require('assert');
const eachLimit = require('async/eachLimit'); const eachLimit = require('async/eachLimit');
const rimraf = require('rimraf'); const rimraf = require('rimraf');
const unzip = require('unzip'); const DecompressZip = require('decompress-zip');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
@@ -46,6 +46,51 @@ const assertDeepZoomTiles = function (directory, expectedSize, expectedLevels, d
}, done); }, done);
}; };
const assertZoomifyTiles = function (directory, expectedTileSize, expectedLevels, done) {
fs.stat(path.join(directory, 'ImageProperties.xml'), function (err, stat) {
if (err) throw err;
assert.ok(stat.isFile());
assert.ok(stat.size > 0);
let maxTileLevel = -1;
fs.readdirSync(path.join(directory, 'TileGroup0')).forEach(function (tile) {
// Verify tile file name
assert.ok(/^[0-9]+-[0-9]+-[0-9]+\.jpg$/.test(tile));
let level = parseInt(tile.split('-')[0]);
maxTileLevel = Math.max(maxTileLevel, level);
});
assert.strictEqual(maxTileLevel + 1, expectedLevels); // add one to account for zero level tile
done();
});
};
const assertGoogleTiles = function (directory, expectedTileSize, expectedLevels, done) {
const levels = fs.readdirSync(directory);
assert.strictEqual(expectedLevels, levels.length - 1); // subtract one to account for default blank tile
fs.stat(path.join(directory, 'blank.png'), function (err, stat) {
if (err) throw err;
assert.ok(stat.isFile());
assert.ok(stat.size > 0);
// Basic check to confirm lowest and highest level tiles exist
fs.stat(path.join(directory, '0', '0', '0.jpg'), function (err, stat) {
if (err) throw err;
assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0);
fs.stat(path.join(directory, (expectedLevels - 1).toString(), '0', '0.jpg'), function (err, stat) {
if (err) throw err;
assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0);
done();
});
});
});
};
describe('Tile', function () { describe('Tile', function () {
it('Valid size values pass', function () { it('Valid size values pass', function () {
[1, 8192].forEach(function (size) { [1, 8192].forEach(function (size) {
@@ -144,6 +189,26 @@ describe('Tile', function () {
}); });
}); });
it('Valid depths pass', function () {
['onepixel', 'onetile', 'one'].forEach(function (depth) {
assert.doesNotThrow(function (depth) {
sharp().tile({
depth: depth
});
});
});
});
it('Invalid depths fail', function () {
['depth', 1].forEach(function (depth) {
assert.throws(function () {
sharp().tile({
depth: depth
});
});
});
});
it('Prevent larger overlap than default size', function () { it('Prevent larger overlap than default size', function () {
assert.throws(function () { assert.throws(function () {
sharp().tile({ sharp().tile({
@@ -251,6 +316,54 @@ describe('Tile', function () {
}); });
}); });
it('Deep Zoom layout with depth of one', function (done) {
const directory = fixtures.path('output.512_depth_one.dzi_files');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
size: 512,
depth: 'one'
})
.toFile(fixtures.path('output.512_depth_one.dzi'), function (err, info) {
if (err) throw err;
// Verify only one depth generated
assertDeepZoomTiles(directory, 512, 1, done);
});
});
});
it('Deep Zoom layout with depth of onepixel', function (done) {
const directory = fixtures.path('output.512_depth_onepixel.dzi_files');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
size: 512,
depth: 'onepixel'
})
.toFile(fixtures.path('output.512_depth_onepixel.dzi'), function (err, info) {
if (err) throw err;
// Verify only one depth generated
assertDeepZoomTiles(directory, 512, 13, done);
});
});
});
it('Deep Zoom layout with depth of onetile', function (done) {
const directory = fixtures.path('output.256_depth_onetile.dzi_files');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
size: 256,
depth: 'onetile'
})
.toFile(fixtures.path('output.256_depth_onetile.dzi'), function (err, info) {
if (err) throw err;
// Verify only one depth generated
assertDeepZoomTiles(directory, 256, 5, done);
});
});
});
it('Zoomify layout', function (done) { it('Zoomify layout', function (done) {
const directory = fixtures.path('output.zoomify.dzi'); const directory = fixtures.path('output.zoomify.dzi');
rimraf(directory, function () { rimraf(directory, function () {
@@ -275,6 +388,69 @@ describe('Tile', function () {
}); });
}); });
it('Zoomify layout with depth one', function (done) {
const directory = fixtures.path('output.zoomify.depth_one.dzi');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
size: 256,
layout: 'zoomify',
depth: 'one'
})
.toFile(directory, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual('number', typeof info.size);
assertZoomifyTiles(directory, 256, 1, done);
});
});
});
it('Zoomify layout with depth onetile', function (done) {
const directory = fixtures.path('output.zoomify.depth_onetile.dzi');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
size: 256,
layout: 'zoomify',
depth: 'onetile'
})
.toFile(directory, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual('number', typeof info.size);
assertZoomifyTiles(directory, 256, 5, done);
});
});
});
it('Zoomify layout with depth onepixel', function (done) {
const directory = fixtures.path('output.zoomify.depth_onepixel.dzi');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
size: 256,
layout: 'zoomify',
depth: 'onepixel'
})
.toFile(directory, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual('number', typeof info.size);
assertZoomifyTiles(directory, 256, 13, done);
});
});
});
it('Google layout', function (done) { it('Google layout', function (done) {
const directory = fixtures.path('output.google.dzi'); const directory = fixtures.path('output.google.dzi');
rimraf(directory, function () { rimraf(directory, function () {
@@ -410,6 +586,72 @@ describe('Tile', function () {
}); });
}); });
it('Google layout with depth one', function (done) {
const directory = fixtures.path('output.google_depth_one.dzi');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
layout: 'google',
depth: 'one',
size: 256
})
.toFile(directory, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual('number', typeof info.size);
assertGoogleTiles(directory, 256, 1, done);
});
});
});
it('Google layout with depth onepixel', function (done) {
const directory = fixtures.path('output.google_depth_onepixel.dzi');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
layout: 'google',
depth: 'onepixel',
size: 256
})
.toFile(directory, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual('number', typeof info.size);
assertGoogleTiles(directory, 256, 13, done);
});
});
});
it('Google layout with depth onetile', function (done) {
const directory = fixtures.path('output.google_depth_onetile.dzi');
rimraf(directory, function () {
sharp(fixtures.inputJpg)
.tile({
layout: 'google',
depth: 'onetile',
size: 256
})
.toFile(directory, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual('number', typeof info.size);
assertGoogleTiles(directory, 256, 5, done);
});
});
});
it('Write to ZIP container using file extension', function (done) { it('Write to ZIP container using file extension', function (done) {
const container = fixtures.path('output.dz.container.zip'); const container = fixtures.path('output.dz.container.zip');
const extractTo = fixtures.path('output.dz.container'); const extractTo = fixtures.path('output.dz.container');
@@ -427,16 +669,14 @@ describe('Tile', function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, stat.isFile()); assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0); assert.strictEqual(true, stat.size > 0);
fs.createReadStream(container) new DecompressZip(container)
.pipe(unzip.Extract({ .on('extract', function () {
path: path.dirname(extractTo) assertDeepZoomTiles(directory, 256, 13, done);
})) })
.on('error', function (err) { .on('error', function (err) {
throw err; throw err;
}) })
.on('close', function () { .extract({ path: path.dirname(extractTo) });
assertDeepZoomTiles(directory, 256, 13, done);
});
}); });
}); });
}); });
@@ -463,16 +703,14 @@ describe('Tile', function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, stat.isFile()); assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0); assert.strictEqual(true, stat.size > 0);
fs.createReadStream(container) new DecompressZip(container)
.pipe(unzip.Extract({ .on('extract', function () {
path: path.dirname(extractTo) assertDeepZoomTiles(directory, 256, 13, done);
})) })
.on('error', function (err) { .on('error', function (err) {
throw err; throw err;
}) })
.on('close', function () { .extract({ path: path.dirname(extractTo) });
assertDeepZoomTiles(directory, 256, 13, done);
});
}); });
}); });
}); });

102
test/unit/tint.js Normal file
View File

@@ -0,0 +1,102 @@
'use strict';
const assert = require('assert');
const sharp = require('../../');
const fixtures = require('../fixtures');
describe('Tint', function () {
it('tints rgb image red', function (done) {
const output = fixtures.path('output.tint-red.jpg');
sharp(fixtures.inputJpg)
.resize(320, 240)
.tint('#FF0000')
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
fixtures.assertMaxColourDistance(output, fixtures.expected('tint-red.jpg'), 10);
done();
});
});
it('tints rgb image green', function (done) {
const output = fixtures.path('output.tint-green.jpg');
sharp(fixtures.inputJpg)
.resize(320, 240)
.tint('#00FF00')
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
fixtures.assertMaxColourDistance(output, fixtures.expected('tint-green.jpg'), 10);
done();
});
});
it('tints rgb image blue', function (done) {
const output = fixtures.path('output.tint-blue.jpg');
sharp(fixtures.inputJpg)
.resize(320, 240)
.tint('#0000FF')
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
fixtures.assertMaxColourDistance(output, fixtures.expected('tint-blue.jpg'), 10);
done();
});
});
it('tints rgb image with sepia tone', function (done) {
const output = fixtures.path('output.tint-sepia.jpg');
sharp(fixtures.inputJpg)
.resize(320, 240)
.tint('#704214')
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertMaxColourDistance(output, fixtures.expected('tint-sepia.jpg'), 10);
done();
});
});
it('tints rgb image with sepia tone with rgb colour', function (done) {
const output = fixtures.path('output.tint-sepia.jpg');
sharp(fixtures.inputJpg)
.resize(320, 240)
.tint([112, 66, 20])
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertMaxColourDistance(output, fixtures.expected('tint-sepia.jpg'), 10);
done();
});
});
it('tints rgb image with alpha channel', function (done) {
const output = fixtures.path('output.tint-alpha.png');
sharp(fixtures.inputPngRGBWithAlpha)
.resize(320, 240)
.tint('#704214')
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertMaxColourDistance(output, fixtures.expected('tint-alpha.png'), 10);
done();
});
});
it('tints cmyk image red', function (done) {
const output = fixtures.path('output.tint-cmyk.jpg');
sharp(fixtures.inputJpgWithCmykProfile)
.resize(320, 240)
.tint('#FF0000')
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
fixtures.assertMaxColourDistance(output, fixtures.expected('tint-cmyk.jpg'), 10);
done();
});
});
});