Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05d76eeadf | ||
|
|
28a6c53da0 | ||
|
|
6fcd2153c5 | ||
|
|
7ae0512b9b | ||
|
|
0890b59c32 | ||
|
|
df3ce450d9 | ||
|
|
bb0257b318 | ||
|
|
9c3597670d | ||
|
|
aa9b328778 | ||
|
|
159e8dace2 | ||
|
|
3be4d5bb45 | ||
|
|
af7caa7b25 | ||
|
|
b4ede75522 | ||
|
|
9d98114074 | ||
|
|
4ac51899c3 | ||
|
|
90a0382317 | ||
|
|
687795c801 | ||
|
|
2e0fbbb942 | ||
|
|
0a3512d066 | ||
|
|
6032171f91 | ||
|
|
fc178de309 | ||
|
|
3f4398457f | ||
|
|
b494b2e872 |
19
CONTRIBUTING.md → .github/CONTRIBUTING.md
vendored
@@ -12,9 +12,12 @@ New bugs are assigned a `triage` label whilst under investigation.
|
|||||||
|
|
||||||
## Submit a new feature request
|
## Submit a new feature request
|
||||||
|
|
||||||
If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exists, it's probably fastest to add a comment to it about your requirement.
|
If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exists,
|
||||||
|
it's probably fastest to add a comment to it about your requirement.
|
||||||
|
|
||||||
Implementation is usually straightforward if _libvips_ [already supports](https://libvips.github.io/libvips/API/current/) the feature you need.
|
Implementation is usually straightforward if libvips
|
||||||
|
[already supports](https://libvips.github.io/libvips/API/current/func-list.html)
|
||||||
|
the feature you need.
|
||||||
|
|
||||||
## Submit a Pull Request to fix a bug
|
## Submit a Pull Request to fix a bug
|
||||||
|
|
||||||
@@ -41,18 +44,18 @@ Any change that modifies the existing public API should be added to the relevant
|
|||||||
|
|
||||||
| Release | WIP branch |
|
| Release | WIP branch |
|
||||||
| ------: | :--------- |
|
| ------: | :--------- |
|
||||||
| v0.22.0 | uptake |
|
|
||||||
| v0.23.0 | vision |
|
| v0.23.0 | vision |
|
||||||
|
| v0.24.0 | wit |
|
||||||
|
|
||||||
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.
|
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.
|
||||||
|
|
||||||
### Add a new public method
|
### Add a new public method
|
||||||
|
|
||||||
The API tries to be as fluent as possible. Image processing concepts follow the naming conventions from _libvips_ and, to a lesser extent, _ImageMagick_.
|
The API tries to be as fluent as possible.
|
||||||
|
Image processing concepts follow the naming conventions from libvips and, to a lesser extent, ImageMagick.
|
||||||
|
|
||||||
Most methods have optional parameters and assume sensible defaults. Methods with mandatory parameters often have names like `doSomethingWith(X)`.
|
Most methods have optional parameters and assume sensible defaults.
|
||||||
|
Please ensure backwards compatibility where possible.
|
||||||
Please ensure backwards compatibility where possible. Methods to modify previously default behaviour often have names like `withoutOptionY()` or `withExtraZ()`.
|
|
||||||
|
|
||||||
Feel free to create a [new issue](https://github.com/lovell/sharp/issues/new) to gather feedback on a potential API change.
|
Feel free to create a [new issue](https://github.com/lovell/sharp/issues/new) to gather feedback on a potential API change.
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@ Feel free to create a [new issue](https://github.com/lovell/sharp/issues/new) to
|
|||||||
|
|
||||||
A method to be removed should be deprecated in the next major version then removed in the following major version.
|
A method to be removed should be deprecated in the next major version then removed in the following major version.
|
||||||
|
|
||||||
By way of example, the [bilinearInterpolation method](https://github.com/lovell/sharp/blob/v0.6.0/index.js#L155) present in v0.5.0 was deprecated in v0.6.0 and removed in v0.7.0.
|
By way of example, the `background()` method present in v0.20.0 was deprecated in v0.21.0 and removed in v0.22.0.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
18
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
What are you trying to achieve?
|
||||||
|
|
||||||
|
Have you searched for similar feature requests?
|
||||||
|
|
||||||
|
What would you expect the API to look like?
|
||||||
|
|
||||||
|
What alternatives have you considered?
|
||||||
|
|
||||||
|
Is there a sample image that helps explain?
|
||||||
14
.github/ISSUE_TEMPLATE/installation.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: Installation
|
||||||
|
about: For help if something went wrong installing sharp
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
What is the output of running `npm install --verbose sharp`?
|
||||||
|
|
||||||
|
What is the output of running `npx envinfo --binaries --languages --system --utilities`?
|
||||||
|
|
||||||
|
Have you ensured the platform and version of Node.js used for `npm install` is the same as the platform and version of Node.js used at runtime?
|
||||||
18
.github/ISSUE_TEMPLATE/possible-bug.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: Possible bug
|
||||||
|
about: Please provide steps to reproduce
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
What is the output of running `npx envinfo --binaries --languages --system --utilities`?
|
||||||
|
|
||||||
|
What are the steps to reproduce?
|
||||||
|
|
||||||
|
What is the expected behaviour?
|
||||||
|
|
||||||
|
Are you able to provide a standalone code sample, without other dependencies, that demonstrates this problem?
|
||||||
|
|
||||||
|
Are you able to provide a sample image that helps explain the problem?
|
||||||
16
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: Question
|
||||||
|
about: For help with an existing feature
|
||||||
|
title: ''
|
||||||
|
labels: question
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
What are you trying to achieve?
|
||||||
|
|
||||||
|
Have you searched for similar questions?
|
||||||
|
|
||||||
|
Are you able to provide a standalone code sample that demonstrates this question?
|
||||||
|
|
||||||
|
Are you able to provide a sample image that helps explain the question?
|
||||||
@@ -12,4 +12,4 @@ docs/css/
|
|||||||
vendor
|
vendor
|
||||||
.prebuildrc
|
.prebuildrc
|
||||||
.nyc_output
|
.nyc_output
|
||||||
CONTRIBUTING.md
|
.github/
|
||||||
|
|||||||
21
.travis.yml
@@ -18,6 +18,12 @@ matrix:
|
|||||||
sudo: false
|
sudo: false
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: "10"
|
node_js: "10"
|
||||||
|
- name: "Linux (glibc) - Node 12"
|
||||||
|
os: linux
|
||||||
|
dist: trusty
|
||||||
|
sudo: false
|
||||||
|
language: node_js
|
||||||
|
node_js: "12"
|
||||||
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
|
||||||
@@ -57,6 +63,16 @@ matrix:
|
|||||||
- sudo docker exec sharp apk add build-base git python2 --update-cache
|
- sudo docker exec sharp apk add build-base git python2 --update-cache
|
||||||
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
script: sudo docker exec sharp sh -c "npm test"
|
||||||
|
- name: "Linux (musl) - Node 12"
|
||||||
|
os: linux
|
||||||
|
dist: trusty
|
||||||
|
sudo: true
|
||||||
|
language: minimal
|
||||||
|
before_install:
|
||||||
|
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12.0-alpine
|
||||||
|
- sudo docker exec sharp apk add build-base git python2 --update-cache
|
||||||
|
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
||||||
|
script: sudo docker exec sharp sh -c "npm test"
|
||||||
- name: "OS X - Node 6"
|
- name: "OS X - Node 6"
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode9.2
|
osx_image: xcode9.2
|
||||||
@@ -77,3 +93,8 @@ matrix:
|
|||||||
osx_image: xcode9.2
|
osx_image: xcode9.2
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: "11"
|
node_js: "11"
|
||||||
|
- name: "OS X - Node 12"
|
||||||
|
os: osx
|
||||||
|
osx_image: xcode9.2
|
||||||
|
language: node_js
|
||||||
|
node_js: "12"
|
||||||
|
|||||||
@@ -20,7 +20,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 systems running
|
Most modern 64-bit OS X, Windows and Linux systems running
|
||||||
Node versions 6, 8, 10 and 11
|
Node versions 6, 8, 10, 11 and 12
|
||||||
do not require any additional install or runtime dependencies.
|
do not require any additional install or runtime dependencies.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
@@ -96,7 +96,7 @@ Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
|
|||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
A [guide for contributors](https://github.com/lovell/sharp/blob/master/CONTRIBUTING.md)
|
A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md)
|
||||||
covers reporting bugs, requesting features and submitting code changes.
|
covers reporting bugs, requesting features and submitting code changes.
|
||||||
|
|
||||||
### Licensing
|
### Licensing
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ environment:
|
|||||||
- nodejs_version: "8"
|
- nodejs_version: "8"
|
||||||
- nodejs_version: "10"
|
- nodejs_version: "10"
|
||||||
- nodejs_version: "11"
|
- nodejs_version: "11"
|
||||||
|
- nodejs_version: "12"
|
||||||
install:
|
install:
|
||||||
- ps: Install-Product node $env:nodejs_version x64
|
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64
|
||||||
- npm install -g npm@5
|
- npm install -g npm@6
|
||||||
- npm install
|
- npm install
|
||||||
test_script:
|
test_script:
|
||||||
- npm test
|
- npm test
|
||||||
|
|||||||
@@ -20,22 +20,22 @@ and [https://www.cairographics.org/operators/][2]
|
|||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `images` **[Array][3]<[Object][4]>** Ordered list of images to composite
|
- `images` **[Array][3]<[Object][4]>** Ordered list of images to composite
|
||||||
- `images[].input` **([Buffer][5] \| [String][6])?** Buffer containing image data or String containing the path to an image file.
|
- `images[].input` **([Buffer][5] \| [String][6])?** Buffer containing image data, String containing the path to an image file, or Create object (see bellow)
|
||||||
|
- `images[].input.create` **[Object][4]?** describes a blank overlay to be created.
|
||||||
|
- `images[].input.create.width` **[Number][7]?**
|
||||||
|
- `images[].input.create.height` **[Number][7]?**
|
||||||
|
- `images[].input.create.channels` **[Number][7]?** 3-4
|
||||||
|
- `images[].input.create.background` **([String][6] \| [Object][4])?** parsed by the [color][8] module to extract values for red, green, blue and alpha.
|
||||||
- `images[].blend` **[String][6]** how to blend this image with the image below. (optional, default `'over'`)
|
- `images[].blend` **[String][6]** how to blend this image with the image below. (optional, default `'over'`)
|
||||||
- `images[].gravity` **[String][6]** gravity at which to place the overlay. (optional, default `'centre'`)
|
- `images[].gravity` **[String][6]** gravity at which to place the overlay. (optional, default `'centre'`)
|
||||||
- `images[].top` **[Number][7]?** the pixel offset from the top edge.
|
- `images[].top` **[Number][7]?** the pixel offset from the top edge.
|
||||||
- `images[].left` **[Number][7]?** the pixel offset from the left edge.
|
- `images[].left` **[Number][7]?** the pixel offset from the left edge.
|
||||||
- `images[].tile` **[Boolean][8]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
|
- `images[].tile` **[Boolean][9]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
|
||||||
- `images[].density` **[Number][7]** number representing the DPI for vector overlay image. (optional, default `72`)
|
- `images[].density` **[Number][7]** number representing the DPI for vector overlay image. (optional, default `72`)
|
||||||
- `images[].raw` **[Object][4]?** describes overlay when using raw pixel data.
|
- `images[].raw` **[Object][4]?** describes overlay when using raw pixel data.
|
||||||
- `images[].raw.width` **[Number][7]?**
|
- `images[].raw.width` **[Number][7]?**
|
||||||
- `images[].raw.height` **[Number][7]?**
|
- `images[].raw.height` **[Number][7]?**
|
||||||
- `images[].raw.channels` **[Number][7]?**
|
- `images[].raw.channels` **[Number][7]?**
|
||||||
- `images[].create` **[Object][4]?** describes a blank overlay to be created.
|
|
||||||
- `images[].create.width` **[Number][7]?**
|
|
||||||
- `images[].create.height` **[Number][7]?**
|
|
||||||
- `images[].create.channels` **[Number][7]?** 3-4
|
|
||||||
- `images[].create.background` **([String][6] \| [Object][4])?** parsed by the [color][9] module to extract values for red, green, blue and alpha.
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -74,8 +74,8 @@ Returns **Sharp**
|
|||||||
|
|
||||||
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||||
|
|
||||||
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
[8]: https://www.npmjs.org/package/color
|
||||||
|
|
||||||
[9]: https://www.npmjs.org/package/color
|
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||||
|
|
||||||
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||||
|
|||||||
@@ -287,6 +287,41 @@ sharp(input)
|
|||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
|
## modulate
|
||||||
|
|
||||||
|
Transforms the image using brightness, saturation and hue rotation.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- `options` **[Object][2]?**
|
||||||
|
- `options.brightness` **[Number][1]?** Brightness multiplier
|
||||||
|
- `options.saturation` **[Number][1]?** Saturation multiplier
|
||||||
|
- `options.hue` **[Number][1]?** Degrees for hue rotation
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharp(input)
|
||||||
|
.modulate({
|
||||||
|
brightness: 2 // increase lightness by a factor of 2
|
||||||
|
});
|
||||||
|
|
||||||
|
sharp(input)
|
||||||
|
.modulate({
|
||||||
|
hue: 180 // hue-rotate by 180 degrees
|
||||||
|
});
|
||||||
|
|
||||||
|
// decreate brightness and saturation while also hue-rotating by 90 degrees
|
||||||
|
sharp(input)
|
||||||
|
.modulate({
|
||||||
|
brightness: 0.5,
|
||||||
|
saturation: 0.5,
|
||||||
|
hue: 90
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns **Sharp**
|
||||||
|
|
||||||
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||||
|
|
||||||
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||||
|
|||||||
@@ -115,13 +115,13 @@ Use these JPEG options for output image.
|
|||||||
- `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
|
- `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
|
||||||
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
||||||
- `options.chromaSubsampling` **[String][1]** set to '4:4:4' to prevent chroma subsampling when quality <= 90 (optional, default `'4:2:0'`)
|
- `options.chromaSubsampling` **[String][1]** set to '4:4:4' to prevent chroma subsampling when quality <= 90 (optional, default `'4:2:0'`)
|
||||||
- `options.trellisQuantisation` **[Boolean][6]** apply trellis quantisation, requires mozjpeg (optional, default `false`)
|
- `options.trellisQuantisation` **[Boolean][6]** apply trellis quantisation, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
||||||
- `options.overshootDeringing` **[Boolean][6]** apply overshoot deringing, requires mozjpeg (optional, default `false`)
|
- `options.overshootDeringing` **[Boolean][6]** apply overshoot deringing, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
||||||
- `options.optimiseScans` **[Boolean][6]** optimise progressive scans, forces progressive, requires mozjpeg (optional, default `false`)
|
- `options.optimiseScans` **[Boolean][6]** optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
||||||
- `options.optimizeScans` **[Boolean][6]** alternative spelling of optimiseScans (optional, default `false`)
|
- `options.optimizeScans` **[Boolean][6]** alternative spelling of optimiseScans (optional, default `false`)
|
||||||
- `options.optimiseCoding` **[Boolean][6]** optimise Huffman coding tables (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.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.quantisationTable` **[Number][8]** quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg (optional, default `0`)
|
||||||
- `options.quantizationTable` **[Number][8]** alternative spelling of quantisationTable (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`)
|
- `options.force` **[Boolean][6]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
@@ -154,11 +154,11 @@ Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
|||||||
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
||||||
- `options.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
|
- `options.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
|
||||||
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
|
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
|
||||||
- `options.palette` **[Boolean][6]** quantise to a palette-based image with alpha transparency support, requires libimagequant (optional, default `false`)
|
- `options.palette` **[Boolean][6]** quantise to a palette-based image with alpha transparency support, requires libvips compiled with support for libimagequant (optional, default `false`)
|
||||||
- `options.quality` **[Number][8]** use the lowest number of colours needed to achieve given quality, requires libimagequant (optional, default `100`)
|
- `options.quality` **[Number][8]** use the lowest number of colours needed to achieve given quality, requires libvips compiled with support for libimagequant (optional, default `100`)
|
||||||
- `options.colours` **[Number][8]** maximum number of palette entries, requires libimagequant (optional, default `256`)
|
- `options.colours` **[Number][8]** maximum number of palette entries, requires libvips compiled with support for libimagequant (optional, default `256`)
|
||||||
- `options.colors` **[Number][8]** alternative spelling of `options.colours`, requires libimagequant (optional, default `256`)
|
- `options.colors` **[Number][8]** alternative spelling of `options.colours`, requires libvips compiled with support for libimagequant (optional, default `256`)
|
||||||
- `options.dither` **[Number][8]** level of Floyd-Steinberg error diffusion, requires libimagequant (optional, default `1.0`)
|
- `options.dither` **[Number][8]** level of Floyd-Steinberg error diffusion, requires libvips compiled with support for libimagequant (optional, default `1.0`)
|
||||||
- `options.force` **[Boolean][6]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
- `options.force` **[Boolean][6]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|||||||
@@ -163,11 +163,11 @@ Extract a region of the image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][9]**
|
- `options` **[Object][9]** describes the region to extract using integral pixel values
|
||||||
- `options.left` **[Number][8]** zero-indexed offset from left edge
|
- `options.left` **[Number][8]** zero-indexed offset from left edge
|
||||||
- `options.top` **[Number][8]** zero-indexed offset from top edge
|
- `options.top` **[Number][8]** zero-indexed offset from top edge
|
||||||
- `options.width` **[Number][8]** dimension of extracted image
|
- `options.width` **[Number][8]** width of region to extract
|
||||||
- `options.height` **[Number][8]** dimension of extracted image
|
- `options.height` **[Number][8]** height of region to extract
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,19 @@
|
|||||||
|
|
||||||
Requires libvips v8.7.4.
|
Requires libvips v8.7.4.
|
||||||
|
|
||||||
|
#### v0.22.1 - 25<sup>th</sup> April 2019
|
||||||
|
|
||||||
|
* Add `modulate` operation for brightness, saturation and hue.
|
||||||
|
[#1601](https://github.com/lovell/sharp/pull/1601)
|
||||||
|
[@Goues](https://github.com/Goues)
|
||||||
|
|
||||||
|
* Improve help messaging should `require("sharp")` fail.
|
||||||
|
[#1638](https://github.com/lovell/sharp/pull/1638)
|
||||||
|
[@sidharthachatterjee](https://github.com/sidharthachatterjee)
|
||||||
|
|
||||||
|
* Add support for Node 12.
|
||||||
|
[#1668](https://github.com/lovell/sharp/issues/1668)
|
||||||
|
|
||||||
#### v0.22.0 - 18<sup>th</sup> March 2019
|
#### v0.22.0 - 18<sup>th</sup> March 2019
|
||||||
|
|
||||||
* Remove functions previously deprecated in v0.21.0:
|
* Remove functions previously deprecated in v0.21.0:
|
||||||
|
|||||||
@@ -16,7 +16,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 systems running
|
Most modern 64-bit OS X, Windows and Linux systems running
|
||||||
Node versions 6, 8 and 10
|
Node versions 6, 8, 10, 11 and 12
|
||||||
do not require any additional install or runtime dependencies.
|
do not require any additional install or runtime dependencies.
|
||||||
|
|
||||||
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
||||||
@@ -64,7 +64,7 @@ as [pngcrush](https://pmt.sourceforge.io/pngcrush/).
|
|||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
A [guide for contributors](https://github.com/lovell/sharp/blob/master/CONTRIBUTING.md)
|
A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md)
|
||||||
covers reporting bugs, requesting features and submitting code changes.
|
covers reporting bugs, requesting features and submitting code changes.
|
||||||
|
|
||||||
### Credits
|
### Credits
|
||||||
@@ -124,6 +124,7 @@ the help and code contributions of the following people:
|
|||||||
* [Julian Aubourg](https://github.com/jaubourg)
|
* [Julian Aubourg](https://github.com/jaubourg)
|
||||||
* [Keith Belovay](https://github.com/fromkeith)
|
* [Keith Belovay](https://github.com/fromkeith)
|
||||||
* [Michael B. Klein](https://github.com/mbklein)
|
* [Michael B. Klein](https://github.com/mbklein)
|
||||||
|
* [Jakub Michálek](https://github.com/Goues)
|
||||||
|
|
||||||
Thank you!
|
Thank you!
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ yarn add sharp
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
* Node v4.5.0+
|
* Node.js v6+
|
||||||
|
|
||||||
### 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 6, 8, 10 and 11 on
|
Node versions 6, 8, 10, 11 and 12 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:
|
||||||
@@ -43,7 +43,7 @@ Most Linux-based (glibc, musl) operating systems running on x64 and ARMv6+ CPUs
|
|||||||
* Debian 7+
|
* Debian 7+
|
||||||
* Ubuntu 14.04+
|
* Ubuntu 14.04+
|
||||||
* Centos 7+
|
* Centos 7+
|
||||||
* Alpine 3.8+ (Node 8 and 10)
|
* Alpine 3.8+ (Node 8+)
|
||||||
* Fedora
|
* Fedora
|
||||||
* openSUSE 13.2+
|
* openSUSE 13.2+
|
||||||
* Archlinux
|
* Archlinux
|
||||||
|
|||||||
@@ -72,7 +72,12 @@ const blend = {
|
|||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* @param {Object[]} images - Ordered list of images to composite
|
* @param {Object[]} images - Ordered list of images to composite
|
||||||
* @param {Buffer|String} [images[].input] - Buffer containing image data or String containing the path to an image file.
|
* @param {Buffer|String} [images[].input] - Buffer containing image data, String containing the path to an image file, or Create object (see bellow)
|
||||||
|
* @param {Object} [images[].input.create] - describes a blank overlay to be created.
|
||||||
|
* @param {Number} [images[].input.create.width]
|
||||||
|
* @param {Number} [images[].input.create.height]
|
||||||
|
* @param {Number} [images[].input.create.channels] - 3-4
|
||||||
|
* @param {String|Object} [images[].input.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||||
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
|
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
|
||||||
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
|
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
|
||||||
* @param {Number} [images[].top] - the pixel offset from the top edge.
|
* @param {Number} [images[].top] - the pixel offset from the top edge.
|
||||||
@@ -83,11 +88,6 @@ const blend = {
|
|||||||
* @param {Number} [images[].raw.width]
|
* @param {Number} [images[].raw.width]
|
||||||
* @param {Number} [images[].raw.height]
|
* @param {Number} [images[].raw.height]
|
||||||
* @param {Number} [images[].raw.channels]
|
* @param {Number} [images[].raw.channels]
|
||||||
* @param {Object} [images[].create] - describes a blank overlay to be created.
|
|
||||||
* @param {Number} [images[].create.width]
|
|
||||||
* @param {Number} [images[].create.height]
|
|
||||||
* @param {Number} [images[].create.channels] - 3-4
|
|
||||||
* @param {String|Object} [images[].create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,7 +7,27 @@ const events = require('events');
|
|||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
|
|
||||||
require('./libvips').hasVendoredLibvips();
|
require('./libvips').hasVendoredLibvips();
|
||||||
const sharp = require('bindings')('sharp.node');
|
|
||||||
|
let sharp;
|
||||||
|
try {
|
||||||
|
sharp = require('../build/Release/sharp.node');
|
||||||
|
} catch (err) {
|
||||||
|
// Bail early if bindings aren't available
|
||||||
|
const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, ''];
|
||||||
|
if (/NODE_MODULE_VERSION/.test(err.message)) {
|
||||||
|
help.push('- Ensure the version of Node.js used at install time matches that used at runtime');
|
||||||
|
} else if (/invalid ELF header/.test(err.message)) {
|
||||||
|
help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
|
||||||
|
} else {
|
||||||
|
help.push('- Remove the "node_modules/sharp" directory, run "npm install" and look for errors');
|
||||||
|
}
|
||||||
|
help.push(
|
||||||
|
'- Consult the installation documentation at https://sharp.pixelplumbing.com/en/stable/install/',
|
||||||
|
'- Search for this error at https://github.com/lovell/sharp/issues', ''
|
||||||
|
);
|
||||||
|
console.error(help.join('\n'));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
// Use NODE_DEBUG=sharp to enable libvips warnings
|
// Use NODE_DEBUG=sharp to enable libvips warnings
|
||||||
const debuglog = util.debuglog('sharp');
|
const debuglog = util.debuglog('sharp');
|
||||||
@@ -139,6 +159,9 @@ const Sharp = function (input, options) {
|
|||||||
gammaOut: 0,
|
gammaOut: 0,
|
||||||
greyscale: false,
|
greyscale: false,
|
||||||
normalise: 0,
|
normalise: 0,
|
||||||
|
brightness: 1,
|
||||||
|
saturation: 1,
|
||||||
|
hue: 0,
|
||||||
booleanBufferIn: null,
|
booleanBufferIn: null,
|
||||||
booleanFileIn: '',
|
booleanFileIn: '',
|
||||||
joinChannelIn: [],
|
joinChannelIn: [],
|
||||||
|
|||||||
@@ -415,6 +415,62 @@ function recomb (inputMatrix) {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the image using brightness, saturation and hue rotation.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* sharp(input)
|
||||||
|
* .modulate({
|
||||||
|
* brightness: 2 // increase lightness by a factor of 2
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* sharp(input)
|
||||||
|
* .modulate({
|
||||||
|
* hue: 180 // hue-rotate by 180 degrees
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // decreate brightness and saturation while also hue-rotating by 90 degrees
|
||||||
|
* sharp(input)
|
||||||
|
* .modulate({
|
||||||
|
* brightness: 0.5,
|
||||||
|
* saturation: 0.5,
|
||||||
|
* hue: 90
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @param {Object} [options]
|
||||||
|
* @param {Number} [options.brightness] Brightness multiplier
|
||||||
|
* @param {Number} [options.saturation] Saturation multiplier
|
||||||
|
* @param {Number} [options.hue] Degrees for hue rotation
|
||||||
|
* @returns {Sharp}
|
||||||
|
*/
|
||||||
|
function modulate (options) {
|
||||||
|
if (!is.plainObject(options)) {
|
||||||
|
throw is.invalidParameterError('options', 'plain object', options);
|
||||||
|
}
|
||||||
|
if ('brightness' in options) {
|
||||||
|
if (is.number(options.brightness) && options.brightness >= 0) {
|
||||||
|
this.options.brightness = options.brightness;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('brightness', 'number above zero', options.brightness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ('saturation' in options) {
|
||||||
|
if (is.number(options.saturation) && options.saturation >= 0) {
|
||||||
|
this.options.saturation = options.saturation;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('saturation', 'number above zero', options.saturation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ('hue' in options) {
|
||||||
|
if (is.integer(options.hue)) {
|
||||||
|
this.options.hue = options.hue % 360;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('hue', 'number', options.hue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorate the Sharp prototype with operation-related functions.
|
* Decorate the Sharp prototype with operation-related functions.
|
||||||
* @private
|
* @private
|
||||||
@@ -436,6 +492,7 @@ module.exports = function (Sharp) {
|
|||||||
threshold,
|
threshold,
|
||||||
boolean,
|
boolean,
|
||||||
linear,
|
linear,
|
||||||
recomb
|
recomb,
|
||||||
|
modulate
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -144,13 +144,13 @@ function withMetadata (withMetadata) {
|
|||||||
* @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
|
||||||
* @param {String} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling when quality <= 90
|
* @param {String} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling when quality <= 90
|
||||||
* @param {Boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires mozjpeg
|
* @param {Boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires libvips compiled with support for mozjpeg
|
||||||
* @param {Boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires mozjpeg
|
* @param {Boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires libvips compiled with support for mozjpeg
|
||||||
* @param {Boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires mozjpeg
|
* @param {Boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires libvips compiled with support for 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.optimiseCoding=true] - optimise Huffman coding tables
|
||||||
* @param {Boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
|
* @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.quantisationTable=0] - quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg
|
||||||
* @param {Number} [options.quantizationTable=0] - alternative spelling of quantisationTable
|
* @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}
|
||||||
@@ -221,11 +221,11 @@ function jpeg (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
|
||||||
* @param {Boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
|
* @param {Boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
|
||||||
* @param {Boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libimagequant
|
* @param {Boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libvips compiled with support for libimagequant
|
||||||
* @param {Number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, requires libimagequant
|
* @param {Number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, requires libvips compiled with support for libimagequant
|
||||||
* @param {Number} [options.colours=256] - maximum number of palette entries, requires libimagequant
|
* @param {Number} [options.colours=256] - maximum number of palette entries, requires libvips compiled with support for libimagequant
|
||||||
* @param {Number} [options.colors=256] - alternative spelling of `options.colours`, requires libimagequant
|
* @param {Number} [options.colors=256] - alternative spelling of `options.colours`, requires libvips compiled with support for libimagequant
|
||||||
* @param {Number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, requires libimagequant
|
* @param {Number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, requires libvips compiled with support for libimagequant
|
||||||
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
|
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
|
|||||||
@@ -334,11 +334,11 @@ function extend (extend) {
|
|||||||
* // Extract a region, resize, then extract from the resized image
|
* // Extract a region, resize, then extract from the resized image
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* @param {Object} options
|
* @param {Object} options - describes the region to extract using integral pixel values
|
||||||
* @param {Number} options.left - zero-indexed offset from left edge
|
* @param {Number} options.left - zero-indexed offset from left edge
|
||||||
* @param {Number} options.top - zero-indexed offset from top edge
|
* @param {Number} options.top - zero-indexed offset from top edge
|
||||||
* @param {Number} options.width - dimension of extracted image
|
* @param {Number} options.width - width of region to extract
|
||||||
* @param {Number} options.height - dimension of extracted image
|
* @param {Number} options.height - height of region to extract
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
|
|||||||
23
package.json
@@ -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.22.0",
|
"version": "0.22.1",
|
||||||
"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": [
|
||||||
@@ -93,14 +93,13 @@
|
|||||||
"vips"
|
"vips"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bindings": "^1.5.0",
|
"color": "^3.1.1",
|
||||||
"color": "^3.1.0",
|
|
||||||
"detect-libc": "^1.0.3",
|
"detect-libc": "^1.0.3",
|
||||||
"fs-copy-file-sync": "^1.1.1",
|
"fs-copy-file-sync": "^1.1.1",
|
||||||
"nan": "^2.13.1",
|
"nan": "^2.13.2",
|
||||||
"npmlog": "^4.1.2",
|
"npmlog": "^4.1.2",
|
||||||
"prebuild-install": "^5.2.5",
|
"prebuild-install": "^5.3.0",
|
||||||
"semver": "^5.6.0",
|
"semver": "^6.0.0",
|
||||||
"simple-get": "^3.0.3",
|
"simple-get": "^3.0.3",
|
||||||
"tar": "^4.4.8",
|
"tar": "^4.4.8",
|
||||||
"tunnel-agent": "^0.6.0"
|
"tunnel-agent": "^0.6.0"
|
||||||
@@ -109,15 +108,15 @@
|
|||||||
"async": "^2.6.2",
|
"async": "^2.6.2",
|
||||||
"cc": "^1.0.2",
|
"cc": "^1.0.2",
|
||||||
"decompress-zip": "^0.3.2",
|
"decompress-zip": "^0.3.2",
|
||||||
"documentation": "^9.3.1",
|
"documentation": "^10.0.0",
|
||||||
"exif-reader": "^1.0.2",
|
"exif-reader": "^1.0.2",
|
||||||
"icc": "^1.0.0",
|
"icc": "^1.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^6.0.2",
|
"mocha": "^6.1.4",
|
||||||
"mock-fs": "^4.8.0",
|
"mock-fs": "^4.9.0",
|
||||||
"nyc": "^13.3.0",
|
"nyc": "^14.0.0",
|
||||||
"prebuild": "8.1.0",
|
"prebuild": "^8.2.1",
|
||||||
"prebuild-ci": "^2.3.0",
|
"prebuild-ci": "^3.0.0",
|
||||||
"rimraf": "^2.6.3",
|
"rimraf": "^2.6.3",
|
||||||
"semistandard": "^13.0.1"
|
"semistandard": "^13.0.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ using vips::VImage;
|
|||||||
namespace sharp {
|
namespace sharp {
|
||||||
|
|
||||||
// Convenience methods to access the attributes of a v8::Object
|
// Convenience methods to access the attributes of a v8::Object
|
||||||
bool HasAttr(v8::Handle<v8::Object> obj, std::string attr) {
|
bool HasAttr(v8::Local<v8::Object> obj, std::string attr) {
|
||||||
return Nan::Has(obj, Nan::New(attr).ToLocalChecked()).FromJust();
|
return Nan::Has(obj, Nan::New(attr).ToLocalChecked()).FromJust();
|
||||||
}
|
}
|
||||||
std::string AttrAsStr(v8::Handle<v8::Object> obj, std::string attr) {
|
std::string AttrAsStr(v8::Local<v8::Object> obj, std::string attr) {
|
||||||
return *Nan::Utf8String(Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked());
|
return *Nan::Utf8String(Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked());
|
||||||
}
|
}
|
||||||
std::vector<double> AttrAsRgba(v8::Handle<v8::Object> obj, std::string attr) {
|
std::vector<double> AttrAsRgba(v8::Local<v8::Object> obj, std::string attr) {
|
||||||
v8::Local<v8::Object> background = AttrAs<v8::Object>(obj, attr);
|
v8::Local<v8::Object> background = AttrAs<v8::Object>(obj, attr);
|
||||||
std::vector<double> rgba(4);
|
std::vector<double> rgba(4);
|
||||||
for (unsigned int i = 0; i < 4; i++) {
|
for (unsigned int i = 0; i < 4; i++) {
|
||||||
@@ -48,7 +48,7 @@ namespace sharp {
|
|||||||
|
|
||||||
// Create an InputDescriptor instance from a v8::Object describing an input image
|
// Create an InputDescriptor instance from a v8::Object describing an input image
|
||||||
InputDescriptor* CreateInputDescriptor(
|
InputDescriptor* CreateInputDescriptor(
|
||||||
v8::Handle<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist
|
v8::Local<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist
|
||||||
) {
|
) {
|
||||||
Nan::HandleScope();
|
Nan::HandleScope();
|
||||||
InputDescriptor *descriptor = new InputDescriptor;
|
InputDescriptor *descriptor = new InputDescriptor;
|
||||||
|
|||||||
14
src/common.h
@@ -77,22 +77,22 @@ namespace sharp {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Convenience methods to access the attributes of a v8::Object
|
// Convenience methods to access the attributes of a v8::Object
|
||||||
bool HasAttr(v8::Handle<v8::Object> obj, std::string attr);
|
bool HasAttr(v8::Local<v8::Object> obj, std::string attr);
|
||||||
std::string AttrAsStr(v8::Handle<v8::Object> obj, std::string attr);
|
std::string AttrAsStr(v8::Local<v8::Object> obj, std::string attr);
|
||||||
std::vector<double> AttrAsRgba(v8::Handle<v8::Object> obj, std::string attr);
|
std::vector<double> AttrAsRgba(v8::Local<v8::Object> obj, std::string attr);
|
||||||
template<typename T> v8::Local<T> AttrAs(v8::Handle<v8::Object> obj, std::string attr) {
|
template<typename T> v8::Local<T> AttrAs(v8::Local<v8::Object> obj, std::string attr) {
|
||||||
return Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked().As<T>();
|
return Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked().As<T>();
|
||||||
}
|
}
|
||||||
template<typename T> T AttrTo(v8::Handle<v8::Object> obj, std::string attr) {
|
template<typename T> T AttrTo(v8::Local<v8::Object> obj, std::string attr) {
|
||||||
return Nan::To<T>(Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked()).FromJust();
|
return Nan::To<T>(Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked()).FromJust();
|
||||||
}
|
}
|
||||||
template<typename T> T AttrTo(v8::Handle<v8::Object> obj, int attr) {
|
template<typename T> T AttrTo(v8::Local<v8::Object> obj, int attr) {
|
||||||
return Nan::To<T>(Nan::Get(obj, attr).ToLocalChecked()).FromJust();
|
return Nan::To<T>(Nan::Get(obj, attr).ToLocalChecked()).FromJust();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an InputDescriptor instance from a v8::Object describing an input image
|
// Create an InputDescriptor instance from a v8::Object describing an input image
|
||||||
InputDescriptor* CreateInputDescriptor(
|
InputDescriptor* CreateInputDescriptor(
|
||||||
v8::Handle<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist);
|
v8::Local<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist);
|
||||||
|
|
||||||
enum class ImageType {
|
enum class ImageType {
|
||||||
JPEG,
|
JPEG,
|
||||||
|
|||||||
@@ -185,6 +185,21 @@ namespace sharp {
|
|||||||
0.0, 0.0, 0.0, 1.0));
|
0.0, 0.0, 0.0, 1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VImage Modulate(VImage image, double const brightness, double const saturation, int const hue) {
|
||||||
|
if (HasAlpha(image)) {
|
||||||
|
// Separate alpha channel
|
||||||
|
VImage alpha = image[image.bands() - 1];
|
||||||
|
return RemoveAlpha(image)
|
||||||
|
.colourspace(VIPS_INTERPRETATION_LCH)
|
||||||
|
.linear({brightness, saturation, 1}, {0, 0, static_cast<double>(hue)})
|
||||||
|
.bandjoin(alpha);
|
||||||
|
} else {
|
||||||
|
return image
|
||||||
|
.colourspace(VIPS_INTERPRETATION_LCH)
|
||||||
|
.linear({brightness, saturation, 1}, {0, 0, static_cast<double>(hue)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
|
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -97,6 +97,11 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix);
|
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Modulate brightness, saturation and hue
|
||||||
|
*/
|
||||||
|
VImage Modulate(VImage image, double const brightness, double const saturation, int const hue);
|
||||||
|
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
|
||||||
#endif // SRC_OPERATIONS_H_
|
#endif // SRC_OPERATIONS_H_
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
|
|
||||||
// Limit input images to a given number of pixels, where pixels = width * height
|
// Limit input images to a given number of pixels, where pixels = width * height
|
||||||
// Ignore if 0
|
// Ignore if 0
|
||||||
if (baton->limitInputPixels > 0 && image.width() * image.height() > baton->limitInputPixels) {
|
if (baton->limitInputPixels > 0 &&
|
||||||
|
static_cast<uint64_t>(image.width() * image.height()) > static_cast<uint64_t>(baton->limitInputPixels)) {
|
||||||
(baton->err).append("Input image exceeds pixel limit");
|
(baton->err).append("Input image exceeds pixel limit");
|
||||||
return Error();
|
return Error();
|
||||||
}
|
}
|
||||||
@@ -349,6 +350,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
||||||
bool const shouldApplyMedian = baton->medianSize > 0;
|
bool const shouldApplyMedian = baton->medianSize > 0;
|
||||||
bool const shouldComposite = !baton->composite.empty();
|
bool const shouldComposite = !baton->composite.empty();
|
||||||
|
bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0;
|
||||||
|
|
||||||
if (shouldComposite && !HasAlpha(image)) {
|
if (shouldComposite && !HasAlpha(image)) {
|
||||||
image = sharp::EnsureAlpha(image);
|
image = sharp::EnsureAlpha(image);
|
||||||
@@ -528,6 +530,10 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
image = sharp::Recomb(image, baton->recombMatrix);
|
image = sharp::Recomb(image, baton->recombMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldModulate) {
|
||||||
|
image = sharp::Modulate(image, baton->brightness, baton->saturation, baton->hue);
|
||||||
|
}
|
||||||
|
|
||||||
// Sharpen
|
// Sharpen
|
||||||
if (shouldSharpen) {
|
if (shouldSharpen) {
|
||||||
image = sharp::Sharpen(image, baton->sharpenSigma, baton->sharpenFlat, baton->sharpenJagged);
|
image = sharp::Sharpen(image, baton->sharpenSigma, baton->sharpenFlat, baton->sharpenJagged);
|
||||||
@@ -1210,6 +1216,9 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->flattenBackground = AttrAsRgba(options, "flattenBackground");
|
baton->flattenBackground = AttrAsRgba(options, "flattenBackground");
|
||||||
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->brightness = AttrTo<double>(options, "brightness");
|
||||||
|
baton->saturation = AttrTo<double>(options, "saturation");
|
||||||
|
baton->hue = AttrTo<int32_t>(options, "hue");
|
||||||
baton->medianSize = AttrTo<uint32_t>(options, "medianSize");
|
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");
|
||||||
|
|||||||
@@ -87,6 +87,9 @@ struct PipelineBaton {
|
|||||||
std::vector<double> flattenBackground;
|
std::vector<double> flattenBackground;
|
||||||
bool negate;
|
bool negate;
|
||||||
double blurSigma;
|
double blurSigma;
|
||||||
|
double brightness;
|
||||||
|
double saturation;
|
||||||
|
int hue;
|
||||||
int medianSize;
|
int medianSize;
|
||||||
double sharpenSigma;
|
double sharpenSigma;
|
||||||
double sharpenFlat;
|
double sharpenFlat;
|
||||||
@@ -189,6 +192,9 @@ struct PipelineBaton {
|
|||||||
flattenBackground{ 0.0, 0.0, 0.0 },
|
flattenBackground{ 0.0, 0.0, 0.0 },
|
||||||
negate(false),
|
negate(false),
|
||||||
blurSigma(0.0),
|
blurSigma(0.0),
|
||||||
|
brightness(1.0),
|
||||||
|
saturation(1.0),
|
||||||
|
hue(0.0),
|
||||||
medianSize(0),
|
medianSize(0),
|
||||||
sharpenSigma(0.0),
|
sharpenSigma(0.0),
|
||||||
sharpenFlat(1.0),
|
sharpenFlat(1.0),
|
||||||
|
|||||||
BIN
test/fixtures/expected/modulate-all.jpg
vendored
Normal file
|
After Width: | Height: | Size: 664 KiB |
BIN
test/fixtures/expected/modulate-brightness-0-5.jpg
vendored
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
test/fixtures/expected/modulate-brightness-2.jpg
vendored
Normal file
|
After Width: | Height: | Size: 692 KiB |
BIN
test/fixtures/expected/modulate-hue-120.jpg
vendored
Normal file
|
After Width: | Height: | Size: 653 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-120.png
vendored
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-150.png
vendored
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-180.png
vendored
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-210.png
vendored
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-240.png
vendored
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-270.png
vendored
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-30.png
vendored
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-300.png
vendored
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-330.png
vendored
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-360.png
vendored
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-60.png
vendored
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
test/fixtures/expected/modulate-hue-angle-90.png
vendored
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
test/fixtures/expected/modulate-saturation-0.5.jpg
vendored
Normal file
|
After Width: | Height: | Size: 606 KiB |
BIN
test/fixtures/expected/modulate-saturation-2.jpg
vendored
Normal file
|
After Width: | Height: | Size: 672 KiB |
2
test/fixtures/index.js
vendored
@@ -120,6 +120,8 @@ module.exports = {
|
|||||||
outputTiff: getPath('output.tiff'),
|
outputTiff: getPath('output.tiff'),
|
||||||
outputZoinks: getPath('output.zoinks'), // an 'unknown' file extension
|
outputZoinks: getPath('output.zoinks'), // an 'unknown' file extension
|
||||||
|
|
||||||
|
testPattern: getPath('test-pattern.png'),
|
||||||
|
|
||||||
// Path for tests requiring human inspection
|
// Path for tests requiring human inspection
|
||||||
path: getPath,
|
path: getPath,
|
||||||
|
|
||||||
|
|||||||
BIN
test/fixtures/test-pattern.png
vendored
Normal file
|
After Width: | Height: | Size: 79 KiB |
@@ -5,8 +5,10 @@ const sharp = require('../../');
|
|||||||
|
|
||||||
const usingCache = detectLibc.family !== detectLibc.MUSL;
|
const usingCache = detectLibc.family !== detectLibc.MUSL;
|
||||||
const usingSimd = !process.env.G_DEBUG;
|
const usingSimd = !process.env.G_DEBUG;
|
||||||
|
const concurrency = detectLibc.family === detectLibc.MUSL ? 1 : undefined;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
sharp.cache(usingCache);
|
sharp.cache(usingCache);
|
||||||
sharp.simd(usingSimd);
|
sharp.simd(usingSimd);
|
||||||
|
sharp.concurrency(concurrency);
|
||||||
});
|
});
|
||||||
|
|||||||
125
test/unit/modulate.js
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const assert = require('assert');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('Modulate', function () {
|
||||||
|
describe('Invalid options', function () {
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
undefined,
|
||||||
|
10,
|
||||||
|
{ brightness: -1 },
|
||||||
|
{ brightness: '50%' },
|
||||||
|
{ brightness: null },
|
||||||
|
{ saturation: -1 },
|
||||||
|
{ saturation: '50%' },
|
||||||
|
{ saturation: null },
|
||||||
|
{ hue: '50deg' },
|
||||||
|
{ hue: 1.5 },
|
||||||
|
{ hue: null }
|
||||||
|
].forEach(function (options) {
|
||||||
|
it('should throw', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpg).modulate(options);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to hue-rotate', function () {
|
||||||
|
const base = 'modulate-hue-120.jpg';
|
||||||
|
const actual = fixtures.path('output.' + base);
|
||||||
|
const expected = fixtures.expected(base);
|
||||||
|
|
||||||
|
return sharp(fixtures.inputJpg)
|
||||||
|
.modulate({ hue: 120 })
|
||||||
|
.toFile(actual)
|
||||||
|
.then(function () {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected, 25);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to brighten', function () {
|
||||||
|
const base = 'modulate-brightness-2.jpg';
|
||||||
|
const actual = fixtures.path('output.' + base);
|
||||||
|
const expected = fixtures.expected(base);
|
||||||
|
|
||||||
|
return sharp(fixtures.inputJpg)
|
||||||
|
.modulate({ brightness: 2 })
|
||||||
|
.toFile(actual)
|
||||||
|
.then(function () {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected, 25);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to unbrighten', function () {
|
||||||
|
const base = 'modulate-brightness-0-5.jpg';
|
||||||
|
const actual = fixtures.path('output.' + base);
|
||||||
|
const expected = fixtures.expected(base);
|
||||||
|
|
||||||
|
return sharp(fixtures.inputJpg)
|
||||||
|
.modulate({ brightness: 0.5 })
|
||||||
|
.toFile(actual)
|
||||||
|
.then(function () {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected, 25);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to saturate', function () {
|
||||||
|
const base = 'modulate-saturation-2.jpg';
|
||||||
|
const actual = fixtures.path('output.' + base);
|
||||||
|
const expected = fixtures.expected(base);
|
||||||
|
|
||||||
|
return sharp(fixtures.inputJpg)
|
||||||
|
.modulate({ saturation: 2 })
|
||||||
|
.toFile(actual)
|
||||||
|
.then(function () {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected, 30);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to desaturate', function () {
|
||||||
|
const base = 'modulate-saturation-0.5.jpg';
|
||||||
|
const actual = fixtures.path('output.' + base);
|
||||||
|
const expected = fixtures.expected(base);
|
||||||
|
|
||||||
|
return sharp(fixtures.inputJpg)
|
||||||
|
.modulate({ saturation: 0.5 })
|
||||||
|
.toFile(actual)
|
||||||
|
.then(function () {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected, 25);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to modulate all channels', function () {
|
||||||
|
const base = 'modulate-all.jpg';
|
||||||
|
const actual = fixtures.path('output.' + base);
|
||||||
|
const expected = fixtures.expected(base);
|
||||||
|
|
||||||
|
return sharp(fixtures.inputJpg)
|
||||||
|
.modulate({ brightness: 2, saturation: 0.5, hue: 180 })
|
||||||
|
.toFile(actual)
|
||||||
|
.then(function () {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected, 25);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hue-rotate', function (done) {
|
||||||
|
[30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330, 360].forEach(function (angle) {
|
||||||
|
it('should properly hue rotate by ' + angle + 'deg', function () {
|
||||||
|
const base = 'modulate-hue-angle-' + angle + '.png';
|
||||||
|
const actual = fixtures.path('output.' + base);
|
||||||
|
const expected = fixtures.expected(base);
|
||||||
|
|
||||||
|
return sharp(fixtures.testPattern)
|
||||||
|
.modulate({ hue: angle })
|
||||||
|
.toFile(actual)
|
||||||
|
.then(function () {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected, 25);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||