Compare commits

...

29 Commits

Author SHA1 Message Date
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
47 changed files with 850 additions and 168 deletions

View File

@@ -16,7 +16,7 @@ matrix:
- os: linux
dist: trusty
sudo: false
node_js: "9"
node_js: "10"
- os: osx
osx_image: xcode8.3
node_js: "4"
@@ -28,7 +28,7 @@ matrix:
node_js: "8"
- os: osx
osx_image: xcode8.3
node_js: "9"
node_js: "10"
after_success:
- npm install coveralls
- 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 |
| ------: | :--------- |
| v0.20.0 | prebuild |
| v0.21.0 | teeth |
| v0.22.0 | uptake |
@@ -90,15 +89,6 @@ Requires [Valgrind](http://valgrind.org/).
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
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.
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.
## Examples

View File

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

View File

@@ -3,10 +3,11 @@
### Table of Contents
- [background][1]
- [greyscale][2]
- [grayscale][3]
- [toColourspace][4]
- [toColorspace][5]
- [tint][2]
- [greyscale][3]
- [grayscale][4]
- [toColourspace][5]
- [toColorspace][6]
## background
@@ -19,10 +20,24 @@ The alpha value is a float between `0` (transparent) and `1` (opaque).
**Parameters**
- `rgba` **([String][6] \| [Object][7])** parsed by the [color][8] module to extract values for red, green, blue and alpha.
- `rgba` **([String][7] \| [Object][8])** parsed by the [color][9] module to extract values for red, green, blue and alpha.
- Throws **[Error][9]** Invalid parameter
- Throws **[Error][10]** 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][7] \| [Object][8])** parsed by the [color][9] module to extract chroma values.
- Throws **[Error][10]** Invalid parameter
Returns **Sharp**
@@ -37,7 +52,7 @@ An alpha channel may be present, and will be unchanged by the operation.
**Parameters**
- `greyscale` **[Boolean][10]** (optional, default `true`)
- `greyscale` **[Boolean][11]** (optional, default `true`)
Returns **Sharp**
@@ -47,7 +62,7 @@ Alternative spelling of `greyscale`.
**Parameters**
- `grayscale` **[Boolean][10]** (optional, default `true`)
- `grayscale` **[Boolean][11]** (optional, default `true`)
Returns **Sharp**
@@ -58,10 +73,10 @@ By default output image will be web-friendly sRGB, with additional channels inte
**Parameters**
- `colourspace` **[String][6]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][11]
- `colourspace` **[String][7]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][12]
- Throws **[Error][9]** Invalid parameters
- Throws **[Error][10]** Invalid parameters
Returns **Sharp**
@@ -71,31 +86,33 @@ Alternative spelling of `toColourspace`.
**Parameters**
- `colorspace` **[String][6]?** output colorspace.
- `colorspace` **[String][7]?** output colorspace.
- Throws **[Error][9]** Invalid parameters
- Throws **[Error][10]** Invalid parameters
Returns **Sharp**
[1]: #background
[2]: #greyscale
[2]: #tint
[3]: #grayscale
[3]: #greyscale
[4]: #tocolourspace
[4]: #grayscale
[5]: #tocolorspace
[5]: #tocolourspace
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[6]: #tocolorspace
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[8]: https://www.npmjs.org/package/color
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[9]: https://www.npmjs.org/package/color
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[11]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[12]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568

View File

@@ -20,6 +20,7 @@
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`)
- `options.density` **[Number][9]** integral number representing the DPI for vector images. (optional, default `72`)
- `options.page` **[Number][9]** page number to extract for multi-page input (GIF, TIFF) (optional, default `0`)
- `options.raw` **[Object][7]?** describes raw pixel input image data. See `raw()` for pixel ordering.
- `options.raw.width` **[Number][9]?**
- `options.raw.height` **[Number][9]?**

View File

@@ -7,18 +7,19 @@
- [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]
- [median][6]
- [blur][7]
- [extend][8]
- [flatten][9]
- [trim][10]
- [gamma][11]
- [negate][12]
- [normalise][13]
- [normalize][14]
- [convolve][15]
- [threshold][16]
- [boolean][17]
- [linear][18]
## rotate
@@ -38,7 +39,7 @@ for example `rotate(x).extract(y)` will produce a different result to `extract(y
**Parameters**
- `angle` **[Number][18]** angle of rotation, must be a multiple of 90. (optional, default `auto`)
- `angle` **[Number][19]** angle of rotation, must be a multiple of 90. (optional, default `auto`)
**Examples**
@@ -54,7 +55,7 @@ const pipeline = sharp()
readableStream.pipe(pipeline);
```
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -68,11 +69,11 @@ Extract a region of the image.
**Parameters**
- `options` **[Object][20]**
- `options.left` **[Number][18]** zero-indexed offset from left edge
- `options.top` **[Number][18]** zero-indexed offset from top edge
- `options.width` **[Number][18]** dimension of extracted image
- `options.height` **[Number][18]** dimension of extracted image
- `options` **[Object][21]**
- `options.left` **[Number][19]** zero-indexed offset from left edge
- `options.top` **[Number][19]** zero-indexed offset from top edge
- `options.width` **[Number][19]** dimension of extracted image
- `options.height` **[Number][19]** dimension of extracted image
**Examples**
@@ -94,7 +95,7 @@ sharp(input)
});
```
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -105,7 +106,7 @@ The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
**Parameters**
- `flip` **[Boolean][21]** (optional, default `true`)
- `flip` **[Boolean][22]** (optional, default `true`)
Returns **Sharp**
@@ -116,7 +117,7 @@ The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
**Parameters**
- `flop` **[Boolean][21]** (optional, default `true`)
- `flop` **[Boolean][22]** (optional, default `true`)
Returns **Sharp**
@@ -129,12 +130,26 @@ Separate control over the level of sharpening in "flat" and "jagged" areas is av
**Parameters**
- `sigma` **[Number][18]?** 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`)
- `jagged` **[Number][18]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
- `sigma` **[Number][19]?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- `flat` **[Number][19]** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
- `jagged` **[Number][19]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
## median
Apply median filter.
When used without parameters the default window is 3x3.
**Parameters**
- `size` **[Number][19]** square mask size: size x size (optional, default `3`)
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -146,10 +161,10 @@ When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
**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][19]?** 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][20]** Invalid parameters
Returns **Sharp**
@@ -160,11 +175,11 @@ This operation will always occur after resizing and extraction, if any.
**Parameters**
- `extend` **([Number][18] \| [Object][20])** single pixel count to add to all edges or an Object with per-edge counts
- `extend.top` **[Number][18]?**
- `extend.left` **[Number][18]?**
- `extend.bottom` **[Number][18]?**
- `extend.right` **[Number][18]?**
- `extend` **([Number][19] \| [Object][21])** single pixel count to add to all edges or an Object with per-edge counts
- `extend.top` **[Number][19]?**
- `extend.left` **[Number][19]?**
- `extend.bottom` **[Number][19]?**
- `extend.right` **[Number][19]?**
**Examples**
@@ -178,7 +193,7 @@ sharp(input)
...
```
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -188,7 +203,7 @@ Merge alpha transparency channel, if any, with `background`.
**Parameters**
- `flatten` **[Boolean][21]** (optional, default `true`)
- `flatten` **[Boolean][22]** (optional, default `true`)
Returns **Sharp**
@@ -198,10 +213,10 @@ Trim "boring" pixels from all edges that contain values within a percentage simi
**Parameters**
- `tolerance` **[Number][18]** value between 1 and 99 representing the percentage similarity. (optional, default `10`)
- `tolerance` **[Number][19]** value between 1 and 99 representing the percentage similarity. (optional, default `10`)
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -215,10 +230,10 @@ when applying a gamma correction.
**Parameters**
- `gamma` **[Number][18]** value between 1.0 and 3.0. (optional, default `2.2`)
- `gamma` **[Number][19]** value between 1.0 and 3.0. (optional, default `2.2`)
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -228,7 +243,7 @@ Produce the "negative" of the image.
**Parameters**
- `negate` **[Boolean][21]** (optional, default `true`)
- `negate` **[Boolean][22]** (optional, default `true`)
Returns **Sharp**
@@ -238,7 +253,7 @@ Enhance output image contrast by stretching its luminance to cover the full dyna
**Parameters**
- `normalise` **[Boolean][21]** (optional, default `true`)
- `normalise` **[Boolean][22]** (optional, default `true`)
Returns **Sharp**
@@ -248,7 +263,7 @@ Alternative spelling of normalise.
**Parameters**
- `normalize` **[Boolean][21]** (optional, default `true`)
- `normalize` **[Boolean][22]** (optional, default `true`)
Returns **Sharp**
@@ -258,12 +273,12 @@ Convolve the image with the specified kernel.
**Parameters**
- `kernel` **[Object][20]**
- `kernel.width` **[Number][18]** width of the kernel in pixels.
- `kernel.height` **[Number][18]** width of the kernel in pixels.
- `kernel.kernel` **[Array][22]<[Number][18]>** Array of length `width*height` containing the kernel values.
- `kernel.scale` **[Number][18]** 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` **[Object][21]**
- `kernel.width` **[Number][19]** width of the kernel in pixels.
- `kernel.height` **[Number][19]** width of the kernel in pixels.
- `kernel.kernel` **[Array][23]<[Number][19]>** Array of length `width*height` containing the kernel values.
- `kernel.scale` **[Number][19]** the scale of the kernel in pixels. (optional, default `sum`)
- `kernel.offset` **[Number][19]** the offset of the kernel in pixels. (optional, default `0`)
**Examples**
@@ -281,7 +296,7 @@ sharp(input)
});
```
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -291,13 +306,13 @@ Any pixel value greather than or equal to the threshold value will be set to 255
**Parameters**
- `threshold` **[Number][18]** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`)
- `options` **[Object][20]?**
- `options.greyscale` **[Boolean][21]** convert to single channel greyscale. (optional, default `true`)
- `options.grayscale` **[Boolean][21]** alternative spelling for greyscale. (optional, default `true`)
- `threshold` **[Number][19]** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`)
- `options` **[Object][21]?**
- `options.greyscale` **[Boolean][22]** convert to single channel greyscale. (optional, default `true`)
- `options.grayscale` **[Boolean][22]** alternative spelling for greyscale. (optional, default `true`)
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -310,16 +325,16 @@ the selected bitwise boolean `operation` between the corresponding pixels of the
**Parameters**
- `operand` **([Buffer][23] \| [String][24])** 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.
- `options` **[Object][20]?**
- `options.raw` **[Object][20]?** describes operand when using raw pixel data.
- `options.raw.width` **[Number][18]?**
- `options.raw.height` **[Number][18]?**
- `options.raw.channels` **[Number][18]?**
- `operand` **([Buffer][24] \| [String][25])** Buffer containing image data or String containing the path to an image file.
- `operator` **[String][25]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
- `options` **[Object][21]?**
- `options.raw` **[Object][21]?** describes operand when using raw pixel data.
- `options.raw.width` **[Number][19]?**
- `options.raw.height` **[Number][19]?**
- `options.raw.channels` **[Number][19]?**
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -329,11 +344,11 @@ Apply the linear formula a \* input + b to the image (levels adjustment)
**Parameters**
- `a` **[Number][18]** multiplier (optional, default `1.0`)
- `b` **[Number][18]** offset (optional, default `0.0`)
- `a` **[Number][19]** multiplier (optional, default `1.0`)
- `b` **[Number][19]** offset (optional, default `0.0`)
- Throws **[Error][19]** Invalid parameters
- Throws **[Error][20]** Invalid parameters
Returns **Sharp**
@@ -347,40 +362,42 @@ Returns **Sharp**
[5]: #sharpen
[6]: #blur
[6]: #median
[7]: #extend
[7]: #blur
[8]: #flatten
[8]: #extend
[9]: #trim
[9]: #flatten
[10]: #gamma
[10]: #trim
[11]: #negate
[11]: #gamma
[12]: #normalise
[12]: #negate
[13]: #normalize
[13]: #normalise
[14]: #convolve
[14]: #normalize
[15]: #threshold
[15]: #convolve
[16]: #boolean
[16]: #threshold
[17]: #linear
[17]: #boolean
[18]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[18]: #linear
[19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[20]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[20]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[21]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[21]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[22]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[22]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[23]: https://nodejs.org/api/buffer.html
[23]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[24]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[24]: https://nodejs.org/api/buffer.html
[25]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String

View File

@@ -31,6 +31,19 @@ A `Promise` is returned when `callback` is not provided.
`channels` and `premultiplied` (indicating if premultiplication was used).
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
**Examples**
```javascript
sharp(input)
.toFile('output.png', (err, info) => { ... });
```
```javascript
sharp(input)
.toFile('output.png')
.then(info => { ... })
.catch(err => { ... });
```
- Throws **[Error][13]** Invalid parameters
@@ -58,6 +71,27 @@ A `Promise` is returned when `callback` is not provided.
- `options.resolveWithObject` **[Boolean][16]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
- `callback` **[Function][12]?**
**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][14]<[Buffer][17]>** when no callback is provided
## withMetadata
@@ -71,6 +105,14 @@ This will also convert to and add a web-friendly sRGB ICC profile.
- `withMetadata` **[Object][15]?**
- `withMetadata.orientation` **[Number][18]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
**Examples**
```javascript
sharp('input.jpg')
.withMetadata()
.toFile('output-with-metadata.jpg')
.then(info => { ... });
```
- Throws **[Error][13]** Invalid parameters
@@ -92,6 +134,17 @@ Use these JPEG options for output image.
- `options.optimizeScans` **[Boolean][16]** alternative spelling of optimiseScans (optional, default `false`)
- `options.force` **[Boolean][16]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
**Examples**
```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][13]** Invalid options
@@ -101,6 +154,9 @@ Returns **Sharp**
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.
**Parameters**
- `options` **[Object][15]?**
@@ -109,6 +165,14 @@ Use these PNG options for output image.
- `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`)
**Examples**
```javascript
// Convert any input to PNG output
const data = await sharp(input)
.png()
.toBuffer();
```
- Throws **[Error][13]** Invalid options
@@ -127,6 +191,14 @@ Use these WebP options for output image.
- `options.nearLossless` **[Boolean][16]** use near_lossless compression mode (optional, default `false`)
- `options.force` **[Boolean][16]** force WebP output, otherwise attempt to use input format (optional, default `true`)
**Examples**
```javascript
// Convert any input to lossless WebP output
const data = await sharp(input)
.webp({ lossless: true })
.toBuffer();
```
- Throws **[Error][13]** Invalid options
@@ -141,12 +213,24 @@ Use these TIFF options for output image.
- `options` **[Object][15]?** output options
- `options.quality` **[Number][18]** quality, integer 1-100 (optional, default `80`)
- `options.force` **[Boolean][16]** 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][16]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
- `options.predictor` **[Boolean][16]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
- `options.xres` **[Number][18]** horizontal resolution in pixels/mm (optional, default `1.0`)
- `options.yres` **[Number][18]** vertical resolution in pixels/mm (optional, default `1.0`)
- `options.squash` **[Boolean][16]** squash 8-bit images down to 1 bit (optional, default `false`)
**Examples**
```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][13]** Invalid options
@@ -156,6 +240,15 @@ Returns **Sharp**
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**
## toFormat
@@ -167,6 +260,14 @@ Force output to a given format.
- `format` **([String][11] \| [Object][15])** as a String or an Object with an 'id' attribute
- `options` **[Object][15]** output options
**Examples**
```javascript
// Convert any input to PNG output
const data = await sharp(input)
.toFormat('png')
.toBuffer();
```
- Throws **[Error][13]** unsupported format or options
@@ -178,6 +279,8 @@ Use tile-based deep zoom (image pyramid) output.
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.
Warning: multiple sharp instances concurrently producing tile output can expose a possible race condition in some versions of libgsf.
**Parameters**
- `tile` **[Object][15]?**

View File

@@ -4,6 +4,43 @@
Requires libvips v8.6.1.
#### 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
* Add support for prebuilt sharp binaries on common platforms.

View File

@@ -14,7 +14,7 @@ As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
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.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
@@ -109,6 +109,9 @@ the help and code contributions of the following people:
* [Oleh Aleinyk](https://github.com/oaleynik)
* [Marcel Bretschneider](https://github.com/3epnm)
* [Andrea Bianco](https://github.com/BiancoA)
* [Rik Heywood](https://github.com/rikh42)
* [Thomas Parisot](https://github.com/oncletom)
* [Nathan Graves](https://github.com/woolite64)
Thank you!

View File

@@ -15,7 +15,7 @@ yarn add sharp
### Building from source
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.
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
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`
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.
### 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
* [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
If a global installation of libvips that meets the
minimum version requirement cannot be found,
this module will attempt to download a pre-compiled bundle of libvips
and its dependencies on Linux and Windows machines.
This module will attempt to download a pre-compiled bundle of libvips
and its dependencies on Linux and Windows machines under either of these
conditions:
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,
you can do so via https://github.com/lovell/sharp-libvips/releases

View File

@@ -18,8 +18,9 @@ const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
const distBaseUrl = process.env.SHARP_DIST_BASE_URL || `https://github.com/lovell/sharp-libvips/releases/download/v${minimumLibvipsVersion}/`;
try {
const useGlobalLibvips = libvips.useGlobalLibvips();
if (useGlobalLibvips) {
const globalLibvipsVersion = libvips.globalLibvipsVersion();
if (globalLibvipsVersion) {
npmLog.info('sharp', `Detected globally-installed libvips v${globalLibvipsVersion}`);
npmLog.info('sharp', 'Building from source via node-gyp');
process.exit(1);
@@ -28,6 +29,9 @@ try {
} else {
// Is this arch/platform supported?
const arch = process.env.npm_config_arch || process.arch;
if (platform() === 'win32-ia32') {
throw new Error('Windows x86 (32-bit) node.exe is not supported');
}
if (arch === 'ia32') {
throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}\n`);
}

View File

@@ -38,6 +38,21 @@ function background (rgba) {
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.
* 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
[
background,
tint,
greyscale,
grayscale,
toColourspace,

View File

@@ -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
* 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.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 {Number} [options.raw.width]
* @param {Number} [options.raw.height]
@@ -151,8 +152,11 @@ const Sharp = function (input, options) {
fastShrinkOnLoad: true,
// operations
background: [0, 0, 0, 255],
tintA: 128,
tintB: 128,
flatten: false,
negate: false,
medianSize: 0,
blurSigma: 0,
sharpenSigma: 0,
sharpenFlat: 1,

View File

@@ -57,6 +57,12 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
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
if (is.defined(inputOptions.create)) {
if (

View File

@@ -46,6 +46,10 @@ const pkgConfigPath = function () {
};
const useGlobalLibvips = function () {
if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
return false;
}
const globalVipsVersion = globalLibvipsVersion();
return !!globalVipsVersion && semver.gte(globalVipsVersion, minimumLibvipsVersion);
};

View File

@@ -156,6 +156,26 @@ function sharpen (sigma, flat, jagged) {
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.
* When used without parameters, performs a fast, mild blur of the output image.
@@ -444,6 +464,7 @@ module.exports = function (Sharp) {
flip,
flop,
sharpen,
median,
blur,
extend,
flatten,

View File

@@ -12,6 +12,16 @@ const sharp = require('../build/Release/sharp.node');
*
* 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 {Function} [callback] - called on completion with two arguments `(err, info)`.
* `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.
*
* @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 {Boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
* @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.
* 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.
*
* @example
* sharp('input.jpg')
* .withMetadata()
* .toFile('output-with-metadata.jpg')
* .then(info => { ... });
*
* @param {Object} [withMetadata]
* @param {Number} [withMetadata.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
* @returns {Sharp}
@@ -97,6 +130,16 @@ function withMetadata (withMetadata) {
/**
* 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 {Number} [options.quality=80] - quality, integer 1-100
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
@@ -148,6 +191,16 @@ function jpeg (options) {
/**
* 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 {Boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9
@@ -177,6 +230,13 @@ function png (options) {
/**
* 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 {Number} [options.quality=80] - quality, integer 1-100
* @param {Number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
@@ -212,10 +272,21 @@ function webp (options) {
/**
* 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 {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.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 {Number} [options.xres=1.0] - horizontal resolution in pixels/mm
* @param {Number} [options.yres=1.0] - vertical resolution in pixels/mm
@@ -255,10 +326,10 @@ function tiff (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;
} 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);
}
}
@@ -276,6 +347,13 @@ function tiff (options) {
/**
* 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}
*/
function raw () {
@@ -284,6 +362,13 @@ function raw () {
/**
* 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 {Object} options - output options
* @returns {Sharp}
@@ -305,6 +390,8 @@ function toFormat (format, options) {
* 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.
*
* Warning: multiple sharp instances concurrently producing tile output can expose a possible race condition in some versions of libgsf.
*
* @example
* sharp('input.tiff')
* .png()

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"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.3",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -45,7 +45,10 @@
"Kenric D'Souza <kenric.dsouza@gmail.com>",
"Oleh Aleinyk <oleg.aleynik@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>"
],
"scripts": {
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
@@ -79,28 +82,28 @@
"dependencies": {
"color": "^3.0.0",
"detect-libc": "^1.0.3",
"nan": "^2.9.2",
"fs-copy-file-sync": "^1.0.1",
"nan": "^2.10.0",
"fs-copy-file-sync": "^1.1.1",
"npmlog": "^4.1.2",
"prebuild-install": "^2.5.1",
"prebuild-install": "^4.0.0",
"semver": "^5.5.0",
"simple-get": "^2.7.0",
"tar": "^4.4.0",
"simple-get": "^2.8.1",
"tar": "^4.4.4",
"tunnel-agent": "^0.6.0"
},
"devDependencies": {
"async": "^2.6.0",
"cc": "^1.0.1",
"documentation": "^6.0.0",
"async": "^2.6.1",
"cc": "^1.0.2",
"decompress-zip": "^0.3.1",
"documentation": "^7.1.0",
"exif-reader": "^1.0.2",
"icc": "^1.0.0",
"mocha": "^5.0.1",
"nyc": "^11.5.0",
"prebuild": "^7.4.0",
"mocha": "^5.2.0",
"nyc": "^11.8.0",
"prebuild": "^7.6.0",
"prebuild-ci": "^2.2.3",
"rimraf": "^2.6.2",
"semistandard": "^12.0.1",
"unzip": "^0.1.11"
"semistandard": "^12.0.1"
},
"license": "Apache-2.0",
"config": {

View File

@@ -63,6 +63,10 @@ namespace sharp {
descriptor->rawWidth = AttrTo<uint32_t>(input, "rawWidth");
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
if (HasAttr(input, "createChannels")) {
descriptor->createChannels = AttrTo<uint32_t>(input, "createChannels");
@@ -229,6 +233,9 @@ namespace sharp {
if (imageType == ImageType::MAGICK) {
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);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
@@ -268,6 +275,9 @@ namespace sharp {
if (imageType == ImageType::MAGICK) {
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);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);

View File

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

View File

@@ -83,9 +83,9 @@ class MetadataWorker : public Nan::AsyncWorker {
baton->iccLength = iccLength;
}
// 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;
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));
memcpy(baton->iptc, iptc, iptcLength);
baton->iptcLength = iptcLength;

View File

@@ -152,6 +152,33 @@ namespace sharp {
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.
*/

View File

@@ -46,6 +46,11 @@ namespace sharp {
*/
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.
*/

View File

@@ -359,6 +359,8 @@ class PipelineWorker : public Nan::AsyncWorker {
bool const shouldBlur = baton->blurSigma != 0.0;
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
bool const shouldSharpen = baton->sharpenSigma != 0.0;
bool const shouldApplyMedian = baton->medianSize > 0;
bool const shouldPremultiplyAlpha = HasAlpha(image) &&
(shouldResize || shouldBlur || shouldConv || shouldSharpen || shouldOverlayWithAlpha);
@@ -482,6 +484,12 @@ class PipelineWorker : public Nan::AsyncWorker {
image = image.extract_area(left, top, width, height);
} else {
// 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()
->set("access", baton->accessMethod)
->set("threaded", TRUE));
@@ -538,7 +546,10 @@ class PipelineWorker : public Nan::AsyncWorker {
image = image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
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
if (baton->threshold != 0) {
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
@@ -671,6 +682,11 @@ class PipelineWorker : public Nan::AsyncWorker {
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)
if (baton->extractChannel > -1) {
if (baton->extractChannel >= image.bands()) {
@@ -1156,6 +1172,9 @@ NAN_METHOD(pipeline) {
for (unsigned int i = 0; i < 4; i++) {
baton->background[i] = AttrTo<double>(background, i);
}
// Tint chroma
baton->tintA = AttrTo<double>(options, "tintA");
baton->tintB = AttrTo<double>(options, "tintB");
// Overlay options
if (HasAttr(options, "overlay")) {
baton->overlay = CreateInputDescriptor(AttrAs<v8::Object>(options, "overlay"), buffersToPersist);
@@ -1188,6 +1207,7 @@ NAN_METHOD(pipeline) {
baton->flatten = AttrTo<bool>(options, "flatten");
baton->negate = AttrTo<bool>(options, "negate");
baton->blurSigma = AttrTo<double>(options, "blurSigma");
baton->medianSize = AttrTo<uint32_t>(options, "medianSize");
baton->sharpenSigma = AttrTo<double>(options, "sharpenSigma");
baton->sharpenFlat = AttrTo<double>(options, "sharpenFlat");
baton->sharpenJagged = AttrTo<double>(options, "sharpenJagged");

View File

@@ -70,9 +70,12 @@ struct PipelineBaton {
std::string kernel;
bool fastShrinkOnLoad;
double background[4];
double tintA;
double tintB;
bool flatten;
bool negate;
double blurSigma;
int medianSize;
double sharpenSigma;
double sharpenFlat;
double sharpenJagged;
@@ -154,9 +157,12 @@ struct PipelineBaton {
cropOffsetLeft(0),
cropOffsetTop(0),
premultiplied(false),
tintA(128.0),
tintB(128.0),
flatten(false),
negate(false),
blurSigma(0.0),
medianSize(0),
sharpenSigma(0.0),
sharpenFlat(1.0),
sharpenJagged(2.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.

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'),
inputJpgTruncated: getPath('truncated.jpg'), // head -c 10000 2569067123_aca715a2ee_o.jpg > truncated.jpg
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
inputPngWithTransparency: getPath('blackbug.png'), // public domain
@@ -87,10 +89,12 @@ module.exports = {
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
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
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
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
inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif
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

@@ -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)
.resize(1600, 1200)
.toBuffer()
@@ -177,6 +177,25 @@ describe('Crop', function () {
});
});
it('Clamp before crop when one post-resize dimension is below target', function () {
return sharp(fixtures.inputJpg)
.resize(1024, 1034)
.toBuffer()
.then(function (input) {
return sharp(input)
.rotate(270)
.resize(256)
.crop(sharp.strategy.entropy)
.toBuffer({ resolveWithObject: true })
.then(function (result) {
assert.strictEqual(256, result.info.width);
assert.strictEqual(253, result.info.height);
assert.strictEqual(0, result.info.cropOffsetLeft);
assert.strictEqual(0, result.info.cropOffsetTop);
});
});
});
describe('Entropy-based strategy', function () {
it('JPEG', function (done) {
sharp(fixtures.inputJpg)

View File

@@ -528,6 +528,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 () {
it('Match JPEG input', function (done) {
sharp(fixtures.inputJpg)
@@ -880,6 +896,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) {
sharp(fixtures.inputTiff)
.resize(320, 240)
@@ -1020,6 +1083,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) {
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
sharp(fixtures.inputTiffUncompressed)

View File

@@ -58,5 +58,13 @@ describe('libvips binaries', function () {
const hasVendoredLibvips = libvips.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;
});
});
});

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

@@ -6,7 +6,7 @@ const assert = require('assert');
const eachLimit = require('async/eachLimit');
const rimraf = require('rimraf');
const unzip = require('unzip');
const DecompressZip = require('decompress-zip');
const sharp = require('../../');
const fixtures = require('../fixtures');
@@ -427,16 +427,14 @@ describe('Tile', function () {
if (err) throw err;
assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0);
fs.createReadStream(container)
.pipe(unzip.Extract({
path: path.dirname(extractTo)
}))
new DecompressZip(container)
.on('extract', function () {
assertDeepZoomTiles(directory, 256, 13, done);
})
.on('error', function (err) {
throw err;
})
.on('close', function () {
assertDeepZoomTiles(directory, 256, 13, done);
});
.extract({ path: path.dirname(extractTo) });
});
});
});
@@ -463,16 +461,14 @@ describe('Tile', function () {
if (err) throw err;
assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0);
fs.createReadStream(container)
.pipe(unzip.Extract({
path: path.dirname(extractTo)
}))
new DecompressZip(container)
.on('extract', function () {
assertDeepZoomTiles(directory, 256, 13, done);
})
.on('error', function (err) {
throw err;
})
.on('close', function () {
assertDeepZoomTiles(directory, 256, 13, done);
});
.extract({ path: path.dirname(extractTo) });
});
});
});

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();
});
});
});