Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca52894651 | ||
|
|
4009acdd30 | ||
|
|
147c93ecd3 | ||
|
|
8e04e4b07f | ||
|
|
e7413ea1e5 | ||
|
|
220bb03a32 | ||
|
|
20f512fe5f | ||
|
|
efb3523eaa | ||
|
|
2f2276e091 | ||
|
|
08a6597626 | ||
|
|
d82a6ee4fc | ||
|
|
e627f6d68d | ||
|
|
e650f58bd8 | ||
|
|
5a9b6c8afd | ||
|
|
075771d1e9 | ||
|
|
4fcf091fef | ||
|
|
0e66454fe4 | ||
|
|
aa3ce760bb | ||
|
|
ba46ad1fd9 | ||
|
|
11214bab5d | ||
|
|
d87c289b4a | ||
|
|
af45c03b6f | ||
|
|
7d6fadce6b | ||
|
|
6b560f7a85 | ||
|
|
5d4221460d | ||
|
|
d42c383992 | ||
|
|
9c7f6fcb2b | ||
|
|
14af0bda61 | ||
|
|
fb5c393fbd | ||
|
|
69fe21a7ec | ||
|
|
da4e05c118 | ||
|
|
e4333ff6b0 | ||
|
|
4ae8999f62 | ||
|
|
3fa91bb4ce | ||
|
|
23b2e541ab | ||
|
|
5bfcf61a6f | ||
|
|
0778c112a9 | ||
|
|
2c300754a7 | ||
|
|
c610e306df | ||
|
|
417cca6e0d | ||
|
|
2ed4b5ae83 | ||
|
|
16e429cf2c | ||
|
|
6b7ce8a605 | ||
|
|
ba4ce75377 | ||
|
|
76ded7fd28 | ||
|
|
a0d1a7be50 | ||
|
|
690bc43abe | ||
|
|
50b461024d | ||
|
|
6cf0b3240d | ||
|
|
233b015d77 | ||
|
|
28de243c11 | ||
|
|
36e8a3da88 | ||
|
|
119d16cad3 | ||
|
|
38402d3185 | ||
|
|
6c02949fc1 | ||
|
|
b737d4601e | ||
|
|
3ff3353550 | ||
|
|
946d3c81a5 | ||
|
|
628996846d | ||
|
|
631a3597c7 | ||
|
|
cfa4f7d45c |
4
.github/CONTRIBUTING.md
vendored
@@ -44,8 +44,8 @@ Any change that modifies the existing public API should be added to the relevant
|
|||||||
|
|
||||||
| Release | WIP branch |
|
| Release | WIP branch |
|
||||||
| ------: | :--------- |
|
| ------: | :--------- |
|
||||||
| v0.23.0 | vision |
|
|
||||||
| v0.24.0 | wit |
|
| v0.24.0 | wit |
|
||||||
|
| v0.25.0 | yield |
|
||||||
|
|
||||||
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>`.
|
||||||
|
|
||||||
@@ -98,5 +98,5 @@ Please feel free to ask any questions via a
|
|||||||
[new issue](https://github.com/lovell/sharp/issues/new).
|
[new issue](https://github.com/lovell/sharp/issues/new).
|
||||||
|
|
||||||
If you're unable to post details publicly, please
|
If you're unable to post details publicly, please
|
||||||
[e-mail](https://github.com/lovell/sharp/blob/master/package.json#L4)
|
[e-mail](https://github.com/lovell/sharp/blob/master/package.json#L5)
|
||||||
for private, paid consulting.
|
for private, paid consulting.
|
||||||
|
|||||||
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
open_collective: libvips
|
||||||
14
.github/ISSUE_TEMPLATE/installation.md
vendored
@@ -1,14 +1,18 @@
|
|||||||
---
|
---
|
||||||
name: Installation
|
name: Installation
|
||||||
about: For help if something went wrong installing sharp
|
about: Something went wrong **installing** sharp
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: installation
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
What is the output of running `npm install --verbose sharp`?
|
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/en/stable/install/)?
|
||||||
|
|
||||||
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?
|
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?
|
||||||
|
|
||||||
|
If you are installing as a `root` or `sudo` user, have you tried with the `npm install --unsafe-perm` flag?
|
||||||
|
|
||||||
|
What is the complete output of running `npm install --verbose sharp`? Have you checked this output for useful error messages?
|
||||||
|
|
||||||
|
What is the output of running `npx envinfo --binaries --languages --system --utilities`?
|
||||||
|
|||||||
6
.github/ISSUE_TEMPLATE/possible-bug.md
vendored
@@ -1,12 +1,14 @@
|
|||||||
---
|
---
|
||||||
name: Possible bug
|
name: Possible bug
|
||||||
about: Please provide steps to reproduce
|
about: Something unexpected occurred **using** sharp
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: triage
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<!-- If this issue relates to installation, please use https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md instead. -->
|
||||||
|
|
||||||
What is the output of running `npx envinfo --binaries --languages --system --utilities`?
|
What is the output of running `npx envinfo --binaries --languages --system --utilities`?
|
||||||
|
|
||||||
What are the steps to reproduce?
|
What are the steps to reproduce?
|
||||||
|
|||||||
4
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,12 +1,14 @@
|
|||||||
---
|
---
|
||||||
name: Question
|
name: Question
|
||||||
about: For help with an existing feature
|
about: For help understanding an existing feature
|
||||||
title: ''
|
title: ''
|
||||||
labels: question
|
labels: question
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<!-- If this issue relates to installation, please use https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md instead. -->
|
||||||
|
|
||||||
What are you trying to achieve?
|
What are you trying to achieve?
|
||||||
|
|
||||||
Have you searched for similar questions?
|
Have you searched for similar questions?
|
||||||
|
|||||||
2
.gitignore
vendored
@@ -12,4 +12,6 @@ vendor
|
|||||||
.gitattributes
|
.gitattributes
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
.vscode/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
.idea
|
||||||
|
|||||||
15
.npmignore
@@ -1,15 +0,0 @@
|
|||||||
build
|
|
||||||
node_modules
|
|
||||||
coverage
|
|
||||||
.editorconfig
|
|
||||||
.gitattributes
|
|
||||||
.gitignore
|
|
||||||
test
|
|
||||||
.travis.yml
|
|
||||||
appveyor.yml
|
|
||||||
mkdocs.yml
|
|
||||||
docs/css/
|
|
||||||
vendor
|
|
||||||
.prebuildrc
|
|
||||||
.nyc_output
|
|
||||||
.github/
|
|
||||||
51
.travis.yml
@@ -1,11 +1,5 @@
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- name: "Linux (glibc) - Node 6"
|
|
||||||
os: linux
|
|
||||||
dist: trusty
|
|
||||||
sudo: false
|
|
||||||
language: node_js
|
|
||||||
node_js: "6"
|
|
||||||
- name: "Linux (glibc) - Node 8"
|
- name: "Linux (glibc) - Node 8"
|
||||||
os: linux
|
os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
@@ -24,15 +18,15 @@ matrix:
|
|||||||
sudo: false
|
sudo: false
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: "12"
|
node_js: "12"
|
||||||
after_success:
|
- name: "Linux (glibc) - Node 13"
|
||||||
- npm install coveralls
|
|
||||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
|
||||||
- name: "Linux (glibc) - Node 11"
|
|
||||||
os: linux
|
os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
sudo: false
|
sudo: false
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: "11"
|
node_js: "13"
|
||||||
|
after_success:
|
||||||
|
- npm install coveralls
|
||||||
|
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
||||||
- name: "Linux (musl) - Node 8"
|
- name: "Linux (musl) - Node 8"
|
||||||
os: linux
|
os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
@@ -53,16 +47,6 @@ 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 11"
|
|
||||||
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:11-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: "Linux (musl) - Node 12"
|
- name: "Linux (musl) - Node 12"
|
||||||
os: linux
|
os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
@@ -73,11 +57,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: "OS X - Node 6"
|
- name: "Linux (musl) - Node 13"
|
||||||
os: osx
|
os: linux
|
||||||
osx_image: xcode9.2
|
dist: trusty
|
||||||
language: node_js
|
sudo: true
|
||||||
node_js: "6"
|
language: minimal
|
||||||
|
before_install:
|
||||||
|
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:13.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 8"
|
- name: "OS X - Node 8"
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode9.2
|
osx_image: xcode9.2
|
||||||
@@ -88,13 +77,13 @@ matrix:
|
|||||||
osx_image: xcode9.2
|
osx_image: xcode9.2
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: "10"
|
node_js: "10"
|
||||||
- name: "OS X - Node 11"
|
|
||||||
os: osx
|
|
||||||
osx_image: xcode9.2
|
|
||||||
language: node_js
|
|
||||||
node_js: "11"
|
|
||||||
- name: "OS X - Node 12"
|
- name: "OS X - Node 12"
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode9.2
|
osx_image: xcode9.2
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: "12"
|
node_js: "12"
|
||||||
|
- name: "OS X - Node 13"
|
||||||
|
os: osx
|
||||||
|
osx_image: xcode10
|
||||||
|
language: node_js
|
||||||
|
node_js: "13"
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ is to convert large images in common formats to
|
|||||||
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
|
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
|
||||||
|
|
||||||
Resizing an image is typically 4x-5x faster than using the
|
Resizing an image is typically 4x-5x faster than using the
|
||||||
quickest ImageMagick and GraphicsMagick settings.
|
quickest ImageMagick and GraphicsMagick settings
|
||||||
|
due to its use of [libvips](https://github.com/libvips/libvips).
|
||||||
|
|
||||||
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
|
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
|
||||||
Lanczos resampling ensures quality is not sacrificed for speed.
|
Lanczos resampling ensures quality is not sacrificed for speed.
|
||||||
@@ -20,7 +21,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, 11 and 12
|
Node versions 8, 10, 12 and 13
|
||||||
do not require any additional install or runtime dependencies.
|
do not require any additional install or runtime dependencies.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ build: off
|
|||||||
platform: x64
|
platform: x64
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- nodejs_version: "6"
|
|
||||||
- nodejs_version: "8"
|
- nodejs_version: "8"
|
||||||
- nodejs_version: "10"
|
- nodejs_version: "10"
|
||||||
- nodejs_version: "11"
|
|
||||||
- nodejs_version: "12"
|
- nodejs_version: "12"
|
||||||
|
- nodejs_version: "13"
|
||||||
install:
|
install:
|
||||||
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64
|
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) x64
|
||||||
- npm install -g npm@6
|
- npm install -g npm@6
|
||||||
|
|||||||
14
binding.gyp
@@ -97,7 +97,8 @@
|
|||||||
'conditions': [
|
'conditions': [
|
||||||
['OS == "win"', {
|
['OS == "win"', {
|
||||||
'defines': [
|
'defines': [
|
||||||
'_ALLOW_KEYWORD_MACROS'
|
'_ALLOW_KEYWORD_MACROS',
|
||||||
|
'_FILE_OFFSET_BITS=64'
|
||||||
],
|
],
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'../vendor/lib/libvips.lib',
|
'../vendor/lib/libvips.lib',
|
||||||
@@ -183,22 +184,15 @@
|
|||||||
'configurations': {
|
'configurations': {
|
||||||
'Release': {
|
'Release': {
|
||||||
'cflags_cc': [
|
'cflags_cc': [
|
||||||
'-Wno-cast-function-type',
|
'-Wno-cast-function-type'
|
||||||
'-Wno-deprecated-declarations'
|
|
||||||
],
|
],
|
||||||
'xcode_settings': {
|
|
||||||
'OTHER_CPLUSPLUSFLAGS': [
|
|
||||||
'-Wno-deprecated-declarations'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'msvs_settings': {
|
'msvs_settings': {
|
||||||
'VCCLCompilerTool': {
|
'VCCLCompilerTool': {
|
||||||
'ExceptionHandling': 1
|
'ExceptionHandling': 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'msvs_disabled_warnings': [
|
'msvs_disabled_warnings': [
|
||||||
4275,
|
4275
|
||||||
4996
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ and [https://www.cairographics.org/operators/][2]
|
|||||||
- `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][9]** 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[].premultiplied` **[Boolean][9]** set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option. (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]?**
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
- `input` **([Buffer][1] \| [String][2])?** if present, can be
|
- `input` **([Buffer][1] \| [String][2])?** if present, can be
|
||||||
a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
||||||
a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
||||||
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||||
- `options` **[Object][3]?** if present, is an Object with optional attributes.
|
- `options` **[Object][3]?** if present, is an Object with optional attributes.
|
||||||
- `options.failOnError` **[Boolean][4]** by default halt processing and raise an error when loading invalid images.
|
- `options.failOnError` **[Boolean][4]** by default halt processing and raise an error when loading invalid images.
|
||||||
|
|||||||
@@ -34,8 +34,9 @@ A `Promise` is returned when `callback` is not provided.
|
|||||||
- `density`: Number of pixels per inch (DPI), if present
|
- `density`: Number of pixels per inch (DPI), if present
|
||||||
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||||
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
- `pages`: Number of pages/frames contained within the image, with support for TIFF, PDF, animated GIF and animated WebP
|
- `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
||||||
- `pageHeight`: Number of pixels high each page in this PDF image will be.
|
- `pageHeight`: Number of pixels high each page in this PDF image will be.
|
||||||
|
- `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
||||||
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||||
- `orientation`: Number value of the EXIF Orientation header, if present
|
- `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
|
|||||||
@@ -8,12 +8,15 @@ If an explicit output format is not selected, it will be inferred from the exten
|
|||||||
with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
|
with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
|
||||||
Note that raw pixel data is only supported for buffer output.
|
Note that raw pixel data is only supported for buffer output.
|
||||||
|
|
||||||
|
By default all metadata will be removed, which includes EXIF-based orientation.
|
||||||
|
See [withMetadata][1] for control over this.
|
||||||
|
|
||||||
A `Promise` is returned when `callback` is not provided.
|
A `Promise` is returned when `callback` is not provided.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `fileOut` **[String][1]** the path to write the image data to.
|
- `fileOut` **[String][2]** the path to write the image data to.
|
||||||
- `callback` **[Function][2]?** called on completion with two arguments `(err, info)`.
|
- `callback` **[Function][3]?** called on completion with two arguments `(err, info)`.
|
||||||
`info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
`info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
||||||
`channels` and `premultiplied` (indicating if premultiplication was used).
|
`channels` and `premultiplied` (indicating if premultiplication was used).
|
||||||
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
|
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
|
||||||
@@ -32,15 +35,19 @@ sharp(input)
|
|||||||
.catch(err => { ... });
|
.catch(err => { ... });
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid parameters
|
- Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
Returns **[Promise][4]<[Object][5]>** when no callback is provided
|
Returns **[Promise][5]<[Object][6]>** when no callback is provided
|
||||||
|
|
||||||
## toBuffer
|
## toBuffer
|
||||||
|
|
||||||
Write output to a Buffer.
|
Write output to a Buffer.
|
||||||
JPEG, PNG, WebP, TIFF and RAW output are supported.
|
JPEG, PNG, WebP, TIFF and RAW output are supported.
|
||||||
By default, the format will match the input image, except GIF and SVG input which become PNG output.
|
|
||||||
|
If no explicit format is set, the output format will match the input image, except GIF and SVG input which become PNG output.
|
||||||
|
|
||||||
|
By default all metadata will be removed, which includes EXIF-based orientation.
|
||||||
|
See [withMetadata][1] for control over this.
|
||||||
|
|
||||||
`callback`, if present, gets three arguments `(err, data, info)` where:
|
`callback`, if present, gets three arguments `(err, data, info)` where:
|
||||||
|
|
||||||
@@ -54,9 +61,9 @@ A `Promise` is returned when `callback` is not provided.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][5]?**
|
- `options` **[Object][6]?**
|
||||||
- `options.resolveWithObject` **[Boolean][6]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
- `options.resolveWithObject` **[Boolean][7]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
||||||
- `callback` **[Function][2]?**
|
- `callback` **[Function][3]?**
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -79,7 +86,7 @@ sharp(input)
|
|||||||
.catch(err => { ... });
|
.catch(err => { ... });
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns **[Promise][4]<[Buffer][7]>** when no callback is provided
|
Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
|
||||||
|
|
||||||
## withMetadata
|
## withMetadata
|
||||||
|
|
||||||
@@ -89,8 +96,8 @@ This will also convert to and add a web-friendly sRGB ICC profile.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `withMetadata` **[Object][5]?**
|
- `options` **[Object][6]?**
|
||||||
- `withMetadata.orientation` **[Number][8]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
- `options.orientation` **[Number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -101,7 +108,7 @@ sharp('input.jpg')
|
|||||||
.then(info => { ... });
|
.then(info => { ... });
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid parameters
|
- Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -111,19 +118,19 @@ Use these JPEG options for output image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][5]?** output options
|
- `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
|
- `options.quality` **[Number][9]** quality, integer 1-100 (optional, default `80`)
|
||||||
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
- `options.progressive` **[Boolean][7]** 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][2]** 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 libvips compiled with support for mozjpeg (optional, default `false`)
|
- `options.trellisQuantisation` **[Boolean][7]** apply trellis quantisation, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
||||||
- `options.overshootDeringing` **[Boolean][6]** apply overshoot deringing, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
- `options.overshootDeringing` **[Boolean][7]** apply overshoot deringing, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
||||||
- `options.optimiseScans` **[Boolean][6]** optimise progressive scans, forces progressive, requires libvips compiled with support for mozjpeg (optional, default `false`)
|
- `options.optimiseScans` **[Boolean][7]** 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][7]** alternative spelling of optimiseScans (optional, default `false`)
|
||||||
- `options.optimiseCoding` **[Boolean][6]** optimise Huffman coding tables (optional, default `true`)
|
- `options.optimiseCoding` **[Boolean][7]** optimise Huffman coding tables (optional, default `true`)
|
||||||
- `options.optimizeCoding` **[Boolean][6]** alternative spelling of optimiseCoding (optional, default `true`)
|
- `options.optimizeCoding` **[Boolean][7]** alternative spelling of optimiseCoding (optional, default `true`)
|
||||||
- `options.quantisationTable` **[Number][8]** quantization table to use, integer 0-8, requires libvips compiled with support for mozjpeg (optional, default `0`)
|
- `options.quantisationTable` **[Number][9]** 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][9]** 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][7]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -137,7 +144,7 @@ const data = await sharp(input)
|
|||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid options
|
- Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -150,16 +157,16 @@ Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][5]?**
|
- `options` **[Object][6]?**
|
||||||
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
- `options.progressive` **[Boolean][7]** use progressive (interlace) scan (optional, default `false`)
|
||||||
- `options.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
|
- `options.compressionLevel` **[Number][9]** zlib compression level, 0-9 (optional, default `9`)
|
||||||
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
|
- `options.adaptiveFiltering` **[Boolean][7]** use adaptive row filtering (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.palette` **[Boolean][7]** 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 libvips compiled with support for libimagequant (optional, default `100`)
|
- `options.quality` **[Number][9]** 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 libvips compiled with support for libimagequant (optional, default `256`)
|
- `options.colours` **[Number][9]** 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 libvips compiled with support for libimagequant (optional, default `256`)
|
- `options.colors` **[Number][9]** 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 libvips compiled with support for libimagequant (optional, default `1.0`)
|
- `options.dither` **[Number][9]** 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][7]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -170,7 +177,7 @@ const data = await sharp(input)
|
|||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid options
|
- Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -180,12 +187,14 @@ Use these WebP options for output image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][5]?** output options
|
- `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
|
- `options.quality` **[Number][9]** quality, integer 1-100 (optional, default `80`)
|
||||||
- `options.alphaQuality` **[Number][8]** quality of alpha layer, integer 0-100 (optional, default `100`)
|
- `options.alphaQuality` **[Number][9]** quality of alpha layer, integer 0-100 (optional, default `100`)
|
||||||
- `options.lossless` **[Boolean][6]** use lossless compression mode (optional, default `false`)
|
- `options.lossless` **[Boolean][7]** use lossless compression mode (optional, default `false`)
|
||||||
- `options.nearLossless` **[Boolean][6]** use near_lossless compression mode (optional, default `false`)
|
- `options.nearLossless` **[Boolean][7]** use near_lossless compression mode (optional, default `false`)
|
||||||
- `options.force` **[Boolean][6]** force WebP output, otherwise attempt to use input format (optional, default `true`)
|
- `options.smartSubsample` **[Boolean][7]** use high quality chroma subsampling (optional, default `false`)
|
||||||
|
- `options.reductionEffort` **[Number][9]** level of CPU effort to reduce file size, integer 0-6 (optional, default `4`)
|
||||||
|
- `options.force` **[Boolean][7]** force WebP output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -196,7 +205,7 @@ const data = await sharp(input)
|
|||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid options
|
- Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -206,18 +215,18 @@ Use these TIFF options for output image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][5]?** output options
|
- `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
|
- `options.quality` **[Number][9]** quality, integer 1-100 (optional, default `80`)
|
||||||
- `options.force` **[Boolean][6]** force TIFF output, otherwise attempt to use input format (optional, default `true`)
|
- `options.force` **[Boolean][7]** force TIFF output, otherwise attempt to use input format (optional, default `true`)
|
||||||
- `options.compression` **[Boolean][6]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
|
- `options.compression` **[Boolean][7]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
|
||||||
- `options.predictor` **[Boolean][6]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
|
- `options.predictor` **[Boolean][7]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
|
||||||
- `options.pyramid` **[Boolean][6]** write an image pyramid (optional, default `false`)
|
- `options.pyramid` **[Boolean][7]** write an image pyramid (optional, default `false`)
|
||||||
- `options.tile` **[Boolean][6]** write a tiled tiff (optional, default `false`)
|
- `options.tile` **[Boolean][7]** write a tiled tiff (optional, default `false`)
|
||||||
- `options.tileWidth` **[Boolean][6]** horizontal tile size (optional, default `256`)
|
- `options.tileWidth` **[Boolean][7]** horizontal tile size (optional, default `256`)
|
||||||
- `options.tileHeight` **[Boolean][6]** vertical tile size (optional, default `256`)
|
- `options.tileHeight` **[Boolean][7]** vertical tile size (optional, default `256`)
|
||||||
- `options.xres` **[Number][8]** horizontal resolution in pixels/mm (optional, default `1.0`)
|
- `options.xres` **[Number][9]** horizontal resolution in pixels/mm (optional, default `1.0`)
|
||||||
- `options.yres` **[Number][8]** vertical resolution in pixels/mm (optional, default `1.0`)
|
- `options.yres` **[Number][9]** vertical resolution in pixels/mm (optional, default `1.0`)
|
||||||
- `options.squash` **[Boolean][6]** squash 8-bit images down to 1 bit (optional, default `false`)
|
- `options.squash` **[Boolean][7]** squash 8-bit images down to 1 bit (optional, default `false`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -232,7 +241,30 @@ sharp('input.svg')
|
|||||||
.then(info => { ... });
|
.then(info => { ... });
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid options
|
- Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
|
Returns **Sharp**
|
||||||
|
|
||||||
|
## heif
|
||||||
|
|
||||||
|
Use these HEIF options for output image.
|
||||||
|
|
||||||
|
Support for HEIF (HEIC/AVIF) is experimental.
|
||||||
|
Do not use this in production systems.
|
||||||
|
|
||||||
|
Requires a custom, globally-installed libvips compiled with support for libheif.
|
||||||
|
|
||||||
|
Most versions of libheif support only the patent-encumbered HEVC compression format.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- `options` **[Object][6]?** output options
|
||||||
|
- `options.quality` **[Number][9]** quality, integer 1-100 (optional, default `80`)
|
||||||
|
- `options.compression` **[Boolean][7]** compression format: hevc, avc, jpeg, av1 (optional, default `'hevc'`)
|
||||||
|
- `options.lossless` **[Boolean][7]** use lossless compression (optional, default `false`)
|
||||||
|
|
||||||
|
|
||||||
|
- Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -257,8 +289,8 @@ Force output to a given format.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `format` **([String][1] \| [Object][5])** as a String or an Object with an 'id' attribute
|
- `format` **([String][2] \| [Object][6])** as a String or an Object with an 'id' attribute
|
||||||
- `options` **[Object][5]** output options
|
- `options` **[Object][6]** output options
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -269,9 +301,9 @@ const data = await sharp(input)
|
|||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** unsupported format or options
|
- Throws **[Error][4]** unsupported format or options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
## tile
|
## tile
|
||||||
|
|
||||||
@@ -283,13 +315,15 @@ Warning: multiple sharp instances concurrently producing tile output can expose
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `tile` **[Object][5]?**
|
- `options` **[Object][6]?**
|
||||||
- `tile.size` **[Number][8]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
|
- `options.size` **[Number][9]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
|
||||||
- `tile.overlap` **[Number][8]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
|
- `options.overlap` **[Number][9]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
|
||||||
- `tile.angle` **[Number][8]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
|
- `options.angle` **[Number][9]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
|
||||||
- `tile.depth` **[String][1]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
|
- `options.background` **([String][2] \| [Object][6])** background colour, parsed by the [color][10] module, defaults to white without transparency. (optional, default `{r:255,g:255,b:255,alpha:1}`)
|
||||||
- `tile.container` **[String][1]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
|
- `options.depth` **[String][2]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
|
||||||
- `tile.layout` **[String][1]** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
|
- `options.skipBlanks` **[Number][9]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`)
|
||||||
|
- `options.container` **[String][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
|
||||||
|
- `options.layout` **[String][2]** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -305,22 +339,26 @@ sharp('input.tiff')
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid parameters
|
- Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
[1]: #withmetadata
|
||||||
|
|
||||||
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
|
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||||
|
|
||||||
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
|
||||||
|
|
||||||
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||||
|
|
||||||
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
|
||||||
|
|
||||||
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||||
|
|
||||||
[7]: https://nodejs.org/api/buffer.html
|
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||||
|
|
||||||
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
[8]: https://nodejs.org/api/buffer.html
|
||||||
|
|
||||||
|
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||||
|
|
||||||
|
[10]: https://www.npmjs.org/package/color
|
||||||
|
|||||||
@@ -1,5 +1,71 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### v0.23 - "*vision*"
|
||||||
|
|
||||||
|
Requires libvips v8.8.1.
|
||||||
|
|
||||||
|
#### v0.23.2 - 28<sup>th</sup> October 2019
|
||||||
|
|
||||||
|
* Add `background` option to tile output operation.
|
||||||
|
[#1924](https://github.com/lovell/sharp/pull/1924)
|
||||||
|
[@neave](https://github.com/neave)
|
||||||
|
|
||||||
|
* Add support for Node.js 13.
|
||||||
|
[#1932](https://github.com/lovell/sharp/pull/1932)
|
||||||
|
[@MayhemYDG](https://github.com/MayhemYDG)
|
||||||
|
|
||||||
|
#### v0.23.1 - 26<sup>th</sup> September 2019
|
||||||
|
|
||||||
|
* Ensure `sharp.format.vips` is present and correct (filesystem only).
|
||||||
|
[#1813](https://github.com/lovell/sharp/issues/1813)
|
||||||
|
|
||||||
|
* Ensure invalid `width` and `height` provided as options to `resize` throw.
|
||||||
|
[#1817](https://github.com/lovell/sharp/issues/1817)
|
||||||
|
|
||||||
|
* Allow use of 'heic' and 'heif' identifiers with `toFormat`.
|
||||||
|
[#1834](https://github.com/lovell/sharp/pull/1834)
|
||||||
|
[@jaubourg](https://github.com/jaubourg)
|
||||||
|
|
||||||
|
* Add `premultiplied` option to `composite` operation.
|
||||||
|
[#1835](https://github.com/lovell/sharp/pull/1835)
|
||||||
|
[@Andargor](https://github.com/Andargor)
|
||||||
|
|
||||||
|
* Allow instance reuse with differing `toBuffer` options.
|
||||||
|
[#1860](https://github.com/lovell/sharp/pull/1860)
|
||||||
|
[@RaboliotTheGrey](https://github.com/RaboliotTheGrey)
|
||||||
|
|
||||||
|
* Ensure image is at least 3x3 pixels before attempting trim operation.
|
||||||
|
|
||||||
|
#### v0.23.0 - 29<sup>th</sup> July 2019
|
||||||
|
|
||||||
|
* Remove `overlayWith` previously deprecated in v0.22.0.
|
||||||
|
|
||||||
|
* Add experimental support for HEIF images. Requires libvips compiled with libheif.
|
||||||
|
[#1105](https://github.com/lovell/sharp/issues/1105)
|
||||||
|
|
||||||
|
* Expose libwebp `smartSubsample` and `reductionEffort` options.
|
||||||
|
[#1545](https://github.com/lovell/sharp/issues/1545)
|
||||||
|
|
||||||
|
* Add experimental support for Worker Threads.
|
||||||
|
[#1558](https://github.com/lovell/sharp/issues/1558)
|
||||||
|
|
||||||
|
* Use libvips' built-in CMYK and sRGB profiles when required.
|
||||||
|
[#1619](https://github.com/lovell/sharp/issues/1619)
|
||||||
|
|
||||||
|
* Drop support for Node.js versions 6 and 11.
|
||||||
|
[#1674](https://github.com/lovell/sharp/issues/1674)
|
||||||
|
|
||||||
|
* Expose `skipBlanks` option for tile-based output.
|
||||||
|
[#1687](https://github.com/lovell/sharp/pull/1687)
|
||||||
|
[@RaboliotTheGrey](https://github.com/RaboliotTheGrey)
|
||||||
|
|
||||||
|
* Allow use of `failOnError` option with Stream-based input.
|
||||||
|
[#1691](https://github.com/lovell/sharp/issues/1691)
|
||||||
|
|
||||||
|
* Fix rotate/extract ordering for non-90 angles.
|
||||||
|
[#1755](https://github.com/lovell/sharp/pull/1755)
|
||||||
|
[@iovdin](https://github.com/iovdin)
|
||||||
|
|
||||||
### v0.22 - "*uptake*"
|
### v0.22 - "*uptake*"
|
||||||
|
|
||||||
Requires libvips v8.7.4.
|
Requires libvips v8.7.4.
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ is to convert large images in common formats to
|
|||||||
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
|
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
|
||||||
|
|
||||||
Resizing an image is typically 4x-5x faster than using the
|
Resizing an image is typically 4x-5x faster than using the
|
||||||
quickest ImageMagick and GraphicsMagick settings.
|
quickest ImageMagick and GraphicsMagick settings
|
||||||
|
due to its use of [libvips](https://github.com/libvips/libvips).
|
||||||
|
|
||||||
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
|
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
|
||||||
Lanczos resampling ensures quality is not sacrificed for speed.
|
Lanczos resampling ensures quality is not sacrificed for speed.
|
||||||
@@ -16,7 +17,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, 11 and 12
|
Node versions 8, 10, 12 and 13
|
||||||
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)
|
||||||
@@ -125,6 +126,8 @@ the help and code contributions of the following people:
|
|||||||
* [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)
|
* [Jakub Michálek](https://github.com/Goues)
|
||||||
|
* [Ilya Ovdin](https://github.com/iovdin)
|
||||||
|
* [Andargor](https://github.com/Andargor)
|
||||||
|
|
||||||
Thank you!
|
Thank you!
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ yarn add sharp
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
* Node.js v6+
|
* Node.js v8.5.0+
|
||||||
|
|
||||||
### 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, 11 and 12 on
|
Node versions 8, 10, 12 and 13 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:
|
||||||
@@ -36,15 +36,16 @@ Building from source requires:
|
|||||||
[](https://travis-ci.org/lovell/sharp)
|
[](https://travis-ci.org/lovell/sharp)
|
||||||
|
|
||||||
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
|
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
|
||||||
This involves an automated HTTPS download of approximately 9MB.
|
This involves an automated HTTPS download of approximately 10MB.
|
||||||
|
|
||||||
Most Linux-based (glibc, musl) operating systems running on x64 and ARMv6+ CPUs should "just work", e.g.:
|
Most Linux-based (glibc, musl) operating systems running on x64 and ARMv6+ CPUs should "just work", e.g.:
|
||||||
|
|
||||||
* Debian 7+
|
* Debian 8+
|
||||||
* Ubuntu 14.04+
|
* Ubuntu 14.04+
|
||||||
* Centos 7+
|
* Red Hat Enterprise 7+
|
||||||
* Alpine 3.8+ (Node 8+)
|
* CentOS 7+
|
||||||
* Fedora
|
* Alpine 3.10+
|
||||||
|
* Fedora 21+
|
||||||
* openSUSE 13.2+
|
* openSUSE 13.2+
|
||||||
* Archlinux
|
* Archlinux
|
||||||
* Raspbian Jessie
|
* Raspbian Jessie
|
||||||
@@ -61,7 +62,8 @@ and `LD_LIBRARY_PATH` at runtime.
|
|||||||
|
|
||||||
This allows the use of newer versions of libvips with older versions of sharp.
|
This allows the use of newer versions of libvips with older versions of sharp.
|
||||||
|
|
||||||
For 32-bit Intel CPUs and older Linux-based operating systems such as Centos 6,
|
For 32-bit Intel CPUs and older Linux-based operating systems such as
|
||||||
|
those based on Red Hat Enterprise 6 (e.g. CentOS 6)
|
||||||
compiling libvips from source is recommended.
|
compiling libvips from source is recommended.
|
||||||
|
|
||||||
[https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball](https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball)
|
[https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball](https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball)
|
||||||
@@ -69,12 +71,11 @@ compiling libvips from source is recommended.
|
|||||||
#### Alpine Linux
|
#### Alpine Linux
|
||||||
|
|
||||||
libvips is available in the
|
libvips is available in the
|
||||||
[testing repository](https://pkgs.alpinelinux.org/packages?name=vips-dev):
|
[community repository](https://pkgs.alpinelinux.org/packages?name=vips-dev):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
apk add vips-dev fftw-dev build-base --update-cache \
|
apk add --upgrade --no-cache vips-dev build-base \
|
||||||
--repository https://alpine.global.ssl.fastly.net/alpine/edge/testing/ \
|
--repository https://alpine.global.ssl.fastly.net/alpine/v3.10/community/
|
||||||
--repository https://alpine.global.ssl.fastly.net/alpine/edge/main
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The smaller stack size of musl libc means
|
The smaller stack size of musl libc means
|
||||||
@@ -97,7 +98,7 @@ that it can be located using `pkg-config --modversion vips-cpp`.
|
|||||||
[](https://ci.appveyor.com/project/lovell/sharp)
|
[](https://ci.appveyor.com/project/lovell/sharp)
|
||||||
|
|
||||||
libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
|
libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
|
||||||
This involves an automated HTTPS download of approximately 14MB.
|
This involves an automated HTTPS download of approximately 10MB.
|
||||||
If you are having issues during installation consider removing the directory
|
If you are having issues during installation consider removing the directory
|
||||||
`C:\Users\[user]\AppData\Roaming\npm-cache\_libvips`.
|
`C:\Users\[user]\AppData\Roaming\npm-cache\_libvips`.
|
||||||
|
|
||||||
@@ -150,7 +151,7 @@ docker pull tailor/docker-libvips
|
|||||||
|
|
||||||
### AWS Lambda
|
### AWS Lambda
|
||||||
|
|
||||||
Set the Lambda runtime to Node.js 8.10.
|
Set the Lambda runtime to `nodejs10.x`.
|
||||||
|
|
||||||
The binaries in the `node_modules` directory of the
|
The binaries in the `node_modules` directory of the
|
||||||
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html)
|
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html)
|
||||||
@@ -160,14 +161,14 @@ On non-Linux machines such as OS X and Windows run the following:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
rm -rf node_modules/sharp
|
rm -rf node_modules/sharp
|
||||||
npm install --arch=x64 --platform=linux --target=8.10.0 sharp
|
npm install --arch=x64 --platform=linux --target=10.15.0 sharp
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively a Docker container closely matching the Lambda runtime can be used:
|
Alternatively a Docker container closely matching the Lambda runtime can be used:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
rm -rf node_modules/sharp
|
rm -rf node_modules/sharp
|
||||||
docker run -v "$PWD":/var/task lambci/lambda:build-nodejs8.10 npm install sharp
|
docker run -v "$PWD":/var/task lambci/lambda:build-nodejs10.x npm install sharp
|
||||||
```
|
```
|
||||||
|
|
||||||
To get the best performance select the largest memory available.
|
To get the best performance select the largest memory available.
|
||||||
|
|||||||
@@ -4,16 +4,15 @@
|
|||||||
|
|
||||||
* AWS EC2 eu-west-1 [c5.large](https://aws.amazon.com/ec2/instance-types/c5/) (2x Xeon Platinum 8124M CPU @ 3.00GHz)
|
* AWS EC2 eu-west-1 [c5.large](https://aws.amazon.com/ec2/instance-types/c5/) (2x Xeon Platinum 8124M CPU @ 3.00GHz)
|
||||||
* Ubuntu 18.04 (hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180912 ami-00035f41c82244dab)
|
* Ubuntu 18.04 (hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180912 ami-00035f41c82244dab)
|
||||||
* Node.js v10.11.0
|
* Node.js v12.10.0
|
||||||
|
|
||||||
### The contenders
|
### The contenders
|
||||||
|
|
||||||
* [jimp](https://www.npmjs.com/package/jimp) v0.5.3 - Image processing in pure JavaScript. Provides bicubic interpolation.
|
* [jimp](https://www.npmjs.com/package/jimp) v0.8.4 - Image processing in pure JavaScript. Provides bicubic interpolation.
|
||||||
* [mapnik](https://www.npmjs.org/package/mapnik) v4.0.1 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
|
* [mapnik](https://www.npmjs.org/package/mapnik) v4.3.1 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
|
||||||
* [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) v1.9.3 - Wrapper around libmagick++, supports Buffers only.
|
|
||||||
* [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
|
* [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
|
||||||
* [gm](https://www.npmjs.com/package/gm) v1.23.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
|
* [gm](https://www.npmjs.com/package/gm) v1.23.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
|
||||||
* sharp v0.21.0 / libvips v8.7.0 - Caching within libvips disabled to ensure a fair comparison.
|
* sharp v0.23.1 / libvips v8.8.1 - Caching within libvips disabled to ensure a fair comparison.
|
||||||
|
|
||||||
### The task
|
### The task
|
||||||
|
|
||||||
@@ -25,14 +24,14 @@ then compress to JPEG at a "quality" setting of 80.
|
|||||||
|
|
||||||
| Module | Input | Output | Ops/sec | Speed-up |
|
| Module | Input | Output | Ops/sec | Speed-up |
|
||||||
| :----------------- | :----- | :----- | ------: | -------: |
|
| :----------------- | :----- | :----- | ------: | -------: |
|
||||||
| jimp | buffer | buffer | 0.71 | 1.0 |
|
| jimp | buffer | buffer | 0.66 | 1.0 |
|
||||||
| mapnik | buffer | buffer | 3.32 | 4.7 |
|
| mapnik | buffer | buffer | 3.31 | 5.0 |
|
||||||
| gm | buffer | buffer | 3.97 | 5.6 |
|
| gm | buffer | buffer | 3.79 | 5.7 |
|
||||||
| imagemagick-native | buffer | buffer | 4.06 | 5.7 |
|
| gm | file | file | 3.82 | 5.8 |
|
||||||
| imagemagick | file | file | 4.24 | 6.0 |
|
| imagemagick | file | file | 4.17 | 6.3 |
|
||||||
| sharp | stream | stream | 25.30 | 35.6 |
|
| sharp | stream | stream | 25.81 | 39.1 |
|
||||||
| sharp | file | file | 26.17 | 36.9 |
|
| sharp | file | file | 26.76 | 40.5 |
|
||||||
| sharp | buffer | buffer | 26.45 | 37.3 |
|
| sharp | buffer | buffer | 28.06 | 42.5 |
|
||||||
|
|
||||||
Greater libvips performance can be expected with caching enabled (default)
|
Greater libvips performance can be expected with caching enabled (default)
|
||||||
and using 8+ core machines, especially those with larger L1/L2 CPU caches.
|
and using 8+ core machines, especially those with larger L1/L2 CPU caches.
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const copyFileSync = require('fs-copy-file-sync');
|
|
||||||
const libvips = require('../lib/libvips');
|
const libvips = require('../lib/libvips');
|
||||||
const npmLog = require('npmlog');
|
const npmLog = require('npmlog');
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ if (process.platform === 'win32') {
|
|||||||
return /\.dll$/.test(filename);
|
return /\.dll$/.test(filename);
|
||||||
})
|
})
|
||||||
.forEach(function (filename) {
|
.forEach(function (filename) {
|
||||||
copyFileSync(
|
fs.copyFileSync(
|
||||||
path.join(vendorLibDir, filename),
|
path.join(vendorLibDir, filename),
|
||||||
path.join(buildReleaseDir, filename)
|
path.join(buildReleaseDir, filename)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ const npmLog = require('npmlog');
|
|||||||
const semver = require('semver');
|
const semver = require('semver');
|
||||||
const simpleGet = require('simple-get');
|
const simpleGet = require('simple-get');
|
||||||
const tar = require('tar');
|
const tar = require('tar');
|
||||||
const copyFileSync = require('fs-copy-file-sync');
|
|
||||||
|
|
||||||
const agent = require('../lib/agent');
|
const agent = require('../lib/agent');
|
||||||
const libvips = require('../lib/libvips');
|
const libvips = require('../lib/libvips');
|
||||||
@@ -20,6 +19,9 @@ const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SH
|
|||||||
|
|
||||||
const fail = function (err) {
|
const fail = function (err) {
|
||||||
npmLog.error('sharp', err.message);
|
npmLog.error('sharp', err.message);
|
||||||
|
if (err.code === 'EACCES') {
|
||||||
|
npmLog.info('sharp', 'Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
|
||||||
|
}
|
||||||
npmLog.info('sharp', 'Attempting to build from source via node-gyp but this may fail due to the above error');
|
npmLog.info('sharp', 'Attempting to build from source via node-gyp but this may fail due to the above error');
|
||||||
npmLog.info('sharp', 'Please see https://sharp.pixelplumbing.com/page/install for required dependencies');
|
npmLog.info('sharp', 'Please see https://sharp.pixelplumbing.com/page/install for required dependencies');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -64,7 +66,7 @@ try {
|
|||||||
if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
|
if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
|
||||||
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
||||||
}
|
}
|
||||||
if (detectLibc.family === detectLibc.GLIBC && detectLibc.version && semver.lt(`${detectLibc.version}.0`, '2.13.0')) {
|
if (detectLibc.family === detectLibc.GLIBC && detectLibc.version && semver.lt(`${detectLibc.version}.0`, '2.17.0')) {
|
||||||
throw new Error(`Use with glibc version ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
throw new Error(`Use with glibc version ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
||||||
}
|
}
|
||||||
// Download to per-process temporary file
|
// Download to per-process temporary file
|
||||||
@@ -80,14 +82,16 @@ try {
|
|||||||
npmLog.info('sharp', `Downloading ${url}`);
|
npmLog.info('sharp', `Downloading ${url}`);
|
||||||
simpleGet({ url: url, agent: agent() }, function (err, response) {
|
simpleGet({ url: url, agent: agent() }, function (err, response) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
fail(err);
|
||||||
|
} else if (response.statusCode === 404) {
|
||||||
|
fail(new Error(`Prebuilt libvips binaries are not yet available for ${platformAndArch}`));
|
||||||
|
} else if (response.statusCode !== 200) {
|
||||||
|
fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
|
||||||
|
} else {
|
||||||
|
response
|
||||||
|
.on('error', fail)
|
||||||
|
.pipe(tmpFile);
|
||||||
}
|
}
|
||||||
if (response.statusCode !== 200) {
|
|
||||||
throw new Error(`Status ${response.statusCode}`);
|
|
||||||
}
|
|
||||||
response
|
|
||||||
.on('error', fail)
|
|
||||||
.pipe(tmpFile);
|
|
||||||
});
|
});
|
||||||
tmpFile
|
tmpFile
|
||||||
.on('error', fail)
|
.on('error', fail)
|
||||||
@@ -97,7 +101,7 @@ try {
|
|||||||
fs.renameSync(tarPathTemp, tarPathCache);
|
fs.renameSync(tarPathTemp, tarPathCache);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Fall back to copy and unlink
|
// Fall back to copy and unlink
|
||||||
copyFileSync(tarPathTemp, tarPathCache);
|
fs.copyFileSync(tarPathTemp, tarPathCache);
|
||||||
fs.unlinkSync(tarPathTemp);
|
fs.unlinkSync(tarPathTemp);
|
||||||
}
|
}
|
||||||
extractTarball(tarPathCache);
|
extractTarball(tarPathCache);
|
||||||
|
|||||||
@@ -20,15 +20,18 @@ function env (key) {
|
|||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
try {
|
try {
|
||||||
const proxy = url.parse(proxies.map(env).find(is.string));
|
const proxy = new url.URL(proxies.map(env).find(is.string));
|
||||||
const tunnel = proxy.protocol === 'https:'
|
const tunnel = proxy.protocol === 'https:'
|
||||||
? tunnelAgent.httpsOverHttps
|
? tunnelAgent.httpsOverHttps
|
||||||
: tunnelAgent.httpsOverHttp;
|
: tunnelAgent.httpsOverHttp;
|
||||||
|
const proxyAuth = proxy.username && proxy.password
|
||||||
|
? `${proxy.username}:${proxy.password}`
|
||||||
|
: null;
|
||||||
return tunnel({
|
return tunnel({
|
||||||
proxy: {
|
proxy: {
|
||||||
port: Number(proxy.port),
|
port: Number(proxy.port),
|
||||||
host: proxy.hostname,
|
host: proxy.hostname,
|
||||||
proxyAuth: proxy.auth
|
proxyAuth
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ function extractChannel (channel) {
|
|||||||
if (is.integer(channel) && is.inRange(channel, 0, 4)) {
|
if (is.integer(channel) && is.inRange(channel, 0, 4)) {
|
||||||
this.options.extractChannel = channel;
|
this.options.extractChannel = channel;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Cannot extract invalid channel ' + channel);
|
throw is.invalidParameterError('channel', 'integer or one of: red, green, blue', channel);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ function bandbool (boolOp) {
|
|||||||
if (is.string(boolOp) && is.inArray(boolOp, ['and', 'or', 'eor'])) {
|
if (is.string(boolOp) && is.inArray(boolOp, ['and', 'or', 'eor'])) {
|
||||||
this.options.bandBoolOp = boolOp;
|
this.options.bandBoolOp = boolOp;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid bandbool operation ' + boolOp);
|
throw is.invalidParameterError('boolOp', 'one of: and, or, eor', boolOp);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ function grayscale (grayscale) {
|
|||||||
*/
|
*/
|
||||||
function toColourspace (colourspace) {
|
function toColourspace (colourspace) {
|
||||||
if (!is.string(colourspace)) {
|
if (!is.string(colourspace)) {
|
||||||
throw new Error('Invalid output colourspace ' + colourspace);
|
throw is.invalidParameterError('colourspace', 'string', colourspace);
|
||||||
}
|
}
|
||||||
this.options.colourspace = colourspace;
|
this.options.colourspace = colourspace;
|
||||||
return this;
|
return this;
|
||||||
@@ -83,18 +83,22 @@ function toColorspace (colorspace) {
|
|||||||
* Update a colour attribute of the this.options Object.
|
* Update a colour attribute of the this.options Object.
|
||||||
* @private
|
* @private
|
||||||
* @param {String} key
|
* @param {String} key
|
||||||
* @param {String|Object} val
|
* @param {String|Object} value
|
||||||
* @throws {Error} Invalid key
|
* @throws {Error} Invalid value
|
||||||
*/
|
*/
|
||||||
function _setColourOption (key, val) {
|
function _setBackgroundColourOption (key, value) {
|
||||||
if (is.object(val) || is.string(val)) {
|
if (is.defined(value)) {
|
||||||
const colour = color(val);
|
if (is.object(value) || is.string(value)) {
|
||||||
this.options[key] = [
|
const colour = color(value);
|
||||||
colour.red(),
|
this.options[key] = [
|
||||||
colour.green(),
|
colour.red(),
|
||||||
colour.blue(),
|
colour.green(),
|
||||||
Math.round(colour.alpha() * 255)
|
colour.blue(),
|
||||||
];
|
Math.round(colour.alpha() * 255)
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('background', 'object or string', value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +115,7 @@ module.exports = function (Sharp) {
|
|||||||
toColourspace,
|
toColourspace,
|
||||||
toColorspace,
|
toColorspace,
|
||||||
// Private
|
// Private
|
||||||
_setColourOption
|
_setBackgroundColourOption
|
||||||
});
|
});
|
||||||
// Class attributes
|
// Class attributes
|
||||||
Sharp.colourspace = colourspace;
|
Sharp.colourspace = colourspace;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const deprecate = require('util').deprecate;
|
|
||||||
|
|
||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,6 +81,7 @@ const blend = {
|
|||||||
* @param {Number} [images[].top] - the pixel offset from the top edge.
|
* @param {Number} [images[].top] - the pixel offset from the top edge.
|
||||||
* @param {Number} [images[].left] - the pixel offset from the left edge.
|
* @param {Number} [images[].left] - the pixel offset from the left edge.
|
||||||
* @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
|
* @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
|
||||||
|
* @param {Boolean} [images[].premultiplied=false] - set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option.
|
||||||
* @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
|
* @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
|
||||||
* @param {Object} [images[].raw] - describes overlay when using raw pixel data.
|
* @param {Object} [images[].raw] - describes overlay when using raw pixel data.
|
||||||
* @param {Number} [images[].raw.width]
|
* @param {Number} [images[].raw.width]
|
||||||
@@ -107,7 +106,8 @@ function composite (images) {
|
|||||||
tile: false,
|
tile: false,
|
||||||
left: -1,
|
left: -1,
|
||||||
top: -1,
|
top: -1,
|
||||||
gravity: 0
|
gravity: 0,
|
||||||
|
premultiplied: false
|
||||||
};
|
};
|
||||||
if (is.defined(image.blend)) {
|
if (is.defined(image.blend)) {
|
||||||
if (is.string(blend[image.blend])) {
|
if (is.string(blend[image.blend])) {
|
||||||
@@ -149,26 +149,24 @@ function composite (images) {
|
|||||||
throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
|
throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is.defined(image.premultiplied)) {
|
||||||
|
if (is.bool(image.premultiplied)) {
|
||||||
|
composite.premultiplied = image.premultiplied;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return composite;
|
return composite;
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function overlayWith (input, options) {
|
|
||||||
const blend = (is.object(options) && options.cutout) ? 'dest-in' : 'over';
|
|
||||||
return this.composite([Object.assign({ input, blend }, options)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorate the Sharp prototype with composite-related functions.
|
* Decorate the Sharp prototype with composite-related functions.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
module.exports = function (Sharp) {
|
module.exports = function (Sharp) {
|
||||||
Sharp.prototype.composite = composite;
|
Sharp.prototype.composite = composite;
|
||||||
Sharp.prototype.overlayWith = deprecate(overlayWith, 'overlayWith(input, options) is deprecated, use composite([{ input, ...options }]) instead');
|
|
||||||
Sharp.blend = blend;
|
Sharp.blend = blend;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
const events = require('events');
|
const events = require('events');
|
||||||
@@ -9,6 +8,7 @@ const is = require('./is');
|
|||||||
require('./libvips').hasVendoredLibvips();
|
require('./libvips').hasVendoredLibvips();
|
||||||
|
|
||||||
let sharp;
|
let sharp;
|
||||||
|
/* istanbul ignore next */
|
||||||
try {
|
try {
|
||||||
sharp = require('../build/Release/sharp.node');
|
sharp = require('../build/Release/sharp.node');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -78,7 +78,7 @@ const debuglog = util.debuglog('sharp');
|
|||||||
*
|
*
|
||||||
* @param {(Buffer|String)} [input] - if present, can be
|
* @param {(Buffer|String)} [input] - if present, can be
|
||||||
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
||||||
* a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
* a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
||||||
* JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
* JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||||
* @param {Object} [options] - if present, is an Object with optional attributes.
|
* @param {Object} [options] - if present, is an Object with optional attributes.
|
||||||
* @param {Boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
|
* @param {Boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
|
||||||
@@ -110,8 +110,6 @@ const Sharp = function (input, options) {
|
|||||||
// input options
|
// input options
|
||||||
sequentialRead: false,
|
sequentialRead: false,
|
||||||
limitInputPixels: Math.pow(0x3FFF, 2),
|
limitInputPixels: Math.pow(0x3FFF, 2),
|
||||||
// ICC profiles
|
|
||||||
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
|
|
||||||
// resize options
|
// resize options
|
||||||
topOffsetPre: -1,
|
topOffsetPre: -1,
|
||||||
leftOffsetPre: -1,
|
leftOffsetPre: -1,
|
||||||
@@ -197,6 +195,8 @@ const Sharp = function (input, options) {
|
|||||||
webpAlphaQuality: 100,
|
webpAlphaQuality: 100,
|
||||||
webpLossless: false,
|
webpLossless: false,
|
||||||
webpNearLossless: false,
|
webpNearLossless: false,
|
||||||
|
webpSmartSubsample: false,
|
||||||
|
webpReductionEffort: 4,
|
||||||
tiffQuality: 80,
|
tiffQuality: 80,
|
||||||
tiffCompression: 'jpeg',
|
tiffCompression: 'jpeg',
|
||||||
tiffPredictor: 'horizontal',
|
tiffPredictor: 'horizontal',
|
||||||
@@ -207,8 +207,13 @@ const Sharp = function (input, options) {
|
|||||||
tiffTileWidth: 256,
|
tiffTileWidth: 256,
|
||||||
tiffXres: 1.0,
|
tiffXres: 1.0,
|
||||||
tiffYres: 1.0,
|
tiffYres: 1.0,
|
||||||
|
heifQuality: 80,
|
||||||
|
heifLossless: false,
|
||||||
|
heifCompression: 'hevc',
|
||||||
tileSize: 256,
|
tileSize: 256,
|
||||||
tileOverlap: 0,
|
tileOverlap: 0,
|
||||||
|
tileSkipBlanks: -1,
|
||||||
|
tileBackground: [255, 255, 255, 255],
|
||||||
linearA: 1,
|
linearA: 1,
|
||||||
linearB: 0,
|
linearB: 0,
|
||||||
// Function to notify of libvips warnings
|
// Function to notify of libvips warnings
|
||||||
|
|||||||
BIN
lib/icc/cmyk.icm
BIN
lib/icc/sRGB.icc
63
lib/input.js
@@ -19,7 +19,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
||||||
// Plain Object descriptor, e.g. create
|
// Plain Object descriptor, e.g. create
|
||||||
inputOptions = input;
|
inputOptions = input;
|
||||||
if (is.plainObject(inputOptions.raw)) {
|
if (is.plainObject(inputOptions.raw) || is.bool(inputOptions.failOnError)) {
|
||||||
// Raw Stream
|
// Raw Stream
|
||||||
inputDescriptor.buffer = [];
|
inputDescriptor.buffer = [];
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
if (is.bool(inputOptions.failOnError)) {
|
if (is.bool(inputOptions.failOnError)) {
|
||||||
inputDescriptor.failOnError = inputOptions.failOnError;
|
inputDescriptor.failOnError = inputOptions.failOnError;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid failOnError (boolean) ' + inputOptions.failOnError);
|
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Density
|
// Density
|
||||||
@@ -43,7 +43,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
if (is.inRange(inputOptions.density, 1, 2400)) {
|
if (is.inRange(inputOptions.density, 1, 2400)) {
|
||||||
inputDescriptor.density = inputOptions.density;
|
inputDescriptor.density = inputOptions.density;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid density (1 to 2400) ' + inputOptions.density);
|
throw is.invalidParameterError('density', 'number between 1 and 2400', inputOptions.density);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Raw pixel input
|
// Raw pixel input
|
||||||
@@ -65,11 +65,15 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
if (is.defined(inputOptions.pages)) {
|
if (is.defined(inputOptions.pages)) {
|
||||||
if (is.integer(inputOptions.pages) && is.inRange(inputOptions.pages, -1, 100000)) {
|
if (is.integer(inputOptions.pages) && is.inRange(inputOptions.pages, -1, 100000)) {
|
||||||
inputDescriptor.pages = inputOptions.pages;
|
inputDescriptor.pages = inputOptions.pages;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('pages', 'integer between -1 and 100000', inputOptions.pages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.defined(inputOptions.page)) {
|
if (is.defined(inputOptions.page)) {
|
||||||
if (is.integer(inputOptions.page) && is.inRange(inputOptions.page, 0, 100000)) {
|
if (is.integer(inputOptions.page) && is.inRange(inputOptions.page, 0, 100000)) {
|
||||||
inputDescriptor.page = inputOptions.page;
|
inputDescriptor.page = inputOptions.page;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create new image
|
// Create new image
|
||||||
@@ -115,9 +119,8 @@ function _write (chunk, encoding, callback) {
|
|||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (is.buffer(chunk)) {
|
if (is.buffer(chunk)) {
|
||||||
if (this.options.input.buffer.length === 0) {
|
if (this.options.input.buffer.length === 0) {
|
||||||
const that = this;
|
this.on('finish', () => {
|
||||||
this.on('finish', function () {
|
this.streamInFinished = true;
|
||||||
that.streamInFinished = true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.options.input.buffer.push(chunk);
|
this.options.input.buffer.push(chunk);
|
||||||
@@ -165,16 +168,15 @@ function _isStreamInput () {
|
|||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
*/
|
*/
|
||||||
function clone () {
|
function clone () {
|
||||||
const that = this;
|
|
||||||
// Clone existing options
|
// Clone existing options
|
||||||
const clone = this.constructor.call();
|
const clone = this.constructor.call();
|
||||||
clone.options = Object.assign({}, this.options);
|
clone.options = Object.assign({}, this.options);
|
||||||
// Pass 'finish' event to clone for Stream-based input
|
// Pass 'finish' event to clone for Stream-based input
|
||||||
if (this._isStreamInput()) {
|
if (this._isStreamInput()) {
|
||||||
this.on('finish', function () {
|
this.on('finish', () => {
|
||||||
// Clone inherits input data
|
// Clone inherits input data
|
||||||
that._flattenBufferIn();
|
this._flattenBufferIn();
|
||||||
clone.options.bufferIn = that.options.bufferIn;
|
clone.options.bufferIn = this.options.bufferIn;
|
||||||
clone.emit('finish');
|
clone.emit('finish');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -195,8 +197,9 @@ function clone () {
|
|||||||
* - `density`: Number of pixels per inch (DPI), if present
|
* - `density`: Number of pixels per inch (DPI), if present
|
||||||
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||||
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, PDF, animated GIF and animated WebP
|
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
||||||
* - `pageHeight`: Number of pixels high each page in this PDF image will be.
|
* - `pageHeight`: Number of pixels high each page in this PDF image will be.
|
||||||
|
* - `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
||||||
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||||
* - `orientation`: Number value of the EXIF Orientation header, if present
|
* - `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
@@ -223,12 +226,11 @@ function clone () {
|
|||||||
* @returns {Promise<Object>|Sharp}
|
* @returns {Promise<Object>|Sharp}
|
||||||
*/
|
*/
|
||||||
function metadata (callback) {
|
function metadata (callback) {
|
||||||
const that = this;
|
|
||||||
if (is.fn(callback)) {
|
if (is.fn(callback)) {
|
||||||
if (this._isStreamInput()) {
|
if (this._isStreamInput()) {
|
||||||
this.on('finish', function () {
|
this.on('finish', () => {
|
||||||
that._flattenBufferIn();
|
this._flattenBufferIn();
|
||||||
sharp.metadata(that.options, callback);
|
sharp.metadata(this.options, callback);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
sharp.metadata(this.options, callback);
|
sharp.metadata(this.options, callback);
|
||||||
@@ -236,10 +238,10 @@ function metadata (callback) {
|
|||||||
return this;
|
return this;
|
||||||
} else {
|
} else {
|
||||||
if (this._isStreamInput()) {
|
if (this._isStreamInput()) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise((resolve, reject) => {
|
||||||
that.on('finish', function () {
|
this.on('finish', () => {
|
||||||
that._flattenBufferIn();
|
this._flattenBufferIn();
|
||||||
sharp.metadata(that.options, function (err, metadata) {
|
sharp.metadata(this.options, (err, metadata) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -249,8 +251,8 @@ function metadata (callback) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise((resolve, reject) => {
|
||||||
sharp.metadata(that.options, function (err, metadata) {
|
sharp.metadata(this.options, (err, metadata) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -292,12 +294,11 @@ function metadata (callback) {
|
|||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
function stats (callback) {
|
function stats (callback) {
|
||||||
const that = this;
|
|
||||||
if (is.fn(callback)) {
|
if (is.fn(callback)) {
|
||||||
if (this._isStreamInput()) {
|
if (this._isStreamInput()) {
|
||||||
this.on('finish', function () {
|
this.on('finish', () => {
|
||||||
that._flattenBufferIn();
|
this._flattenBufferIn();
|
||||||
sharp.stats(that.options, callback);
|
sharp.stats(this.options, callback);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
sharp.stats(this.options, callback);
|
sharp.stats(this.options, callback);
|
||||||
@@ -305,10 +306,10 @@ function stats (callback) {
|
|||||||
return this;
|
return this;
|
||||||
} else {
|
} else {
|
||||||
if (this._isStreamInput()) {
|
if (this._isStreamInput()) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise((resolve, reject) => {
|
||||||
that.on('finish', function () {
|
this.on('finish', function () {
|
||||||
that._flattenBufferIn();
|
this._flattenBufferIn();
|
||||||
sharp.stats(that.options, function (err, stats) {
|
sharp.stats(this.options, (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -318,8 +319,8 @@ function stats (callback) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise((resolve, reject) => {
|
||||||
sharp.stats(that.options, function (err, stats) {
|
sharp.stats(this.options, (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ const semver = require('semver');
|
|||||||
const platform = require('./platform');
|
const platform = require('./platform');
|
||||||
|
|
||||||
const env = process.env;
|
const env = process.env;
|
||||||
const minimumLibvipsVersion = env.npm_package_config_libvips || require('../package.json').config.libvips;
|
const minimumLibvipsVersion = env.npm_package_config_libvips || /* istanbul ignore next */
|
||||||
|
require('../package.json').config.libvips;
|
||||||
|
|
||||||
const spawnSyncOptions = {
|
const spawnSyncOptions = {
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
@@ -19,6 +20,7 @@ const mkdirSync = function (dirPath) {
|
|||||||
try {
|
try {
|
||||||
fs.mkdirSync(dirPath);
|
fs.mkdirSync(dirPath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
/* istanbul ignore if */
|
||||||
if (err.code !== 'EEXIST') {
|
if (err.code !== 'EEXIST') {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -26,7 +28,8 @@ const mkdirSync = function (dirPath) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cachePath = function () {
|
const cachePath = function () {
|
||||||
const npmCachePath = env.npm_config_cache || (env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
|
const npmCachePath = env.npm_config_cache || /* istanbul ignore next */
|
||||||
|
(env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
|
||||||
mkdirSync(npmCachePath);
|
mkdirSync(npmCachePath);
|
||||||
const libvipsCachePath = path.join(npmCachePath, '_libvips');
|
const libvipsCachePath = path.join(npmCachePath, '_libvips');
|
||||||
mkdirSync(libvipsCachePath);
|
mkdirSync(libvipsCachePath);
|
||||||
@@ -51,17 +54,21 @@ const hasVendoredLibvips = function () {
|
|||||||
vendorVersionId = require(path.join(vendorPath, 'versions.json')).vips;
|
vendorVersionId = require(path.join(vendorPath, 'versions.json')).vips;
|
||||||
vendorPlatformId = require(path.join(vendorPath, 'platform.json'));
|
vendorPlatformId = require(path.join(vendorPath, 'platform.json'));
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
|
/* istanbul ignore if */
|
||||||
if (vendorVersionId && vendorVersionId !== minimumLibvipsVersion) {
|
if (vendorVersionId && vendorVersionId !== minimumLibvipsVersion) {
|
||||||
throw new Error(`Found vendored libvips v${vendorVersionId} but require v${minimumLibvipsVersion}. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
|
throw new Error(`Found vendored libvips v${vendorVersionId} but require v${minimumLibvipsVersion}. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
|
||||||
}
|
}
|
||||||
|
/* istanbul ignore else */
|
||||||
if (vendorPlatformId) {
|
if (vendorPlatformId) {
|
||||||
|
/* istanbul ignore else */
|
||||||
if (currentPlatformId === vendorPlatformId) {
|
if (currentPlatformId === vendorPlatformId) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
|
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const pkgConfigPath = function () {
|
const pkgConfigPath = function () {
|
||||||
@@ -81,7 +88,8 @@ const useGlobalLibvips = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const globalVipsVersion = globalLibvipsVersion();
|
const globalVipsVersion = globalLibvipsVersion();
|
||||||
return !!globalVipsVersion && semver.gte(globalVipsVersion, minimumLibvipsVersion);
|
return !!globalVipsVersion && /* istanbul ignore next */
|
||||||
|
semver.gte(globalVipsVersion, minimumLibvipsVersion);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ function rotate (angle, options) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unsupported angle: must be a number.');
|
throw is.invalidParameterError('angle', 'numeric', angle);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ function sharpen (sigma, flat, jagged) {
|
|||||||
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
|
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
|
||||||
this.options.sharpenFlat = flat;
|
this.options.sharpenFlat = flat;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid sharpen level for flat areas (0.0 - 10000.0) ' + flat);
|
throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Control over jagged areas
|
// Control over jagged areas
|
||||||
@@ -117,11 +117,11 @@ function sharpen (sigma, flat, jagged) {
|
|||||||
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
|
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
|
||||||
this.options.sharpenJagged = jagged;
|
this.options.sharpenJagged = jagged;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid sharpen level for jagged areas (0.0 - 10000.0) ' + jagged);
|
throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid sharpen sigma (0.01 - 10000) ' + sigma);
|
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', sigma);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ function median (size) {
|
|||||||
// Numeric argument: specific sigma
|
// Numeric argument: specific sigma
|
||||||
this.options.medianSize = size;
|
this.options.medianSize = size;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid median size ' + size);
|
throw is.invalidParameterError('size', 'integer between 1 and 1000', size);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,7 @@ function blur (sigma) {
|
|||||||
// Numeric argument: specific sigma
|
// Numeric argument: specific sigma
|
||||||
this.options.blurSigma = sigma;
|
this.options.blurSigma = sigma;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid blur sigma (0.3 - 1000.0) ' + sigma);
|
throw is.invalidParameterError('sigma', 'number between 0.3 and 1000', sigma);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ function blur (sigma) {
|
|||||||
function flatten (options) {
|
function flatten (options) {
|
||||||
this.options.flatten = is.bool(options) ? options : true;
|
this.options.flatten = is.bool(options) ? options : true;
|
||||||
if (is.object(options)) {
|
if (is.object(options)) {
|
||||||
this._setColourOption('flattenBackground', options.background);
|
this._setBackgroundColourOption('flattenBackground', options.background);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ function gamma (gamma, gammaOut) {
|
|||||||
} else if (is.number(gamma) && is.inRange(gamma, 1, 3)) {
|
} else if (is.number(gamma) && is.inRange(gamma, 1, 3)) {
|
||||||
this.options.gamma = gamma;
|
this.options.gamma = gamma;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma);
|
throw is.invalidParameterError('gamma', 'number between 1.0 and 3.0', gamma);
|
||||||
}
|
}
|
||||||
if (!is.defined(gammaOut)) {
|
if (!is.defined(gammaOut)) {
|
||||||
// Default gamma correction for output is same as input
|
// Default gamma correction for output is same as input
|
||||||
@@ -213,7 +213,7 @@ function gamma (gamma, gammaOut) {
|
|||||||
} else if (is.number(gammaOut) && is.inRange(gammaOut, 1, 3)) {
|
} else if (is.number(gammaOut) && is.inRange(gammaOut, 1, 3)) {
|
||||||
this.options.gammaOut = gammaOut;
|
this.options.gammaOut = gammaOut;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid output gamma correction (1.0 to 3.0) ' + gammaOut);
|
throw is.invalidParameterError('gammaOut', 'number between 1.0 and 3.0', gammaOut);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ function threshold (threshold, options) {
|
|||||||
} else if (is.integer(threshold) && is.inRange(threshold, 0, 255)) {
|
} else if (is.integer(threshold) && is.inRange(threshold, 0, 255)) {
|
||||||
this.options.threshold = threshold;
|
this.options.threshold = threshold;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid threshold (0 to 255) ' + threshold);
|
throw is.invalidParameterError('threshold', 'integer between 0 and 255', threshold);
|
||||||
}
|
}
|
||||||
if (!is.object(options) || options.greyscale === true || options.grayscale === true) {
|
if (!is.object(options) || options.greyscale === true || options.grayscale === true) {
|
||||||
this.options.thresholdGrayscale = true;
|
this.options.thresholdGrayscale = true;
|
||||||
@@ -346,7 +346,7 @@ function boolean (operand, operator, options) {
|
|||||||
if (is.string(operator) && is.inArray(operator, ['and', 'or', 'eor'])) {
|
if (is.string(operator) && is.inArray(operator, ['and', 'or', 'eor'])) {
|
||||||
this.options.booleanOp = operator;
|
this.options.booleanOp = operator;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid boolean operator ' + operator);
|
throw is.invalidParameterError('operator', 'one of: and, or, eor', operator);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -364,17 +364,15 @@ function linear (a, b) {
|
|||||||
} else if (is.number(a)) {
|
} else if (is.number(a)) {
|
||||||
this.options.linearA = a;
|
this.options.linearA = a;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid linear transform multiplier ' + a);
|
throw is.invalidParameterError('a', 'numeric', a);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is.defined(b)) {
|
if (!is.defined(b)) {
|
||||||
this.options.linearB = 0.0;
|
this.options.linearB = 0.0;
|
||||||
} else if (is.number(b)) {
|
} else if (is.number(b)) {
|
||||||
this.options.linearB = b;
|
this.options.linearB = b;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid linear transform offset ' + b);
|
throw is.invalidParameterError('b', 'numeric', b);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +403,7 @@ function recomb (inputMatrix) {
|
|||||||
inputMatrix[2].length !== 3
|
inputMatrix[2].length !== 3
|
||||||
) {
|
) {
|
||||||
// must pass in a kernel
|
// must pass in a kernel
|
||||||
throw new Error('Invalid Recomb Matrix');
|
throw new Error('Invalid recombination matrix');
|
||||||
}
|
}
|
||||||
this.options.recombMatrix = [
|
this.options.recombMatrix = [
|
||||||
inputMatrix[0][0], inputMatrix[0][1], inputMatrix[0][2],
|
inputMatrix[0][0], inputMatrix[0][1], inputMatrix[0][2],
|
||||||
|
|||||||
322
lib/output.js
@@ -10,6 +10,9 @@ const sharp = require('../build/Release/sharp.node');
|
|||||||
* with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
|
* with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
|
||||||
* Note that raw pixel data is only supported for buffer output.
|
* Note that raw pixel data is only supported for buffer output.
|
||||||
*
|
*
|
||||||
|
* By default all metadata will be removed, which includes EXIF-based orientation.
|
||||||
|
* See {@link withMetadata} for control over this.
|
||||||
|
*
|
||||||
* A `Promise` is returned when `callback` is not provided.
|
* A `Promise` is returned when `callback` is not provided.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
@@ -57,7 +60,11 @@ function toFile (fileOut, callback) {
|
|||||||
/**
|
/**
|
||||||
* Write output to a Buffer.
|
* Write output to a Buffer.
|
||||||
* JPEG, PNG, WebP, TIFF and RAW output are supported.
|
* JPEG, PNG, WebP, TIFF and RAW output are supported.
|
||||||
* By default, the format will match the input image, except GIF and SVG input which become PNG output.
|
*
|
||||||
|
* If no explicit format is set, the output format will match the input image, except GIF and SVG input which become PNG output.
|
||||||
|
*
|
||||||
|
* By default all metadata will be removed, which includes EXIF-based orientation.
|
||||||
|
* See {@link withMetadata} for control over this.
|
||||||
*
|
*
|
||||||
* `callback`, if present, gets three arguments `(err, data, info)` where:
|
* `callback`, if present, gets three arguments `(err, data, info)` where:
|
||||||
* - `err` is an error, if any.
|
* - `err` is an error, if any.
|
||||||
@@ -91,9 +98,9 @@ function toFile (fileOut, callback) {
|
|||||||
*/
|
*/
|
||||||
function toBuffer (options, callback) {
|
function toBuffer (options, callback) {
|
||||||
if (is.object(options)) {
|
if (is.object(options)) {
|
||||||
if (is.bool(options.resolveWithObject)) {
|
this._setBooleanOption('resolveWithObject', options.resolveWithObject);
|
||||||
this.options.resolveWithObject = options.resolveWithObject;
|
} else if (this.options.resolveWithObject) {
|
||||||
}
|
this.options.resolveWithObject = false;
|
||||||
}
|
}
|
||||||
return this._pipeline(is.fn(options) ? options : callback);
|
return this._pipeline(is.fn(options) ? options : callback);
|
||||||
}
|
}
|
||||||
@@ -109,19 +116,19 @@ function toBuffer (options, callback) {
|
|||||||
* .toFile('output-with-metadata.jpg')
|
* .toFile('output-with-metadata.jpg')
|
||||||
* .then(info => { ... });
|
* .then(info => { ... });
|
||||||
*
|
*
|
||||||
* @param {Object} [withMetadata]
|
* @param {Object} [options]
|
||||||
* @param {Number} [withMetadata.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
|
* @param {Number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
function withMetadata (withMetadata) {
|
function withMetadata (options) {
|
||||||
this.options.withMetadata = is.bool(withMetadata) ? withMetadata : true;
|
this.options.withMetadata = is.bool(options) ? options : true;
|
||||||
if (is.object(withMetadata)) {
|
if (is.object(options)) {
|
||||||
if (is.defined(withMetadata.orientation)) {
|
if (is.defined(options.orientation)) {
|
||||||
if (is.integer(withMetadata.orientation) && is.inRange(withMetadata.orientation, 1, 8)) {
|
if (is.integer(options.orientation) && is.inRange(options.orientation, 1, 8)) {
|
||||||
this.options.withMetadataOrientation = withMetadata.orientation;
|
this.options.withMetadataOrientation = options.orientation;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid orientation (1 to 8) ' + withMetadata.orientation);
|
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +169,7 @@ function jpeg (options) {
|
|||||||
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||||
this.options.jpegQuality = options.quality;
|
this.options.jpegQuality = options.quality;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
|
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.defined(options.progressive)) {
|
if (is.defined(options.progressive)) {
|
||||||
@@ -172,7 +179,7 @@ function jpeg (options) {
|
|||||||
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
|
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
|
||||||
this.options.jpegChromaSubsampling = options.chromaSubsampling;
|
this.options.jpegChromaSubsampling = options.chromaSubsampling;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid chromaSubsampling (4:2:0, 4:4:4) ' + options.chromaSubsampling);
|
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
|
const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
|
||||||
@@ -198,7 +205,7 @@ function jpeg (options) {
|
|||||||
if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
|
if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
|
||||||
this.options.jpegQuantisationTable = quantisationTable;
|
this.options.jpegQuantisationTable = quantisationTable;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid quantisation table (integer, 0-8) ' + quantisationTable);
|
throw is.invalidParameterError('quantisationTable', 'integer between 0 and 8', quantisationTable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,7 +246,7 @@ function png (options) {
|
|||||||
if (is.integer(options.compressionLevel) && is.inRange(options.compressionLevel, 0, 9)) {
|
if (is.integer(options.compressionLevel) && is.inRange(options.compressionLevel, 0, 9)) {
|
||||||
this.options.pngCompressionLevel = options.compressionLevel;
|
this.options.pngCompressionLevel = options.compressionLevel;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid compressionLevel (integer, 0-9) ' + options.compressionLevel);
|
throw is.invalidParameterError('compressionLevel', 'integer between 0 and 9', options.compressionLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.defined(options.adaptiveFiltering)) {
|
if (is.defined(options.adaptiveFiltering)) {
|
||||||
@@ -290,6 +297,8 @@ function png (options) {
|
|||||||
* @param {Number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
|
* @param {Number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
|
||||||
* @param {Boolean} [options.lossless=false] - use lossless compression mode
|
* @param {Boolean} [options.lossless=false] - use lossless compression mode
|
||||||
* @param {Boolean} [options.nearLossless=false] - use near_lossless compression mode
|
* @param {Boolean} [options.nearLossless=false] - use near_lossless compression mode
|
||||||
|
* @param {Boolean} [options.smartSubsample=false] - use high quality chroma subsampling
|
||||||
|
* @param {Number} [options.reductionEffort=4] - level of CPU effort to reduce file size, integer 0-6
|
||||||
* @param {Boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
|
* @param {Boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
@@ -299,14 +308,14 @@ function webp (options) {
|
|||||||
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||||
this.options.webpQuality = options.quality;
|
this.options.webpQuality = options.quality;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
|
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.object(options) && is.defined(options.alphaQuality)) {
|
if (is.object(options) && is.defined(options.alphaQuality)) {
|
||||||
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
|
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
|
||||||
this.options.webpAlphaQuality = options.alphaQuality;
|
this.options.webpAlphaQuality = options.alphaQuality;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid webp alpha quality (integer, 0-100) ' + options.alphaQuality);
|
throw is.invalidParameterError('alphaQuality', 'integer between 0 and 100', options.alphaQuality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.object(options) && is.defined(options.lossless)) {
|
if (is.object(options) && is.defined(options.lossless)) {
|
||||||
@@ -315,6 +324,16 @@ function webp (options) {
|
|||||||
if (is.object(options) && is.defined(options.nearLossless)) {
|
if (is.object(options) && is.defined(options.nearLossless)) {
|
||||||
this._setBooleanOption('webpNearLossless', options.nearLossless);
|
this._setBooleanOption('webpNearLossless', options.nearLossless);
|
||||||
}
|
}
|
||||||
|
if (is.object(options) && is.defined(options.smartSubsample)) {
|
||||||
|
this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
|
||||||
|
}
|
||||||
|
if (is.object(options) && is.defined(options.reductionEffort)) {
|
||||||
|
if (is.integer(options.reductionEffort) && is.inRange(options.reductionEffort, 0, 6)) {
|
||||||
|
this.options.webpReductionEffort = options.reductionEffort;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('reductionEffort', 'integer between 0 and 6', options.reductionEffort);
|
||||||
|
}
|
||||||
|
}
|
||||||
return this._updateFormatOut('webp', options);
|
return this._updateFormatOut('webp', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,59 +371,47 @@ function tiff (options) {
|
|||||||
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||||
this.options.tiffQuality = options.quality;
|
this.options.tiffQuality = options.quality;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
|
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.defined(options.squash)) {
|
if (is.defined(options.squash)) {
|
||||||
if (is.bool(options.squash)) {
|
this._setBooleanOption('tiffSquash', options.squash);
|
||||||
this.options.tiffSquash = options.squash;
|
|
||||||
} else {
|
|
||||||
throw new Error('Invalid Value for squash ' + options.squash + ' Only Boolean Values allowed for options.squash.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// tiling
|
// tiling
|
||||||
if (is.defined(options.tile)) {
|
if (is.defined(options.tile)) {
|
||||||
if (is.bool(options.tile)) {
|
this._setBooleanOption('tiffTile', options.tile);
|
||||||
this.options.tiffTile = options.tile;
|
|
||||||
} else {
|
|
||||||
throw new Error('Invalid Value for tile ' + options.tile + ' Only Boolean values allowed for options.tile');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (is.defined(options.tileWidth)) {
|
if (is.defined(options.tileWidth)) {
|
||||||
if (is.number(options.tileWidth) && options.tileWidth > 0) {
|
if (is.integer(options.tileWidth) && options.tileWidth > 0) {
|
||||||
this.options.tiffTileWidth = options.tileWidth;
|
this.options.tiffTileWidth = options.tileWidth;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid Value for tileWidth ' + options.tileWidth + ' Only positive numeric values allowed for options.tileWidth');
|
throw is.invalidParameterError('tileWidth', 'integer greater than zero', options.tileWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.defined(options.tileHeight)) {
|
if (is.defined(options.tileHeight)) {
|
||||||
if (is.number(options.tileHeight) && options.tileHeight > 0) {
|
if (is.integer(options.tileHeight) && options.tileHeight > 0) {
|
||||||
this.options.tiffTileHeight = options.tileHeight;
|
this.options.tiffTileHeight = options.tileHeight;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid Value for tileHeight ' + options.tileHeight + ' Only positive numeric values allowed for options.tileHeight');
|
throw is.invalidParameterError('tileHeight', 'integer greater than zero', options.tileHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// pyramid
|
// pyramid
|
||||||
if (is.defined(options.pyramid)) {
|
if (is.defined(options.pyramid)) {
|
||||||
if (is.bool(options.pyramid)) {
|
this._setBooleanOption('tiffPyramid', options.pyramid);
|
||||||
this.options.tiffPyramid = options.pyramid;
|
|
||||||
} else {
|
|
||||||
throw new Error('Invalid Value for pyramid ' + options.pyramid + ' Only Boolean values allowed for options.pyramid');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// resolution
|
// resolution
|
||||||
if (is.defined(options.xres)) {
|
if (is.defined(options.xres)) {
|
||||||
if (is.number(options.xres)) {
|
if (is.number(options.xres) && options.xres > 0) {
|
||||||
this.options.tiffXres = options.xres;
|
this.options.tiffXres = options.xres;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid Value for xres ' + options.xres + ' Only numeric values allowed for options.xres');
|
throw is.invalidParameterError('xres', 'number greater than zero', options.xres);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.defined(options.yres)) {
|
if (is.defined(options.yres)) {
|
||||||
if (is.number(options.yres)) {
|
if (is.number(options.yres) && options.yres > 0) {
|
||||||
this.options.tiffYres = options.yres;
|
this.options.tiffYres = options.yres;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid Value for yres ' + options.yres + ' Only numeric values allowed for options.yres');
|
throw is.invalidParameterError('yres', 'number greater than zero', options.yres);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// compression
|
// compression
|
||||||
@@ -412,8 +419,7 @@ function tiff (options) {
|
|||||||
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
|
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
|
||||||
this.options.tiffCompression = options.compression;
|
this.options.tiffCompression = options.compression;
|
||||||
} else {
|
} else {
|
||||||
const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, ccittfax4, none`;
|
throw is.invalidParameterError('compression', 'one of: lzw, deflate, jpeg, ccittfax4, none', options.compression);
|
||||||
throw new Error(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// predictor
|
// predictor
|
||||||
@@ -421,14 +427,60 @@ function tiff (options) {
|
|||||||
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
|
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
|
||||||
this.options.tiffPredictor = options.predictor;
|
this.options.tiffPredictor = options.predictor;
|
||||||
} else {
|
} else {
|
||||||
const message = `Invalid predictor option "${options.predictor}". Should be one of: none, horizontal, float`;
|
throw is.invalidParameterError('predictor', 'one of: none, horizontal, float', options.predictor);
|
||||||
throw new Error(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this._updateFormatOut('tiff', options);
|
return this._updateFormatOut('tiff', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use these HEIF options for output image.
|
||||||
|
*
|
||||||
|
* Support for HEIF (HEIC/AVIF) is experimental.
|
||||||
|
* Do not use this in production systems.
|
||||||
|
*
|
||||||
|
* Requires a custom, globally-installed libvips compiled with support for libheif.
|
||||||
|
*
|
||||||
|
* Most versions of libheif support only the patent-encumbered HEVC compression format.
|
||||||
|
*
|
||||||
|
* @param {Object} [options] - output options
|
||||||
|
* @param {Number} [options.quality=80] - quality, integer 1-100
|
||||||
|
* @param {Boolean} [options.compression='hevc'] - compression format: hevc, avc, jpeg, av1
|
||||||
|
* @param {Boolean} [options.lossless=false] - use lossless compression
|
||||||
|
* @returns {Sharp}
|
||||||
|
* @throws {Error} Invalid options
|
||||||
|
*/
|
||||||
|
function heif (options) {
|
||||||
|
if (!this.constructor.format.heif.output.buffer) {
|
||||||
|
throw new Error('The heif operation requires libvips to have been installed with support for libheif');
|
||||||
|
}
|
||||||
|
if (is.object(options)) {
|
||||||
|
if (is.defined(options.quality)) {
|
||||||
|
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||||
|
this.options.heifQuality = options.quality;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is.defined(options.lossless)) {
|
||||||
|
if (is.bool(options.lossless)) {
|
||||||
|
this.options.heifLossless = options.lossless;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is.defined(options.compression)) {
|
||||||
|
if (is.string(options.compression) && is.inArray(options.compression, ['hevc', 'avc', 'jpeg', 'av1'])) {
|
||||||
|
this.options.heifCompression = options.compression;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('compression', 'one of: hevc, avc, jpeg, av1', options.compression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._updateFormatOut('heif', options);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force output to be raw, uncompressed uint8 pixel data.
|
* Force output to be raw, uncompressed uint8 pixel data.
|
||||||
*
|
*
|
||||||
@@ -444,6 +496,17 @@ function raw () {
|
|||||||
return this._updateFormatOut('raw');
|
return this._updateFormatOut('raw');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formats = new Map([
|
||||||
|
['heic', 'heif'],
|
||||||
|
['heif', 'heif'],
|
||||||
|
['jpeg', 'jpeg'],
|
||||||
|
['jpg', 'jpeg'],
|
||||||
|
['png', 'png'],
|
||||||
|
['raw', 'raw'],
|
||||||
|
['tiff', 'tiff'],
|
||||||
|
['webp', 'webp']
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force output to a given format.
|
* Force output to a given format.
|
||||||
*
|
*
|
||||||
@@ -459,14 +522,11 @@ function raw () {
|
|||||||
* @throws {Error} unsupported format or options
|
* @throws {Error} unsupported format or options
|
||||||
*/
|
*/
|
||||||
function toFormat (format, options) {
|
function toFormat (format, options) {
|
||||||
if (is.object(format) && is.string(format.id)) {
|
const actualFormat = formats.get(is.object(format) && is.string(format.id) ? format.id : format);
|
||||||
format = format.id;
|
if (!actualFormat) {
|
||||||
|
throw is.invalidParameterError('format', `one of: ${[...formats.keys()].join(', ')}`, format);
|
||||||
}
|
}
|
||||||
if (format === 'jpg') format = 'jpeg';
|
return this[actualFormat](options);
|
||||||
if (!is.inArray(format, ['jpeg', 'png', 'webp', 'tiff', 'raw'])) {
|
|
||||||
throw new Error('Unsupported output format ' + format);
|
|
||||||
}
|
|
||||||
return this[format](options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -487,79 +547,90 @@ function toFormat (format, options) {
|
|||||||
* // output_files contains 512x512 tiles grouped by zoom level
|
* // output_files contains 512x512 tiles grouped by zoom level
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* @param {Object} [tile]
|
* @param {Object} [options]
|
||||||
* @param {Number} [tile.size=256] tile size in pixels, a value between 1 and 8192.
|
* @param {Number} [options.size=256] tile size in pixels, a value between 1 and 8192.
|
||||||
* @param {Number} [tile.overlap=0] tile overlap in pixels, a value between 0 and 8192.
|
* @param {Number} [options.overlap=0] tile overlap in pixels, a value between 0 and 8192.
|
||||||
* @param {Number} [tile.angle=0] tile angle of rotation, must be a multiple of 90.
|
* @param {Number} [options.angle=0] tile angle of rotation, must be a multiple of 90.
|
||||||
* @param {String} [tile.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
|
* @param {String|Object} [options.background={r: 255, g: 255, b: 255, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency.
|
||||||
* @param {String} [tile.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
|
* @param {String} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
|
||||||
* @param {String} [tile.layout='dz'] filesystem layout, possible values are `dz`, `zoomify` or `google`.
|
* @param {Number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images
|
||||||
|
* @param {String} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
|
||||||
|
* @param {String} [options.layout='dz'] filesystem layout, possible values are `dz`, `zoomify` or `google`.
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
function tile (tile) {
|
function tile (options) {
|
||||||
if (is.object(tile)) {
|
if (is.object(options)) {
|
||||||
// Size of square tiles, in pixels
|
// Size of square tiles, in pixels
|
||||||
if (is.defined(tile.size)) {
|
if (is.defined(options.size)) {
|
||||||
if (is.integer(tile.size) && is.inRange(tile.size, 1, 8192)) {
|
if (is.integer(options.size) && is.inRange(options.size, 1, 8192)) {
|
||||||
this.options.tileSize = tile.size;
|
this.options.tileSize = options.size;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid tile size (1 to 8192) ' + tile.size);
|
throw is.invalidParameterError('size', 'integer between 1 and 8192', options.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Overlap of tiles, in pixels
|
// Overlap of tiles, in pixels
|
||||||
if (is.defined(tile.overlap)) {
|
if (is.defined(options.overlap)) {
|
||||||
if (is.integer(tile.overlap) && is.inRange(tile.overlap, 0, 8192)) {
|
if (is.integer(options.overlap) && is.inRange(options.overlap, 0, 8192)) {
|
||||||
if (tile.overlap > this.options.tileSize) {
|
if (options.overlap > this.options.tileSize) {
|
||||||
throw new Error('Tile overlap ' + tile.overlap + ' cannot be larger than tile size ' + this.options.tileSize);
|
throw is.invalidParameterError('overlap', `<= size (${this.options.tileSize})`, options.overlap);
|
||||||
}
|
}
|
||||||
this.options.tileOverlap = tile.overlap;
|
this.options.tileOverlap = tile.overlap;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid tile overlap (0 to 8192) ' + tile.overlap);
|
throw is.invalidParameterError('overlap', 'integer between 0 and 8192', options.overlap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Container
|
// Container
|
||||||
if (is.defined(tile.container)) {
|
if (is.defined(options.container)) {
|
||||||
if (is.string(tile.container) && is.inArray(tile.container, ['fs', 'zip'])) {
|
if (is.string(options.container) && is.inArray(options.container, ['fs', 'zip'])) {
|
||||||
this.options.tileContainer = tile.container;
|
this.options.tileContainer = options.container;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid tile container ' + tile.container);
|
throw is.invalidParameterError('container', 'one of: fs, zip', options.container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Layout
|
// Layout
|
||||||
if (is.defined(tile.layout)) {
|
if (is.defined(options.layout)) {
|
||||||
if (is.string(tile.layout) && is.inArray(tile.layout, ['dz', 'google', 'zoomify'])) {
|
if (is.string(options.layout) && is.inArray(options.layout, ['dz', 'google', 'zoomify'])) {
|
||||||
this.options.tileLayout = tile.layout;
|
this.options.tileLayout = options.layout;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid tile layout ' + tile.layout);
|
throw is.invalidParameterError('layout', 'one of: dz, google, zoomify', options.layout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Angle of rotation,
|
// Angle of rotation,
|
||||||
if (is.defined(tile.angle)) {
|
if (is.defined(options.angle)) {
|
||||||
if (is.integer(tile.angle) && !(tile.angle % 90)) {
|
if (is.integer(options.angle) && !(options.angle % 90)) {
|
||||||
this.options.tileAngle = tile.angle;
|
this.options.tileAngle = options.angle;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unsupported angle: angle must be a positive/negative multiple of 90 ' + tile.angle);
|
throw is.invalidParameterError('angle', 'positive/negative multiple of 90', options.angle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Background colour
|
||||||
|
this._setBackgroundColourOption('tileBackground', options.background);
|
||||||
// Depth of tiles
|
// Depth of tiles
|
||||||
if (is.defined(tile.depth)) {
|
if (is.defined(options.depth)) {
|
||||||
if (is.string(tile.depth) && is.inArray(tile.depth, ['onepixel', 'onetile', 'one'])) {
|
if (is.string(options.depth) && is.inArray(options.depth, ['onepixel', 'onetile', 'one'])) {
|
||||||
this.options.tileDepth = tile.depth;
|
this.options.tileDepth = options.depth;
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid tile depth '" + tile.depth + "', should be one of 'onepixel', 'onetile' or 'one'");
|
throw is.invalidParameterError('depth', 'one of: onepixel, onetile, one', options.depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Threshold to skip blank tiles
|
||||||
|
if (is.defined(options.skipBlanks)) {
|
||||||
|
if (is.integer(options.skipBlanks) && is.inRange(options.skipBlanks, -1, 65535)) {
|
||||||
|
this.options.tileSkipBlanks = options.skipBlanks;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('skipBlanks', 'integer between -1 and 255/65535', options.skipBlanks);
|
||||||
|
}
|
||||||
|
} else if (is.defined(options.layout) && options.layout === 'google') {
|
||||||
|
this.options.tileSkipBlanks = 5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Format
|
// Format
|
||||||
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
|
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
|
||||||
this.options.tileFormat = this.options.formatOut;
|
this.options.tileFormat = this.options.formatOut;
|
||||||
} else if (this.options.formatOut !== 'input') {
|
} else if (this.options.formatOut !== 'input') {
|
||||||
throw new Error('Invalid tile format ' + this.options.formatOut);
|
throw is.invalidParameterError('format', 'one of: jpeg, png, webp', this.options.formatOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._updateFormatOut('dz');
|
return this._updateFormatOut('dz');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,7 +661,7 @@ function _setBooleanOption (key, val) {
|
|||||||
if (is.bool(val)) {
|
if (is.bool(val)) {
|
||||||
this.options[key] = val;
|
this.options[key] = val;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid ' + key + ' (boolean) ' + val);
|
throw is.invalidParameterError(key, 'boolean', val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -599,6 +670,7 @@ function _setBooleanOption (key, val) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function _read () {
|
function _read () {
|
||||||
|
/* istanbul ignore else */
|
||||||
if (!this.options.streamOut) {
|
if (!this.options.streamOut) {
|
||||||
this.options.streamOut = true;
|
this.options.streamOut = true;
|
||||||
this._pipeline();
|
this._pipeline();
|
||||||
@@ -611,14 +683,13 @@ function _read () {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function _pipeline (callback) {
|
function _pipeline (callback) {
|
||||||
const that = this;
|
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
// output=file/buffer
|
// output=file/buffer
|
||||||
if (this._isStreamInput()) {
|
if (this._isStreamInput()) {
|
||||||
// output=file/buffer, input=stream
|
// output=file/buffer, input=stream
|
||||||
this.on('finish', function () {
|
this.on('finish', () => {
|
||||||
that._flattenBufferIn();
|
this._flattenBufferIn();
|
||||||
sharp.pipeline(that.options, callback);
|
sharp.pipeline(this.options, callback);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// output=file/buffer, input=file/buffer
|
// output=file/buffer, input=file/buffer
|
||||||
@@ -629,41 +700,31 @@ function _pipeline (callback) {
|
|||||||
// output=stream
|
// output=stream
|
||||||
if (this._isStreamInput()) {
|
if (this._isStreamInput()) {
|
||||||
// output=stream, input=stream
|
// output=stream, input=stream
|
||||||
if (this.streamInFinished) {
|
this.once('finish', () => {
|
||||||
this._flattenBufferIn();
|
this._flattenBufferIn();
|
||||||
sharp.pipeline(this.options, function (err, data, info) {
|
sharp.pipeline(this.options, (err, data, info) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
that.emit('error', err);
|
this.emit('error', err);
|
||||||
} else {
|
} else {
|
||||||
that.emit('info', info);
|
this.emit('info', info);
|
||||||
that.push(data);
|
this.push(data);
|
||||||
}
|
}
|
||||||
that.push(null);
|
this.push(null);
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.on('finish', function () {
|
|
||||||
that._flattenBufferIn();
|
|
||||||
sharp.pipeline(that.options, function (err, data, info) {
|
|
||||||
if (err) {
|
|
||||||
that.emit('error', err);
|
|
||||||
} else {
|
|
||||||
that.emit('info', info);
|
|
||||||
that.push(data);
|
|
||||||
}
|
|
||||||
that.push(null);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
if (this.streamInFinished) {
|
||||||
|
this.emit('finish');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// output=stream, input=file/buffer
|
// output=stream, input=file/buffer
|
||||||
sharp.pipeline(this.options, function (err, data, info) {
|
sharp.pipeline(this.options, (err, data, info) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
that.emit('error', err);
|
this.emit('error', err);
|
||||||
} else {
|
} else {
|
||||||
that.emit('info', info);
|
this.emit('info', info);
|
||||||
that.push(data);
|
this.push(data);
|
||||||
}
|
}
|
||||||
that.push(null);
|
this.push(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@@ -671,15 +732,15 @@ function _pipeline (callback) {
|
|||||||
// output=promise
|
// output=promise
|
||||||
if (this._isStreamInput()) {
|
if (this._isStreamInput()) {
|
||||||
// output=promise, input=stream
|
// output=promise, input=stream
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise((resolve, reject) => {
|
||||||
that.on('finish', function () {
|
this.once('finish', () => {
|
||||||
that._flattenBufferIn();
|
this._flattenBufferIn();
|
||||||
sharp.pipeline(that.options, function (err, data, info) {
|
sharp.pipeline(this.options, (err, data, info) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
if (that.options.resolveWithObject) {
|
if (this.options.resolveWithObject) {
|
||||||
resolve({ data: data, info: info });
|
resolve({ data, info });
|
||||||
} else {
|
} else {
|
||||||
resolve(data);
|
resolve(data);
|
||||||
}
|
}
|
||||||
@@ -689,12 +750,12 @@ function _pipeline (callback) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// output=promise, input=file/buffer
|
// output=promise, input=file/buffer
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise((resolve, reject) => {
|
||||||
sharp.pipeline(that.options, function (err, data, info) {
|
sharp.pipeline(this.options, (err, data, info) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
if (that.options.resolveWithObject) {
|
if (this.options.resolveWithObject) {
|
||||||
resolve({ data: data, info: info });
|
resolve({ data: data, info: info });
|
||||||
} else {
|
} else {
|
||||||
resolve(data);
|
resolve(data);
|
||||||
@@ -720,6 +781,7 @@ module.exports = function (Sharp) {
|
|||||||
png,
|
png,
|
||||||
webp,
|
webp,
|
||||||
tiff,
|
tiff,
|
||||||
|
heif,
|
||||||
raw,
|
raw,
|
||||||
toFormat,
|
toFormat,
|
||||||
tile,
|
tile,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const env = process.env;
|
|||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
const arch = env.npm_config_arch || process.arch;
|
const arch = env.npm_config_arch || process.arch;
|
||||||
const platform = env.npm_config_platform || process.platform;
|
const platform = env.npm_config_platform || process.platform;
|
||||||
|
/* istanbul ignore next */
|
||||||
const libc = (platform === 'linux' && detectLibc.isNonGlibcLinux) ? detectLibc.family : '';
|
const libc = (platform === 'linux' && detectLibc.isNonGlibcLinux) ? detectLibc.family : '';
|
||||||
|
|
||||||
const platformId = [`${platform}${libc}`];
|
const platformId = [`${platform}${libc}`];
|
||||||
|
|||||||
@@ -210,12 +210,20 @@ function resize (width, height, options) {
|
|||||||
}
|
}
|
||||||
if (is.object(options)) {
|
if (is.object(options)) {
|
||||||
// Width
|
// Width
|
||||||
if (is.integer(options.width) && options.width > 0) {
|
if (is.defined(options.width)) {
|
||||||
this.options.width = options.width;
|
if (is.integer(options.width) && options.width > 0) {
|
||||||
|
this.options.width = options.width;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('width', 'positive integer', options.width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Height
|
// Height
|
||||||
if (is.integer(options.height) && options.height > 0) {
|
if (is.defined(options.height)) {
|
||||||
this.options.height = options.height;
|
if (is.integer(options.height) && options.height > 0) {
|
||||||
|
this.options.height = options.height;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('height', 'positive integer', options.height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Fit
|
// Fit
|
||||||
if (is.defined(options.fit)) {
|
if (is.defined(options.fit)) {
|
||||||
@@ -238,9 +246,7 @@ function resize (width, height, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Background
|
// Background
|
||||||
if (is.defined(options.background)) {
|
this._setBackgroundColourOption('resizeBackground', options.background);
|
||||||
this._setColourOption('resizeBackground', options.background);
|
|
||||||
}
|
|
||||||
// Kernel
|
// Kernel
|
||||||
if (is.defined(options.kernel)) {
|
if (is.defined(options.kernel)) {
|
||||||
if (is.string(kernel[options.kernel])) {
|
if (is.string(kernel[options.kernel])) {
|
||||||
@@ -305,9 +311,9 @@ function extend (extend) {
|
|||||||
this.options.extendBottom = extend.bottom;
|
this.options.extendBottom = extend.bottom;
|
||||||
this.options.extendLeft = extend.left;
|
this.options.extendLeft = extend.left;
|
||||||
this.options.extendRight = extend.right;
|
this.options.extendRight = extend.right;
|
||||||
this._setColourOption('extendBackground', extend.background);
|
this._setBackgroundColourOption('extendBackground', extend.background);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid edge extension ' + extend);
|
throw is.invalidParameterError('extend', 'integer or object', extend);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -349,11 +355,11 @@ function extract (options) {
|
|||||||
if (is.integer(value) && value >= 0) {
|
if (is.integer(value) && value >= 0) {
|
||||||
this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
|
this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Non-integer value for ' + name + ' of ' + value);
|
throw is.invalidParameterError(name, 'integer', value);
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
// Ensure existing rotation occurs before pre-resize extraction
|
// Ensure existing rotation occurs before pre-resize extraction
|
||||||
if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true)) {
|
if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true || this.options.rotationAngle !== 0)) {
|
||||||
this.options.rotateBeforePreExtract = true;
|
this.options.rotateBeforePreExtract = true;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
53
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.1",
|
"version": "0.23.2",
|
||||||
"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": [
|
||||||
@@ -59,7 +59,11 @@
|
|||||||
"Daiz <taneli.vatanen@gmail.com>",
|
"Daiz <taneli.vatanen@gmail.com>",
|
||||||
"Julian Aubourg <j@ubourg.net>",
|
"Julian Aubourg <j@ubourg.net>",
|
||||||
"Keith Belovay <keith@picthrive.com>",
|
"Keith Belovay <keith@picthrive.com>",
|
||||||
"Michael B. Klein <mbklein@gmail.com>"
|
"Michael B. Klein <mbklein@gmail.com>",
|
||||||
|
"Jordan Prudhomme <jordan@raboland.fr>",
|
||||||
|
"Ilya Ovdin <iovdin@gmail.com>",
|
||||||
|
"Andargor <andargor@yahoo.com>",
|
||||||
|
"Paul Neave <paul.neave@gmail.com>"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
||||||
@@ -72,6 +76,14 @@
|
|||||||
"docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done"
|
"docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done"
|
||||||
},
|
},
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
|
"files": [
|
||||||
|
"binding.gyp",
|
||||||
|
"docs/**",
|
||||||
|
"!docs/css/**",
|
||||||
|
"install/**",
|
||||||
|
"lib/**",
|
||||||
|
"src/**"
|
||||||
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/lovell/sharp"
|
"url": "git://github.com/lovell/sharp"
|
||||||
@@ -93,39 +105,38 @@
|
|||||||
"vips"
|
"vips"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^3.1.1",
|
"color": "^3.1.2",
|
||||||
"detect-libc": "^1.0.3",
|
"detect-libc": "^1.0.3",
|
||||||
"fs-copy-file-sync": "^1.1.1",
|
"nan": "^2.14.0",
|
||||||
"nan": "^2.13.2",
|
|
||||||
"npmlog": "^4.1.2",
|
"npmlog": "^4.1.2",
|
||||||
"prebuild-install": "^5.3.0",
|
"prebuild-install": "^5.3.2",
|
||||||
"semver": "^6.0.0",
|
"semver": "^6.3.0",
|
||||||
"simple-get": "^3.0.3",
|
"simple-get": "^3.1.0",
|
||||||
"tar": "^4.4.8",
|
"tar": "^5.0.5",
|
||||||
"tunnel-agent": "^0.6.0"
|
"tunnel-agent": "^0.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"async": "^2.6.2",
|
"async": "^3.1.0",
|
||||||
"cc": "^1.0.2",
|
"cc": "^1.0.2",
|
||||||
"decompress-zip": "^0.3.2",
|
"decompress-zip": "^0.3.2",
|
||||||
"documentation": "^10.0.0",
|
"documentation": "^12.1.2",
|
||||||
"exif-reader": "^1.0.2",
|
"exif-reader": "^1.0.3",
|
||||||
"icc": "^1.0.0",
|
"icc": "^1.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^6.1.4",
|
"mocha": "^6.2.2",
|
||||||
"mock-fs": "^4.9.0",
|
"mock-fs": "^4.10.2",
|
||||||
"nyc": "^14.0.0",
|
"nyc": "^14.1.1",
|
||||||
"prebuild": "^8.2.1",
|
"prebuild": "^9.1.1",
|
||||||
"prebuild-ci": "^3.0.0",
|
"prebuild-ci": "^3.1.0",
|
||||||
"rimraf": "^2.6.3",
|
"rimraf": "^3.0.0",
|
||||||
"semistandard": "^13.0.1"
|
"semistandard": "^14.2.0"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"config": {
|
"config": {
|
||||||
"libvips": "8.7.4"
|
"libvips": "8.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=8.5.0"
|
||||||
},
|
},
|
||||||
"semistandard": {
|
"semistandard": {
|
||||||
"env": [
|
"env": [
|
||||||
|
|||||||
@@ -110,6 +110,15 @@ namespace sharp {
|
|||||||
bool IsTiff(std::string const &str) {
|
bool IsTiff(std::string const &str) {
|
||||||
return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
|
return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
|
||||||
}
|
}
|
||||||
|
bool IsHeic(std::string const &str) {
|
||||||
|
return EndsWith(str, ".heic") || EndsWith(str, ".HEIC");
|
||||||
|
}
|
||||||
|
bool IsHeif(std::string const &str) {
|
||||||
|
return EndsWith(str, ".heif") || EndsWith(str, ".HEIF") || IsHeic(str) || IsAvif(str);
|
||||||
|
}
|
||||||
|
bool IsAvif(std::string const &str) {
|
||||||
|
return EndsWith(str, ".avif") || EndsWith(str, ".AVIF");
|
||||||
|
}
|
||||||
bool IsDz(std::string const &str) {
|
bool IsDz(std::string const &str) {
|
||||||
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
|
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
|
||||||
}
|
}
|
||||||
@@ -132,12 +141,13 @@ namespace sharp {
|
|||||||
case ImageType::TIFF: id = "tiff"; break;
|
case ImageType::TIFF: id = "tiff"; break;
|
||||||
case ImageType::GIF: id = "gif"; break;
|
case ImageType::GIF: id = "gif"; break;
|
||||||
case ImageType::SVG: id = "svg"; break;
|
case ImageType::SVG: id = "svg"; break;
|
||||||
|
case ImageType::HEIF: id = "heif"; break;
|
||||||
case ImageType::PDF: id = "pdf"; break;
|
case ImageType::PDF: id = "pdf"; break;
|
||||||
case ImageType::MAGICK: id = "magick"; break;
|
case ImageType::MAGICK: id = "magick"; break;
|
||||||
case ImageType::OPENSLIDE: id = "openslide"; break;
|
case ImageType::OPENSLIDE: id = "openslide"; break;
|
||||||
case ImageType::PPM: id = "ppm"; break;
|
case ImageType::PPM: id = "ppm"; break;
|
||||||
case ImageType::FITS: id = "fits"; break;
|
case ImageType::FITS: id = "fits"; break;
|
||||||
case ImageType::VIPS: id = "v"; break;
|
case ImageType::VIPS: id = "vips"; break;
|
||||||
case ImageType::RAW: id = "raw"; break;
|
case ImageType::RAW: id = "raw"; break;
|
||||||
case ImageType::UNKNOWN: id = "unknown"; break;
|
case ImageType::UNKNOWN: id = "unknown"; break;
|
||||||
case ImageType::MISSING: id = "missing"; break;
|
case ImageType::MISSING: id = "missing"; break;
|
||||||
@@ -165,6 +175,8 @@ namespace sharp {
|
|||||||
imageType = ImageType::GIF;
|
imageType = ImageType::GIF;
|
||||||
} else if (EndsWith(loader, "SvgBuffer")) {
|
} else if (EndsWith(loader, "SvgBuffer")) {
|
||||||
imageType = ImageType::SVG;
|
imageType = ImageType::SVG;
|
||||||
|
} else if (EndsWith(loader, "HeifBuffer")) {
|
||||||
|
imageType = ImageType::HEIF;
|
||||||
} else if (EndsWith(loader, "PdfBuffer")) {
|
} else if (EndsWith(loader, "PdfBuffer")) {
|
||||||
imageType = ImageType::PDF;
|
imageType = ImageType::PDF;
|
||||||
} else if (EndsWith(loader, "MagickBuffer")) {
|
} else if (EndsWith(loader, "MagickBuffer")) {
|
||||||
@@ -196,6 +208,8 @@ namespace sharp {
|
|||||||
imageType = ImageType::GIF;
|
imageType = ImageType::GIF;
|
||||||
} else if (EndsWith(loader, "SvgFile")) {
|
} else if (EndsWith(loader, "SvgFile")) {
|
||||||
imageType = ImageType::SVG;
|
imageType = ImageType::SVG;
|
||||||
|
} else if (EndsWith(loader, "HeifFile")) {
|
||||||
|
imageType = ImageType::HEIF;
|
||||||
} else if (EndsWith(loader, "PdfFile")) {
|
} else if (EndsWith(loader, "PdfFile")) {
|
||||||
imageType = ImageType::PDF;
|
imageType = ImageType::PDF;
|
||||||
} else if (EndsWith(loader, "Ppm")) {
|
} else if (EndsWith(loader, "Ppm")) {
|
||||||
@@ -222,6 +236,7 @@ namespace sharp {
|
|||||||
return
|
return
|
||||||
imageType == ImageType::GIF ||
|
imageType == ImageType::GIF ||
|
||||||
imageType == ImageType::TIFF ||
|
imageType == ImageType::TIFF ||
|
||||||
|
imageType == ImageType::HEIF ||
|
||||||
imageType == ImageType::PDF;
|
imageType == ImageType::PDF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
|
|
||||||
// Verify platform and compiler compatibility
|
// Verify platform and compiler compatibility
|
||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 7))
|
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 8))
|
||||||
#error libvips version 8.7.0+ is required - see sharp.pixelplumbing.com/page/install
|
#error libvips version 8.8.0+ is required - see sharp.pixelplumbing.com/page/install
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
||||||
@@ -101,6 +101,7 @@ namespace sharp {
|
|||||||
TIFF,
|
TIFF,
|
||||||
GIF,
|
GIF,
|
||||||
SVG,
|
SVG,
|
||||||
|
HEIF,
|
||||||
PDF,
|
PDF,
|
||||||
MAGICK,
|
MAGICK,
|
||||||
OPENSLIDE,
|
OPENSLIDE,
|
||||||
@@ -123,6 +124,9 @@ namespace sharp {
|
|||||||
bool IsPng(std::string const &str);
|
bool IsPng(std::string const &str);
|
||||||
bool IsWebp(std::string const &str);
|
bool IsWebp(std::string const &str);
|
||||||
bool IsTiff(std::string const &str);
|
bool IsTiff(std::string const &str);
|
||||||
|
bool IsHeic(std::string const &str);
|
||||||
|
bool IsHeif(std::string const &str);
|
||||||
|
bool IsAvif(std::string const &str);
|
||||||
bool IsDz(std::string const &str);
|
bool IsDz(std::string const &str);
|
||||||
bool IsDzZip(std::string const &str);
|
bool IsDzZip(std::string const &str);
|
||||||
bool IsV(std::string const &str);
|
bool IsV(std::string const &str);
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
#endif /*HAVE_CONFIG_H*/
|
#endif /*HAVE_CONFIG_H*/
|
||||||
#include <vips/intl.h>
|
#include <vips/intl.h>
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <vips/vips8>
|
#include <vips/vips8>
|
||||||
|
|
||||||
VIPS_NAMESPACE_START
|
VIPS_NAMESPACE_START
|
||||||
|
|||||||
@@ -563,7 +563,7 @@ VImage::new_from_file( const char *name, VOption *options )
|
|||||||
}
|
}
|
||||||
|
|
||||||
VImage
|
VImage
|
||||||
VImage::new_from_buffer( void *buf, size_t len, const char *option_string,
|
VImage::new_from_buffer( const void *buf, size_t len, const char *option_string,
|
||||||
VOption *options )
|
VOption *options )
|
||||||
{
|
{
|
||||||
const char *operation_name;
|
const char *operation_name;
|
||||||
@@ -588,6 +588,13 @@ VImage::new_from_buffer( void *buf, size_t len, const char *option_string,
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VImage
|
||||||
|
VImage::new_from_buffer( const std::string &buf, const char *option_string,
|
||||||
|
VOption *options )
|
||||||
|
{
|
||||||
|
return( new_from_buffer( buf.c_str(), buf.size(), option_string, options ) );
|
||||||
|
}
|
||||||
|
|
||||||
VImage
|
VImage
|
||||||
VImage::new_matrix( int width, int height )
|
VImage::new_matrix( int width, int height )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|||||||
if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
|
if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
|
||||||
baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
|
baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
|
||||||
}
|
}
|
||||||
|
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
|
||||||
|
baton->pagePrimary = image.get_int("heif-primary");
|
||||||
|
}
|
||||||
baton->hasProfile = sharp::HasProfile(image);
|
baton->hasProfile = sharp::HasProfile(image);
|
||||||
// Derived attributes
|
// Derived attributes
|
||||||
baton->hasAlpha = sharp::HasAlpha(image);
|
baton->hasAlpha = sharp::HasAlpha(image);
|
||||||
@@ -158,6 +161,9 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|||||||
if (baton->pageHeight > 0) {
|
if (baton->pageHeight > 0) {
|
||||||
Set(info, New("pageHeight").ToLocalChecked(), New<v8::Uint32>(baton->pageHeight));
|
Set(info, New("pageHeight").ToLocalChecked(), New<v8::Uint32>(baton->pageHeight));
|
||||||
}
|
}
|
||||||
|
if (baton->pagePrimary > -1) {
|
||||||
|
Set(info, New("pagePrimary").ToLocalChecked(), New<v8::Uint32>(baton->pagePrimary));
|
||||||
|
}
|
||||||
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
|
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
|
||||||
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
|
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
|
||||||
if (baton->orientation > 0) {
|
if (baton->orientation > 0) {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ struct MetadataBaton {
|
|||||||
int paletteBitDepth;
|
int paletteBitDepth;
|
||||||
int pages;
|
int pages;
|
||||||
int pageHeight;
|
int pageHeight;
|
||||||
|
int pagePrimary;
|
||||||
bool hasProfile;
|
bool hasProfile;
|
||||||
bool hasAlpha;
|
bool hasAlpha;
|
||||||
int orientation;
|
int orientation;
|
||||||
@@ -59,6 +60,7 @@ struct MetadataBaton {
|
|||||||
paletteBitDepth(0),
|
paletteBitDepth(0),
|
||||||
pages(0),
|
pages(0),
|
||||||
pageHeight(0),
|
pageHeight(0),
|
||||||
|
pagePrimary(-1),
|
||||||
hasProfile(false),
|
hasProfile(false),
|
||||||
hasAlpha(false),
|
hasAlpha(false),
|
||||||
orientation(0),
|
orientation(0),
|
||||||
|
|||||||
@@ -250,6 +250,9 @@ namespace sharp {
|
|||||||
Trim an image
|
Trim an image
|
||||||
*/
|
*/
|
||||||
VImage Trim(VImage image, double const threshold) {
|
VImage Trim(VImage image, double const threshold) {
|
||||||
|
if (image.width() < 3 && image.height() < 3) {
|
||||||
|
throw VError("Image to trim must be at least 3x3 pixels");
|
||||||
|
}
|
||||||
// Top-left pixel provides the background colour
|
// Top-left pixel provides the background colour
|
||||||
VImage background = image.extract_area(0, 0, 1, 1);
|
VImage background = image.extract_area(0, 0, 1, 1);
|
||||||
if (HasAlpha(background)) {
|
if (HasAlpha(background)) {
|
||||||
|
|||||||
127
src/pipeline.cc
@@ -21,6 +21,8 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <vips/vips8>
|
#include <vips/vips8>
|
||||||
#include <node.h>
|
#include <node.h>
|
||||||
@@ -30,6 +32,17 @@
|
|||||||
#include "operations.h"
|
#include "operations.h"
|
||||||
#include "pipeline.h"
|
#include "pipeline.h"
|
||||||
|
|
||||||
|
#if defined(WIN32)
|
||||||
|
#define STAT64_STRUCT __stat64
|
||||||
|
#define STAT64_FUNCTION _stat64
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define STAT64_STRUCT stat
|
||||||
|
#define STAT64_FUNCTION stat
|
||||||
|
#else
|
||||||
|
#define STAT64_STRUCT stat64
|
||||||
|
#define STAT64_FUNCTION stat64
|
||||||
|
#endif
|
||||||
|
|
||||||
class PipelineWorker : public Nan::AsyncWorker {
|
class PipelineWorker : public Nan::AsyncWorker {
|
||||||
public:
|
public:
|
||||||
PipelineWorker(
|
PipelineWorker(
|
||||||
@@ -57,16 +70,6 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
// Increment processing task counter
|
// Increment processing task counter
|
||||||
g_atomic_int_inc(&sharp::counterProcess);
|
g_atomic_int_inc(&sharp::counterProcess);
|
||||||
|
|
||||||
std::map<VipsInterpretation, std::string> profileMap;
|
|
||||||
// Default sRGB ICC profile from https://packages.debian.org/sid/all/icc-profiles-free/filelist
|
|
||||||
profileMap.insert(
|
|
||||||
std::pair<VipsInterpretation, std::string>(VIPS_INTERPRETATION_sRGB,
|
|
||||||
baton->iccProfilePath + "sRGB.icc"));
|
|
||||||
// Convert to sRGB using default CMYK profile from http://www.argyllcms.com/cmyk.icm
|
|
||||||
profileMap.insert(
|
|
||||||
std::pair<VipsInterpretation, std::string>(VIPS_INTERPRETATION_CMYK,
|
|
||||||
baton->iccProfilePath + "cmyk.icm"));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Open input
|
// Open input
|
||||||
vips::VImage image;
|
vips::VImage image;
|
||||||
@@ -95,9 +98,16 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rotate pre-extract
|
// Rotate pre-extract
|
||||||
if (baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
|
if (baton->rotateBeforePreExtract) {
|
||||||
image = image.rot(rotation);
|
if (rotation != VIPS_ANGLE_D0) {
|
||||||
sharp::RemoveExifOrientation(image);
|
image = image.rot(rotation);
|
||||||
|
sharp::RemoveExifOrientation(image);
|
||||||
|
}
|
||||||
|
if (baton->rotationAngle != 0.0) {
|
||||||
|
std::vector<double> background;
|
||||||
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground);
|
||||||
|
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim
|
// Trim
|
||||||
@@ -301,17 +311,15 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
if (sharp::HasProfile(image) && image.interpretation() != VIPS_INTERPRETATION_LABS) {
|
if (sharp::HasProfile(image) && image.interpretation() != VIPS_INTERPRETATION_LABS) {
|
||||||
// Convert to sRGB using embedded profile
|
// Convert to sRGB using embedded profile
|
||||||
try {
|
try {
|
||||||
image = image.icc_transform(
|
image = image.icc_transform("srgb", VImage::option()
|
||||||
const_cast<char*>(profileMap[VIPS_INTERPRETATION_sRGB].data()), VImage::option()
|
|
||||||
->set("embedded", TRUE)
|
->set("embedded", TRUE)
|
||||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
// Ignore failure of embedded profile
|
// Ignore failure of embedded profile
|
||||||
}
|
}
|
||||||
} else if (image.interpretation() == VIPS_INTERPRETATION_CMYK) {
|
} else if (image.interpretation() == VIPS_INTERPRETATION_CMYK) {
|
||||||
image = image.icc_transform(
|
image = image.icc_transform("srgb", VImage::option()
|
||||||
const_cast<char*>(profileMap[VIPS_INTERPRETATION_sRGB].data()), VImage::option()
|
->set("input_profile", "cmyk")
|
||||||
->set("input_profile", profileMap[VIPS_INTERPRETATION_CMYK].data())
|
|
||||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,12 +398,13 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("kernel", kernel));
|
->set("kernel", kernel));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate
|
// Rotate post-extract 90-angle
|
||||||
if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
|
if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
|
||||||
image = image.rot(rotation);
|
image = image.rot(rotation);
|
||||||
sharp::RemoveExifOrientation(image);
|
sharp::RemoveExifOrientation(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Flip (mirror about Y axis)
|
// Flip (mirror about Y axis)
|
||||||
if (baton->flip) {
|
if (baton->flip) {
|
||||||
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
||||||
@@ -478,8 +487,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate by degree
|
// Rotate post-extract non-90 angle
|
||||||
if (baton->rotationAngle != 0.0) {
|
if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
|
||||||
std::vector<double> background;
|
std::vector<double> background;
|
||||||
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground);
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground);
|
||||||
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
||||||
@@ -582,7 +591,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
if (!HasAlpha(compositeImage)) {
|
if (!HasAlpha(compositeImage)) {
|
||||||
compositeImage = sharp::EnsureAlpha(compositeImage);
|
compositeImage = sharp::EnsureAlpha(compositeImage);
|
||||||
}
|
}
|
||||||
compositeImage = compositeImage.premultiply();
|
if (!composite->premultiplied) compositeImage = compositeImage.premultiply();
|
||||||
// Calculate position
|
// Calculate position
|
||||||
int left;
|
int left;
|
||||||
int top;
|
int top;
|
||||||
@@ -680,8 +689,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
// Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
|
// Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
|
||||||
image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
|
image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
|
||||||
// Transform colours from embedded profile to output profile
|
// Transform colours from embedded profile to output profile
|
||||||
if (baton->withMetadata && sharp::HasProfile(image) && profileMap[baton->colourspace] != std::string()) {
|
if (baton->withMetadata && sharp::HasProfile(image)) {
|
||||||
image = image.icc_transform(const_cast<char*>(profileMap[baton->colourspace].data()),
|
image = image.icc_transform(vips_enum_nick(VIPS_TYPE_INTERPRETATION, baton->colourspace),
|
||||||
VImage::option()->set("embedded", TRUE));
|
VImage::option()->set("embedded", TRUE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -747,6 +756,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("Q", baton->webpQuality)
|
->set("Q", baton->webpQuality)
|
||||||
->set("lossless", baton->webpLossless)
|
->set("lossless", baton->webpLossless)
|
||||||
->set("near_lossless", baton->webpNearLossless)
|
->set("near_lossless", baton->webpNearLossless)
|
||||||
|
->set("smart_subsample", baton->webpSmartSubsample)
|
||||||
|
->set("reduction_effort", baton->webpReductionEffort)
|
||||||
->set("alpha_q", baton->webpAlphaQuality)));
|
->set("alpha_q", baton->webpAlphaQuality)));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
baton->bufferOutLength = area->length;
|
baton->bufferOutLength = area->length;
|
||||||
@@ -780,6 +791,18 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
vips_area_unref(area);
|
vips_area_unref(area);
|
||||||
baton->formatOut = "tiff";
|
baton->formatOut = "tiff";
|
||||||
baton->channels = std::min(baton->channels, 3);
|
baton->channels = std::min(baton->channels, 3);
|
||||||
|
} else if (baton->formatOut == "heif" || (baton->formatOut == "input" && inputImageType == ImageType::HEIF)) {
|
||||||
|
// Write HEIF to buffer
|
||||||
|
VipsArea *area = VIPS_AREA(image.heifsave_buffer(VImage::option()
|
||||||
|
->set("strip", !baton->withMetadata)
|
||||||
|
->set("compression", baton->heifCompression)
|
||||||
|
->set("Q", baton->heifQuality)
|
||||||
|
->set("lossless", baton->heifLossless)));
|
||||||
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
|
baton->bufferOutLength = area->length;
|
||||||
|
area->free_fn = nullptr;
|
||||||
|
vips_area_unref(area);
|
||||||
|
baton->formatOut = "heif";
|
||||||
} else if (baton->formatOut == "raw" || (baton->formatOut == "input" && inputImageType == ImageType::RAW)) {
|
} else if (baton->formatOut == "raw" || (baton->formatOut == "input" && inputImageType == ImageType::RAW)) {
|
||||||
// Write raw, uncompressed image data to buffer
|
// Write raw, uncompressed image data to buffer
|
||||||
if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) {
|
if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) {
|
||||||
@@ -814,6 +837,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
bool const isPng = sharp::IsPng(baton->fileOut);
|
bool const isPng = sharp::IsPng(baton->fileOut);
|
||||||
bool const isWebp = sharp::IsWebp(baton->fileOut);
|
bool const isWebp = sharp::IsWebp(baton->fileOut);
|
||||||
bool const isTiff = sharp::IsTiff(baton->fileOut);
|
bool const isTiff = sharp::IsTiff(baton->fileOut);
|
||||||
|
bool const isHeif = sharp::IsHeif(baton->fileOut);
|
||||||
bool const isDz = sharp::IsDz(baton->fileOut);
|
bool const isDz = sharp::IsDz(baton->fileOut);
|
||||||
bool const isDzZip = sharp::IsDzZip(baton->fileOut);
|
bool const isDzZip = sharp::IsDzZip(baton->fileOut);
|
||||||
bool const isV = sharp::IsV(baton->fileOut);
|
bool const isV = sharp::IsV(baton->fileOut);
|
||||||
@@ -858,6 +882,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("Q", baton->webpQuality)
|
->set("Q", baton->webpQuality)
|
||||||
->set("lossless", baton->webpLossless)
|
->set("lossless", baton->webpLossless)
|
||||||
->set("near_lossless", baton->webpNearLossless)
|
->set("near_lossless", baton->webpNearLossless)
|
||||||
|
->set("smart_subsample", baton->webpSmartSubsample)
|
||||||
|
->set("reduction_effort", baton->webpReductionEffort)
|
||||||
->set("alpha_q", baton->webpAlphaQuality));
|
->set("alpha_q", baton->webpAlphaQuality));
|
||||||
baton->formatOut = "webp";
|
baton->formatOut = "webp";
|
||||||
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
||||||
@@ -880,6 +906,20 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("yres", baton->tiffYres));
|
->set("yres", baton->tiffYres));
|
||||||
baton->formatOut = "tiff";
|
baton->formatOut = "tiff";
|
||||||
baton->channels = std::min(baton->channels, 3);
|
baton->channels = std::min(baton->channels, 3);
|
||||||
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
||||||
|
(willMatchInput && inputImageType == ImageType::HEIF)) {
|
||||||
|
// Write HEIF to file
|
||||||
|
#ifdef VIPS_TYPE_FOREIGN_HEIF_COMPRESSION
|
||||||
|
if (sharp::IsAvif(baton->fileOut)) {
|
||||||
|
baton->heifCompression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||||
|
->set("strip", !baton->withMetadata)
|
||||||
|
->set("Q", baton->heifQuality)
|
||||||
|
->set("compression", baton->heifCompression)
|
||||||
|
->set("lossless", baton->heifLossless));
|
||||||
|
baton->formatOut = "heif";
|
||||||
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
||||||
if (isDzZip) {
|
if (isDzZip) {
|
||||||
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||||
@@ -898,7 +938,9 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
{"Q", std::to_string(baton->webpQuality)},
|
{"Q", std::to_string(baton->webpQuality)},
|
||||||
{"alpha_q", std::to_string(baton->webpAlphaQuality)},
|
{"alpha_q", std::to_string(baton->webpAlphaQuality)},
|
||||||
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
||||||
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"}
|
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
|
||||||
|
{"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
|
||||||
|
{"reduction_effort", std::to_string(baton->webpReductionEffort)}
|
||||||
};
|
};
|
||||||
suffix = AssembleSuffixString(".webp", options);
|
suffix = AssembleSuffixString(".webp", options);
|
||||||
} else {
|
} else {
|
||||||
@@ -917,6 +959,11 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
};
|
};
|
||||||
suffix = AssembleSuffixString(extname, options);
|
suffix = AssembleSuffixString(extname, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove alpha channel from tile background if image does not contain an alpha channel
|
||||||
|
if (!HasAlpha(image)) {
|
||||||
|
baton->tileBackground.pop_back();
|
||||||
|
}
|
||||||
// Write DZ to file
|
// Write DZ to file
|
||||||
vips::VOption *options = VImage::option()
|
vips::VOption *options = VImage::option()
|
||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
@@ -925,7 +972,9 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("container", baton->tileContainer)
|
->set("container", baton->tileContainer)
|
||||||
->set("layout", baton->tileLayout)
|
->set("layout", baton->tileLayout)
|
||||||
->set("suffix", const_cast<char*>(suffix.data()))
|
->set("suffix", const_cast<char*>(suffix.data()))
|
||||||
->set("angle", CalculateAngleRotation(baton->tileAngle));
|
->set("angle", CalculateAngleRotation(baton->tileAngle))
|
||||||
|
->set("background", baton->tileBackground)
|
||||||
|
->set("skip_blanks", baton->tileSkipBlanks);
|
||||||
|
|
||||||
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
|
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
|
||||||
// not passing anything - libvips will handle choice
|
// not passing anything - libvips will handle choice
|
||||||
@@ -1005,8 +1054,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
argv[2] = info;
|
argv[2] = info;
|
||||||
} else {
|
} else {
|
||||||
// Add file size to info
|
// Add file size to info
|
||||||
GStatBuf st;
|
struct STAT64_STRUCT st;
|
||||||
if (g_stat(baton->fileOut.data(), &st) == 0) {
|
if (STAT64_FUNCTION(baton->fileOut.data(), &st) == 0) {
|
||||||
Set(info, New("size").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(st.st_size)));
|
Set(info, New("size").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(st.st_size)));
|
||||||
}
|
}
|
||||||
argv[1] = info;
|
argv[1] = info;
|
||||||
@@ -1141,9 +1190,6 @@ NAN_METHOD(pipeline) {
|
|||||||
|
|
||||||
// Input
|
// Input
|
||||||
baton->input = CreateInputDescriptor(AttrAs<v8::Object>(options, "input"), buffersToPersist);
|
baton->input = CreateInputDescriptor(AttrAs<v8::Object>(options, "input"), buffersToPersist);
|
||||||
|
|
||||||
// ICC profile to use when input CMYK image has no embedded profile
|
|
||||||
baton->iccProfilePath = AttrAsStr(options, "iccProfilePath");
|
|
||||||
baton->accessMethod = AttrTo<bool>(options, "sequentialRead") ?
|
baton->accessMethod = AttrTo<bool>(options, "sequentialRead") ?
|
||||||
VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
||||||
// 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
|
||||||
@@ -1190,6 +1236,7 @@ NAN_METHOD(pipeline) {
|
|||||||
composite->left = AttrTo<int32_t>(compositeObject, "left");
|
composite->left = AttrTo<int32_t>(compositeObject, "left");
|
||||||
composite->top = AttrTo<int32_t>(compositeObject, "top");
|
composite->top = AttrTo<int32_t>(compositeObject, "top");
|
||||||
composite->tile = AttrTo<bool>(compositeObject, "tile");
|
composite->tile = AttrTo<bool>(compositeObject, "tile");
|
||||||
|
composite->premultiplied = AttrTo<bool>(compositeObject, "premultiplied");
|
||||||
baton->composite.push_back(composite);
|
baton->composite.push_back(composite);
|
||||||
}
|
}
|
||||||
// Resize options
|
// Resize options
|
||||||
@@ -1304,6 +1351,8 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
|
baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
|
||||||
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
||||||
baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
|
baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
|
||||||
|
baton->webpSmartSubsample = AttrTo<bool>(options, "webpSmartSubsample");
|
||||||
|
baton->webpReductionEffort = AttrTo<uint32_t>(options, "webpReductionEffort");
|
||||||
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
|
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
|
||||||
baton->tiffPyramid = AttrTo<bool>(options, "tiffPyramid");
|
baton->tiffPyramid = AttrTo<bool>(options, "tiffPyramid");
|
||||||
baton->tiffSquash = AttrTo<bool>(options, "tiffSquash");
|
baton->tiffSquash = AttrTo<bool>(options, "tiffSquash");
|
||||||
@@ -1319,12 +1368,20 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
|
baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
|
||||||
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
|
||||||
AttrAsStr(options, "tiffPredictor").data()));
|
AttrAsStr(options, "tiffPredictor").data()));
|
||||||
|
baton->heifQuality = AttrTo<uint32_t>(options, "heifQuality");
|
||||||
|
baton->heifLossless = AttrTo<bool>(options, "heifLossless");
|
||||||
|
#ifdef VIPS_TYPE_FOREIGN_HEIF_COMPRESSION
|
||||||
|
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
||||||
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
||||||
|
AttrAsStr(options, "heifCompression").data()));
|
||||||
|
#endif
|
||||||
// Tile output
|
// Tile output
|
||||||
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
|
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
|
||||||
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
|
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
|
||||||
std::string tileContainer = AttrAsStr(options, "tileContainer");
|
std::string tileContainer = AttrAsStr(options, "tileContainer");
|
||||||
baton->tileAngle = AttrTo<int32_t>(options, "tileAngle");
|
baton->tileAngle = AttrTo<int32_t>(options, "tileAngle");
|
||||||
|
baton->tileBackground = AttrAsRgba(options, "tileBackground");
|
||||||
|
baton->tileSkipBlanks = AttrTo<int32_t>(options, "tileSkipBlanks");
|
||||||
if (tileContainer == "zip") {
|
if (tileContainer == "zip") {
|
||||||
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ struct Composite {
|
|||||||
int left;
|
int left;
|
||||||
int top;
|
int top;
|
||||||
bool tile;
|
bool tile;
|
||||||
|
bool premultiplied;
|
||||||
|
|
||||||
Composite():
|
Composite():
|
||||||
input(nullptr),
|
input(nullptr),
|
||||||
@@ -48,12 +49,12 @@ struct Composite {
|
|||||||
gravity(0),
|
gravity(0),
|
||||||
left(-1),
|
left(-1),
|
||||||
top(-1),
|
top(-1),
|
||||||
tile(false) {}
|
tile(false),
|
||||||
|
premultiplied(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PipelineBaton {
|
struct PipelineBaton {
|
||||||
sharp::InputDescriptor *input;
|
sharp::InputDescriptor *input;
|
||||||
std::string iccProfilePath;
|
|
||||||
int limitInputPixels;
|
int limitInputPixels;
|
||||||
std::string formatOut;
|
std::string formatOut;
|
||||||
std::string fileOut;
|
std::string fileOut;
|
||||||
@@ -138,6 +139,8 @@ struct PipelineBaton {
|
|||||||
int webpAlphaQuality;
|
int webpAlphaQuality;
|
||||||
bool webpNearLossless;
|
bool webpNearLossless;
|
||||||
bool webpLossless;
|
bool webpLossless;
|
||||||
|
bool webpSmartSubsample;
|
||||||
|
int webpReductionEffort;
|
||||||
int tiffQuality;
|
int tiffQuality;
|
||||||
VipsForeignTiffCompression tiffCompression;
|
VipsForeignTiffCompression tiffCompression;
|
||||||
VipsForeignTiffPredictor tiffPredictor;
|
VipsForeignTiffPredictor tiffPredictor;
|
||||||
@@ -148,6 +151,9 @@ struct PipelineBaton {
|
|||||||
int tiffTileWidth;
|
int tiffTileWidth;
|
||||||
double tiffXres;
|
double tiffXres;
|
||||||
double tiffYres;
|
double tiffYres;
|
||||||
|
int heifQuality;
|
||||||
|
int heifCompression; // TODO(libvips 8.9.0): VipsForeignHeifCompression
|
||||||
|
bool heifLossless;
|
||||||
std::string err;
|
std::string err;
|
||||||
bool withMetadata;
|
bool withMetadata;
|
||||||
int withMetadataOrientation;
|
int withMetadataOrientation;
|
||||||
@@ -169,6 +175,8 @@ struct PipelineBaton {
|
|||||||
VipsForeignDzLayout tileLayout;
|
VipsForeignDzLayout tileLayout;
|
||||||
std::string tileFormat;
|
std::string tileFormat;
|
||||||
int tileAngle;
|
int tileAngle;
|
||||||
|
std::vector<double> tileBackground;
|
||||||
|
int tileSkipBlanks;
|
||||||
VipsForeignDzDepth tileDepth;
|
VipsForeignDzDepth tileDepth;
|
||||||
std::unique_ptr<double[]> recombMatrix;
|
std::unique_ptr<double[]> recombMatrix;
|
||||||
|
|
||||||
@@ -194,7 +202,7 @@ struct PipelineBaton {
|
|||||||
blurSigma(0.0),
|
blurSigma(0.0),
|
||||||
brightness(1.0),
|
brightness(1.0),
|
||||||
saturation(1.0),
|
saturation(1.0),
|
||||||
hue(0.0),
|
hue(0),
|
||||||
medianSize(0),
|
medianSize(0),
|
||||||
sharpenSigma(0.0),
|
sharpenSigma(0.0),
|
||||||
sharpenFlat(1.0),
|
sharpenFlat(1.0),
|
||||||
@@ -237,6 +245,11 @@ struct PipelineBaton {
|
|||||||
pngColours(256),
|
pngColours(256),
|
||||||
pngDither(1.0),
|
pngDither(1.0),
|
||||||
webpQuality(80),
|
webpQuality(80),
|
||||||
|
webpAlphaQuality(100),
|
||||||
|
webpNearLossless(false),
|
||||||
|
webpLossless(false),
|
||||||
|
webpSmartSubsample(false),
|
||||||
|
webpReductionEffort(4),
|
||||||
tiffQuality(80),
|
tiffQuality(80),
|
||||||
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
||||||
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
||||||
@@ -247,6 +260,9 @@ struct PipelineBaton {
|
|||||||
tiffTileWidth(256),
|
tiffTileWidth(256),
|
||||||
tiffXres(1.0),
|
tiffXres(1.0),
|
||||||
tiffYres(1.0),
|
tiffYres(1.0),
|
||||||
|
heifQuality(80),
|
||||||
|
heifCompression(1), // TODO(libvips 8.9.0): VIPS_FOREIGN_HEIF_COMPRESSION_HEVC
|
||||||
|
heifLossless(false),
|
||||||
withMetadata(false),
|
withMetadata(false),
|
||||||
withMetadataOrientation(-1),
|
withMetadataOrientation(-1),
|
||||||
convKernelWidth(0),
|
convKernelWidth(0),
|
||||||
@@ -265,6 +281,8 @@ struct PipelineBaton {
|
|||||||
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
|
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
|
||||||
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ),
|
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ),
|
||||||
tileAngle(0),
|
tileAngle(0),
|
||||||
|
tileBackground{ 255.0, 255.0, 255.0, 255.0 },
|
||||||
|
tileSkipBlanks(-1),
|
||||||
tileDepth(VIPS_FOREIGN_DZ_DEPTH_LAST) {}
|
tileDepth(VIPS_FOREIGN_DZ_DEPTH_LAST) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -51,4 +51,4 @@ NAN_MODULE_INIT(init) {
|
|||||||
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(stats)).ToLocalChecked());
|
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(stats)).ToLocalChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_MODULE(sharp, init)
|
NAN_MODULE_WORKER_ENABLED(sharp, init)
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class StatsWorker : public Nan::AsyncWorker {
|
|||||||
|
|
||||||
std::vector<ChannelStats>::iterator it;
|
std::vector<ChannelStats>::iterator it;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (it=baton->channelStats.begin() ; it < baton->channelStats.end(); it++, i++) {
|
for (it = baton->channelStats.begin(); it < baton->channelStats.end(); it++, i++) {
|
||||||
v8::Local<v8::Object> channelStat = New<v8::Object>();
|
v8::Local<v8::Object> channelStat = New<v8::Object>();
|
||||||
Set(channelStat, New("min").ToLocalChecked(), New<v8::Number>(it->min));
|
Set(channelStat, New("min").ToLocalChecked(), New<v8::Number>(it->min));
|
||||||
Set(channelStat, New("max").ToLocalChecked(), New<v8::Number>(it->max));
|
Set(channelStat, New("max").ToLocalChecked(), New<v8::Number>(it->max));
|
||||||
@@ -127,7 +127,7 @@ class StatsWorker : public Nan::AsyncWorker {
|
|||||||
Set(channelStat, New("minY").ToLocalChecked(), New<v8::Number>(it->minY));
|
Set(channelStat, New("minY").ToLocalChecked(), New<v8::Number>(it->minY));
|
||||||
Set(channelStat, New("maxX").ToLocalChecked(), New<v8::Number>(it->maxX));
|
Set(channelStat, New("maxX").ToLocalChecked(), New<v8::Number>(it->maxX));
|
||||||
Set(channelStat, New("maxY").ToLocalChecked(), New<v8::Number>(it->maxY));
|
Set(channelStat, New("maxY").ToLocalChecked(), New<v8::Number>(it->maxY));
|
||||||
channels->Set(i, channelStat);
|
Set(channels, i, channelStat);
|
||||||
}
|
}
|
||||||
|
|
||||||
Set(info, New("channels").ToLocalChecked(), channels);
|
Set(info, New("channels").ToLocalChecked(), channels);
|
||||||
|
|||||||
@@ -150,8 +150,9 @@ NAN_METHOD(format) {
|
|||||||
|
|
||||||
// Which load/save operations are available for each compressed format?
|
// Which load/save operations are available for each compressed format?
|
||||||
Local<Object> format = New<Object>();
|
Local<Object> format = New<Object>();
|
||||||
for (std::string f : {
|
for (std::string const f : {
|
||||||
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz", "ppm", "fits", "gif", "svg", "pdf", "v"
|
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz",
|
||||||
|
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips"
|
||||||
}) {
|
}) {
|
||||||
// Input
|
// Input
|
||||||
Local<Boolean> hasInputFile =
|
Local<Boolean> hasInputFile =
|
||||||
|
|||||||
@@ -8,17 +8,16 @@
|
|||||||
"test": "node perf && node random && node parallel"
|
"test": "node perf && node random && node parallel"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"async": "^2.6.1",
|
"async": "^3.1.0",
|
||||||
"benchmark": "^2.1.4",
|
"benchmark": "^2.1.4",
|
||||||
"gm": "^1.23.1",
|
"gm": "^1.23.1",
|
||||||
"imagemagick": "^0.1.3",
|
"imagemagick": "^0.1.3",
|
||||||
"imagemagick-native": "^1.9.3",
|
"jimp": "^0.8.4",
|
||||||
"jimp": "^0.5.3",
|
"mapnik": "^4.3.1",
|
||||||
"mapnik": "^4.0.1",
|
"semver": "^6.3.0"
|
||||||
"semver": "^5.5.1"
|
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=8.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,6 @@ const gm = require('gm');
|
|||||||
const imagemagick = require('imagemagick');
|
const imagemagick = require('imagemagick');
|
||||||
const mapnik = require('mapnik');
|
const mapnik = require('mapnik');
|
||||||
const jimp = require('jimp');
|
const jimp = require('jimp');
|
||||||
let imagemagickNative;
|
|
||||||
try {
|
|
||||||
imagemagickNative = require('imagemagick-native');
|
|
||||||
} catch (err) {
|
|
||||||
console.log('Excluding imagemagick-native');
|
|
||||||
}
|
|
||||||
|
|
||||||
const fixtures = require('../fixtures');
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
@@ -28,7 +22,7 @@ const height = 588;
|
|||||||
sharp.cache(false);
|
sharp.cache(false);
|
||||||
|
|
||||||
async.series({
|
async.series({
|
||||||
'jpeg': function (callback) {
|
jpeg: function (callback) {
|
||||||
const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
|
const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
|
||||||
const jpegSuite = new Benchmark.Suite('jpeg');
|
const jpegSuite = new Benchmark.Suite('jpeg');
|
||||||
// jimp
|
// jimp
|
||||||
@@ -126,29 +120,6 @@ async.series({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// imagemagick-native
|
|
||||||
if (typeof imagemagickNative !== 'undefined') {
|
|
||||||
jpegSuite.add('imagemagick-native-buffer-buffer', {
|
|
||||||
defer: true,
|
|
||||||
fn: function (deferred) {
|
|
||||||
imagemagickNative.convert({
|
|
||||||
srcData: inputJpgBuffer,
|
|
||||||
quality: 80,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
format: 'JPEG',
|
|
||||||
filter: 'Lanczos'
|
|
||||||
}, function (err, buffer) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
} else {
|
|
||||||
assert.notStrictEqual(null, buffer);
|
|
||||||
deferred.resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// gm
|
// gm
|
||||||
jpegSuite.add('gm-buffer-file', {
|
jpegSuite.add('gm-buffer-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
@@ -289,6 +260,9 @@ async.series({
|
|||||||
.then(function (buffer) {
|
.then(function (buffer) {
|
||||||
assert.notStrictEqual(null, buffer);
|
assert.notStrictEqual(null, buffer);
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
throw err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).on('cycle', function (event) {
|
}).on('cycle', function (event) {
|
||||||
@@ -698,22 +672,6 @@ async.series({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// imagemagick-native
|
|
||||||
if (typeof imagemagickNative !== 'undefined') {
|
|
||||||
pngSuite.add('imagemagick-native-buffer-buffer', {
|
|
||||||
defer: true,
|
|
||||||
fn: function (deferred) {
|
|
||||||
imagemagickNative.convert({
|
|
||||||
srcData: inputPngBuffer,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
format: 'PNG',
|
|
||||||
filter: 'Lanczos'
|
|
||||||
});
|
|
||||||
deferred.resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// gm
|
// gm
|
||||||
pngSuite.add('gm-file-file', {
|
pngSuite.add('gm-file-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
|
|||||||
BIN
test/fixtures/alpha-layer-1-fill-low-alpha.png
vendored
|
Before Width: | Height: | Size: 222 KiB |
BIN
test/fixtures/alpha-layer-2-ink-low-alpha.png
vendored
|
Before Width: | Height: | Size: 89 KiB |
BIN
test/fixtures/alpha-layer-2-ink.png
vendored
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 186 KiB |
BIN
test/fixtures/expected/alpha-layer-01-low-alpha.png
vendored
|
Before Width: | Height: | Size: 188 KiB |
BIN
test/fixtures/expected/alpha-layer-01.png
vendored
|
Before Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 208 KiB |
BIN
test/fixtures/expected/alpha-layer-012-low-alpha.png
vendored
|
Before Width: | Height: | Size: 209 KiB |
BIN
test/fixtures/expected/alpha-layer-012.png
vendored
|
Before Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 221 KiB |
|
Before Width: | Height: | Size: 179 KiB |
BIN
test/fixtures/expected/alpha-layer-12-low-alpha.png
vendored
|
Before Width: | Height: | Size: 179 KiB |
BIN
test/fixtures/expected/alpha-layer-12.png
vendored
|
Before Width: | Height: | Size: 200 KiB |
BIN
test/fixtures/expected/crop-strategy-attention.jpg
vendored
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.9 KiB |
BIN
test/fixtures/expected/expected.absent.composite.premultiplied.png
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
test/fixtures/expected/expected.false.composite.premultiplied.png
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
test/fixtures/expected/expected.true.composite.premultiplied.png
vendored
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
test/fixtures/expected/extend-equal-single.jpg
vendored
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
test/fixtures/expected/extract-rotate-45.jpg
vendored
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
test/fixtures/expected/flatten-rgb16-orange.jpg
vendored
|
Before Width: | Height: | Size: 839 B After Width: | Height: | Size: 824 B |
BIN
test/fixtures/expected/rotate-extract-45.jpg
vendored
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
test/fixtures/expected/svg-embedded.png
vendored
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 84 KiB |
3
test/fixtures/index.js
vendored
@@ -80,9 +80,6 @@ module.exports = {
|
|||||||
inputPngWithTransparency16bit: getPath('tbgn2c16.png'), // http://www.schaik.com/pngsuite/tbgn2c16.png
|
inputPngWithTransparency16bit: getPath('tbgn2c16.png'), // http://www.schaik.com/pngsuite/tbgn2c16.png
|
||||||
inputPngOverlayLayer0: getPath('alpha-layer-0-background.png'),
|
inputPngOverlayLayer0: getPath('alpha-layer-0-background.png'),
|
||||||
inputPngOverlayLayer1: getPath('alpha-layer-1-fill.png'),
|
inputPngOverlayLayer1: getPath('alpha-layer-1-fill.png'),
|
||||||
inputPngOverlayLayer2: getPath('alpha-layer-2-ink.png'),
|
|
||||||
inputPngOverlayLayer1LowAlpha: getPath('alpha-layer-1-fill-low-alpha.png'),
|
|
||||||
inputPngOverlayLayer2LowAlpha: getPath('alpha-layer-2-ink-low-alpha.png'),
|
|
||||||
inputPngAlphaPremultiplicationSmall: getPath('alpha-premultiply-1024x768-paper.png'),
|
inputPngAlphaPremultiplicationSmall: getPath('alpha-premultiply-1024x768-paper.png'),
|
||||||
inputPngAlphaPremultiplicationLarge: getPath('alpha-premultiply-2048x1536-paper.png'),
|
inputPngAlphaPremultiplicationLarge: getPath('alpha-premultiply-2048x1536-paper.png'),
|
||||||
inputPngBooleanNoAlpha: getPath('bandbool.png'),
|
inputPngBooleanNoAlpha: getPath('bandbool.png'),
|
||||||
|
|||||||
BIN
test/fixtures/input.above.composite.premultiplied.png
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
test/fixtures/input.below.composite.premultiplied.png
vendored
Normal file
|
After Width: | Height: | Size: 14 KiB |
@@ -16,5 +16,5 @@ for test in ./test/unit/*.js; do
|
|||||||
--show-leak-kinds=definite,indirect,possible \
|
--show-leak-kinds=definite,indirect,possible \
|
||||||
--num-callers=20 \
|
--num-callers=20 \
|
||||||
--trace-children=yes \
|
--trace-children=yes \
|
||||||
node node_modules/.bin/mocha --slow=60000 --timeout=120000 --file test/unit/beforeEach.js "$test";
|
node --expose-gc node_modules/.bin/mocha --slow=60000 --timeout=120000 --file test/unit/beforeEach.js "$test";
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -150,7 +150,7 @@
|
|||||||
{
|
{
|
||||||
cond_libwebp_generic
|
cond_libwebp_generic
|
||||||
Memcheck:Cond
|
Memcheck:Cond
|
||||||
obj:/usr/lib/x86_64-linux-gnu/libwebp.so.6.0.2
|
obj:*/libwebp.so.*
|
||||||
}
|
}
|
||||||
|
|
||||||
# tiff
|
# tiff
|
||||||
@@ -188,6 +188,14 @@
|
|||||||
...
|
...
|
||||||
fun:FcConfigSubstituteWithPat
|
fun:FcConfigSubstituteWithPat
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
leak_fontconfig_init
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: indirect
|
||||||
|
fun:calloc
|
||||||
|
...
|
||||||
|
fun:FcInitLoadConfigAndFonts
|
||||||
|
}
|
||||||
|
|
||||||
# libvips
|
# libvips
|
||||||
{
|
{
|
||||||
@@ -220,6 +228,13 @@
|
|||||||
fun:write_webp.constprop.1
|
fun:write_webp.constprop.1
|
||||||
fun:vips__webp_write_buffer
|
fun:vips__webp_write_buffer
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
value_libvips_start_thread
|
||||||
|
Memcheck:Value8
|
||||||
|
obj:*/libvips.so.*
|
||||||
|
fun:start_thread
|
||||||
|
fun:clone
|
||||||
|
}
|
||||||
{
|
{
|
||||||
cond_libvips_vips_cast_gen
|
cond_libvips_vips_cast_gen
|
||||||
Memcheck:Cond
|
Memcheck:Cond
|
||||||
@@ -261,6 +276,14 @@
|
|||||||
...
|
...
|
||||||
fun:write_webp_image
|
fun:write_webp_image
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
param_libvips_write_buf
|
||||||
|
Memcheck:Param
|
||||||
|
write(buf)
|
||||||
|
fun:write
|
||||||
|
...
|
||||||
|
fun:start_thread
|
||||||
|
}
|
||||||
{
|
{
|
||||||
leak_libvips_init
|
leak_libvips_init
|
||||||
Memcheck:Leak
|
Memcheck:Leak
|
||||||
@@ -284,6 +307,13 @@
|
|||||||
...
|
...
|
||||||
fun:uv__fs_work
|
fun:uv__fs_work
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
param_libuv_epoll_ctl
|
||||||
|
Memcheck:Param
|
||||||
|
epoll_ctl(event)
|
||||||
|
fun:epoll_ctl
|
||||||
|
fun:uv__io_poll
|
||||||
|
}
|
||||||
{
|
{
|
||||||
cond_libuv_work_done
|
cond_libuv_work_done
|
||||||
Memcheck:Cond
|
Memcheck:Cond
|
||||||
@@ -434,6 +464,30 @@
|
|||||||
...
|
...
|
||||||
fun:_ZN4node12NodePlatformC1EiPN2v817TracingControllerE
|
fun:_ZN4node12NodePlatformC1EiPN2v817TracingControllerE
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
leak_nodejs_start_isolate_data
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: possible
|
||||||
|
fun:_Znwm
|
||||||
|
...
|
||||||
|
fun:_ZN4node5StartEPN2v87IsolateEPNS_11IsolateDataERKSt6vectorISsSaISsEES9_
|
||||||
|
}
|
||||||
|
{
|
||||||
|
leak_nodejs_runtime_stackguard_object_isolate
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: possible
|
||||||
|
fun:_Znwm
|
||||||
|
...
|
||||||
|
fun:_ZN2v88internal18Runtime_StackGuardEiPPNS0_6ObjectEPNS0_7IsolateE
|
||||||
|
}
|
||||||
|
{
|
||||||
|
leak_nodejs_builtin_handleapicall_object_isolate
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: possible
|
||||||
|
fun:_Znwm
|
||||||
|
...
|
||||||
|
fun:_ZN2v88internal21Builtin_HandleApiCallEiPPNS0_6ObjectEPNS0_7IsolateE
|
||||||
|
}
|
||||||
{
|
{
|
||||||
param_nodejs_delayed_task_scheduler
|
param_nodejs_delayed_task_scheduler
|
||||||
Memcheck:Param
|
Memcheck:Param
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ describe('Alpha transparency', function () {
|
|||||||
assert.strictEqual(true, info.size > 0);
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual(32, info.width);
|
assert.strictEqual(32, info.width);
|
||||||
assert.strictEqual(32, info.height);
|
assert.strictEqual(32, info.height);
|
||||||
fixtures.assertMaxColourDistance(output, fixtures.expected('flatten-rgb16-orange.jpg'), 25);
|
fixtures.assertMaxColourDistance(output, fixtures.expected('flatten-rgb16-orange.jpg'), 10);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -81,6 +81,18 @@ describe('Alpha transparency', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Flatten with options but without colour does not throw', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
sharp().flatten({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Flatten to invalid colour throws', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().flatten({ background: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Enlargement with non-nearest neighbor interpolation shouldn’t cause dark edges', function () {
|
it('Enlargement with non-nearest neighbor interpolation shouldn’t cause dark edges', function () {
|
||||||
const base = 'alpha-premultiply-enlargement-2048x1536-paper.png';
|
const base = 'alpha-premultiply-enlargement-2048x1536-paper.png';
|
||||||
const actual = fixtures.path('output.' + base);
|
const actual = fixtures.path('output.' + base);
|
||||||
|
|||||||
@@ -12,3 +12,9 @@ beforeEach(function () {
|
|||||||
sharp.simd(usingSimd);
|
sharp.simd(usingSimd);
|
||||||
sharp.concurrency(concurrency);
|
sharp.concurrency(concurrency);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
if (global.gc) {
|
||||||
|
global.gc();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -62,6 +62,65 @@ describe('composite', () => {
|
|||||||
})
|
})
|
||||||
));
|
));
|
||||||
|
|
||||||
|
it('premultiplied true', () => {
|
||||||
|
const filename = 'composite.premultiplied.png';
|
||||||
|
const below = fixtures.path(`input.below.${filename}`);
|
||||||
|
const above = fixtures.path(`input.above.${filename}`);
|
||||||
|
const actual = fixtures.path(`output.true.${filename}`);
|
||||||
|
const expected = fixtures.expected(`expected.true.${filename}`);
|
||||||
|
return sharp(below)
|
||||||
|
.composite([{
|
||||||
|
input: above,
|
||||||
|
blend: 'color-burn',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
premultiplied: true
|
||||||
|
}])
|
||||||
|
.toFile(actual)
|
||||||
|
.then(() => {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('premultiplied false', () => {
|
||||||
|
const filename = 'composite.premultiplied.png';
|
||||||
|
const below = fixtures.path(`input.below.${filename}`);
|
||||||
|
const above = fixtures.path(`input.above.${filename}`);
|
||||||
|
const actual = fixtures.path(`output.false.${filename}`);
|
||||||
|
const expected = fixtures.expected(`expected.false.${filename}`);
|
||||||
|
return sharp(below)
|
||||||
|
.composite([{
|
||||||
|
input: above,
|
||||||
|
blend: 'color-burn',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
premultiplied: false
|
||||||
|
}])
|
||||||
|
.toFile(actual)
|
||||||
|
.then(() => {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('premultiplied absent', () => {
|
||||||
|
const filename = 'composite.premultiplied.png';
|
||||||
|
const below = fixtures.path(`input.below.${filename}`);
|
||||||
|
const above = fixtures.path(`input.above.${filename}`);
|
||||||
|
const actual = fixtures.path(`output.absent.${filename}`);
|
||||||
|
const expected = fixtures.expected(`expected.absent.${filename}`);
|
||||||
|
return sharp(below)
|
||||||
|
.composite([{
|
||||||
|
input: above,
|
||||||
|
blend: 'color-burn',
|
||||||
|
top: 0,
|
||||||
|
left: 0
|
||||||
|
}])
|
||||||
|
.toFile(actual)
|
||||||
|
.then(() => {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('multiple', () => {
|
it('multiple', () => {
|
||||||
const filename = 'composite-multiple.png';
|
const filename = 'composite-multiple.png';
|
||||||
const actual = fixtures.path(`output.${filename}`);
|
const actual = fixtures.path(`output.${filename}`);
|
||||||
@@ -265,6 +324,12 @@ describe('composite', () => {
|
|||||||
}, /Expected boolean for tile but received invalid of type string/);
|
}, /Expected boolean for tile but received invalid of type string/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('invalid premultiplied', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().composite([{ input: 'test', premultiplied: 'invalid' }]);
|
||||||
|
}, /Expected boolean for premultiplied but received invalid of type string/);
|
||||||
|
});
|
||||||
|
|
||||||
it('invalid left', () => {
|
it('invalid left', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
sharp().composite([{ input: 'test', left: 0.5 }]);
|
sharp().composite([{ input: 'test', left: 0.5 }]);
|
||||||
|
|||||||
@@ -6,6 +6,18 @@ const sharp = require('../../');
|
|||||||
const fixtures = require('../fixtures');
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
describe('Extend', function () {
|
describe('Extend', function () {
|
||||||
|
it('extend all sides equally via a single value', function (done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(120)
|
||||||
|
.extend(10)
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(140, info.width);
|
||||||
|
assert.strictEqual(118, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('extend-equal-single.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('extend all sides equally with RGB', function (done) {
|
it('extend all sides equally with RGB', function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(120)
|
.resize(120)
|
||||||
|
|||||||
@@ -121,6 +121,32 @@ describe('Partial image extraction', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Extract then rotate non-90 anagle', function (done) {
|
||||||
|
sharp(fixtures.inputPngWithGreyAlpha)
|
||||||
|
.extract({ left: 20, top: 10, width: 380, height: 280 })
|
||||||
|
.rotate(45)
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(467, info.width);
|
||||||
|
assert.strictEqual(467, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('extract-rotate-45.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Rotate then extract non-90 angle', function (done) {
|
||||||
|
sharp(fixtures.inputPngWithGreyAlpha)
|
||||||
|
.rotate(45)
|
||||||
|
.extract({ left: 20, top: 10, width: 380, height: 280 })
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(380, info.width);
|
||||||
|
assert.strictEqual(280, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('rotate-extract-45.jpg'), data, { threshold: 7 }, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Invalid parameters', function () {
|
describe('Invalid parameters', function () {
|
||||||
describe('using the legacy extract(top,left,width,height) syntax', function () {
|
describe('using the legacy extract(top,left,width,height) syntax', function () {
|
||||||
it('String top', function () {
|
it('String top', function () {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
const sharp = require('../../');
|
const sharp = require('../../');
|
||||||
const fixtures = require('../fixtures');
|
const fixtures = require('../fixtures');
|
||||||
@@ -72,4 +73,10 @@ describe('failOnError', function () {
|
|||||||
done(err.message.includes('VipsJpeg: Premature end of JPEG file') ? undefined : err);
|
done(err.message.includes('VipsJpeg: Premature end of JPEG file') ? undefined : err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles stream-based input', function () {
|
||||||
|
const writable = sharp({ failOnError: false });
|
||||||
|
fs.createReadStream(fixtures.inputJpgTruncated).pipe(writable);
|
||||||
|
return writable.toBuffer();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
79
test/unit/heif.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
|
||||||
|
const formatHeifOutputBuffer = sharp.format.heif.output.buffer;
|
||||||
|
|
||||||
|
describe('HEIF (experimental)', () => {
|
||||||
|
describe('Stubbed without support for HEIF', () => {
|
||||||
|
before(() => {
|
||||||
|
sharp.format.heif.output.buffer = false;
|
||||||
|
});
|
||||||
|
after(() => {
|
||||||
|
sharp.format.heif.output.buffer = formatHeifOutputBuffer;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Stubbed with support for HEIF', () => {
|
||||||
|
before(() => {
|
||||||
|
sharp.format.heif.output.buffer = true;
|
||||||
|
});
|
||||||
|
after(() => {
|
||||||
|
sharp.format.heif.output.buffer = formatHeifOutputBuffer;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('called without options does not throw an error', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
sharp().heif();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('valid quality does not throw an error', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
sharp().heif({ quality: 50 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('invalid quality should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ quality: 101 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('non-numeric quality should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ quality: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('valid lossless does not throw an error', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
sharp().heif({ lossless: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('non-boolean lossless should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ lossless: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('valid compression does not throw an error', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
sharp().heif({ compression: 'avc' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('unknown compression should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ compression: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('invalid compression should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ compression: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -392,6 +392,22 @@ describe('Input/output', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Stream input with corrupt header fails gracefully', function (done) {
|
||||||
|
const transformer = sharp();
|
||||||
|
transformer
|
||||||
|
.toBuffer()
|
||||||
|
.then(function () {
|
||||||
|
done(new Error('Unexpectedly resolved Promise'));
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
assert.strictEqual(true, !!err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
fs
|
||||||
|
.createReadStream(fixtures.inputJpgWithCorruptHeader)
|
||||||
|
.pipe(transformer);
|
||||||
|
});
|
||||||
|
|
||||||
describe('Output filename with unknown extension', function () {
|
describe('Output filename with unknown extension', function () {
|
||||||
it('Match JPEG input', function (done) {
|
it('Match JPEG input', function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
@@ -632,6 +648,16 @@ describe('Input/output', function () {
|
|||||||
it('Ignore unknown attribute', function () {
|
it('Ignore unknown attribute', function () {
|
||||||
sharp(null, { unknown: true });
|
sharp(null, { unknown: true });
|
||||||
});
|
});
|
||||||
|
it('Invalid page property throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(null, { page: -1 });
|
||||||
|
}, /Expected integer between 0 and 100000 for page but received -1 of type number/);
|
||||||
|
});
|
||||||
|
it('Invalid pages property throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(null, { pages: '1' });
|
||||||
|
}, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create new image', function () {
|
describe('create new image', function () {
|
||||||
|
|||||||
@@ -259,4 +259,35 @@ describe('JPEG', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Specifying quantization table provides different JPEG', function (done) {
|
||||||
|
// First generate with default quantization table
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ optimiseCoding: false })
|
||||||
|
.toBuffer(function (err, withDefaultQuantizationTable, withInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withDefaultQuantizationTable.length > 0);
|
||||||
|
assert.strictEqual(withDefaultQuantizationTable.length, withInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withInfo.format);
|
||||||
|
assert.strictEqual(320, withInfo.width);
|
||||||
|
assert.strictEqual(240, withInfo.height);
|
||||||
|
// Then generate with different quantization table
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ optimiseCoding: false, quantizationTable: 3 })
|
||||||
|
.toBuffer(function (err, withQuantTable3, withoutInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withQuantTable3.length > 0);
|
||||||
|
assert.strictEqual(withQuantTable3.length, withoutInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withoutInfo.format);
|
||||||
|
assert.strictEqual(320, withoutInfo.width);
|
||||||
|
assert.strictEqual(240, withoutInfo.height);
|
||||||
|
|
||||||
|
// Verify image is same (as mozjpeg may not be present) size or less
|
||||||
|
assert.strictEqual(true, withQuantTable3.length <= withDefaultQuantizationTable.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ describe('Linear adjustment', function () {
|
|||||||
|
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp(fixtures.inputPngOverlayLayer1)
|
sharp(fixtures.inputPngOverlayLayer1)
|
||||||
.linear(undefined, { 'bar': 'baz' });
|
.linear(undefined, { bar: 'baz' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ describe('Image metadata', function () {
|
|||||||
// XMP
|
// XMP
|
||||||
assert.strictEqual('object', typeof metadata.xmp);
|
assert.strictEqual('object', typeof metadata.xmp);
|
||||||
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||||
assert.strictEqual(12495, metadata.xmp.byteLength);
|
assert.strictEqual(12466, metadata.xmp.byteLength);
|
||||||
assert.strictEqual(metadata.xmp.indexOf(Buffer.from('http://ns.adobe.com/xap/1.0')), 0);
|
assert.strictEqual(metadata.xmp.indexOf(Buffer.from('<?xpacket begin="')), 0);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -232,6 +232,26 @@ describe('Image metadata', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('vips', () =>
|
||||||
|
sharp(fixtures.inputV)
|
||||||
|
.metadata()
|
||||||
|
.then(metadata => {
|
||||||
|
assert.strictEqual('vips', metadata.format);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.size);
|
||||||
|
assert.strictEqual(70, metadata.width);
|
||||||
|
assert.strictEqual(60, metadata.height);
|
||||||
|
assert.strictEqual(3, metadata.channels);
|
||||||
|
assert.strictEqual('uchar', metadata.depth);
|
||||||
|
assert.strictEqual(72, metadata.density);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.chromaSubsampling);
|
||||||
|
assert.strictEqual(false, metadata.isProgressive);
|
||||||
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.orientation);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.exif);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.icc);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
it('File in, Promise out', function (done) {
|
it('File in, Promise out', function (done) {
|
||||||
sharp(fixtures.inputJpg).metadata().then(function (metadata) {
|
sharp(fixtures.inputJpg).metadata().then(function (metadata) {
|
||||||
@@ -287,6 +307,20 @@ describe('Image metadata', function () {
|
|||||||
readable.pipe(pipeline);
|
readable.pipe(pipeline);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Stream in, rejected Promise out', () => {
|
||||||
|
const pipeline = sharp();
|
||||||
|
fs
|
||||||
|
.createReadStream(__filename)
|
||||||
|
.pipe(pipeline);
|
||||||
|
|
||||||
|
return pipeline
|
||||||
|
.metadata()
|
||||||
|
.then(
|
||||||
|
() => Promise.reject(new Error('Expected metadata to reject')),
|
||||||
|
err => assert.strictEqual(err.message, 'Input buffer contains unsupported image format')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('Stream', function (done) {
|
it('Stream', function (done) {
|
||||||
const readable = fs.createReadStream(fixtures.inputJpg);
|
const readable = fs.createReadStream(fixtures.inputJpg);
|
||||||
const pipeline = sharp().metadata(function (err, metadata) {
|
const pipeline = sharp().metadata(function (err, metadata) {
|
||||||
@@ -370,6 +404,34 @@ describe('Image metadata', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Include metadata in output, enabled via empty object', () =>
|
||||||
|
sharp(fixtures.inputJpgWithExif)
|
||||||
|
.withMetadata({})
|
||||||
|
.toBuffer()
|
||||||
|
.then((buffer) => sharp(buffer)
|
||||||
|
.metadata()
|
||||||
|
.then(metadata => {
|
||||||
|
assert.strictEqual(true, metadata.hasProfile);
|
||||||
|
assert.strictEqual(8, metadata.orientation);
|
||||||
|
assert.strictEqual('object', typeof metadata.exif);
|
||||||
|
assert.strictEqual(true, metadata.exif instanceof Buffer);
|
||||||
|
// EXIF
|
||||||
|
const exif = exifReader(metadata.exif);
|
||||||
|
assert.strictEqual('object', typeof exif);
|
||||||
|
assert.strictEqual('object', typeof exif.image);
|
||||||
|
assert.strictEqual('number', typeof exif.image.XResolution);
|
||||||
|
// ICC
|
||||||
|
assert.strictEqual('object', typeof metadata.icc);
|
||||||
|
assert.strictEqual(true, metadata.icc instanceof Buffer);
|
||||||
|
const profile = icc.parse(metadata.icc);
|
||||||
|
assert.strictEqual('object', typeof profile);
|
||||||
|
assert.strictEqual('RGB', profile.colorSpace);
|
||||||
|
assert.strictEqual('Perceptual', profile.intent);
|
||||||
|
assert.strictEqual('Monitor', profile.deviceClass);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
it('Remove EXIF metadata after a resize', function (done) {
|
it('Remove EXIF metadata after a resize', function (done) {
|
||||||
sharp(fixtures.inputJpgWithExif)
|
sharp(fixtures.inputJpgWithExif)
|
||||||
.resize(320, 240)
|
.resize(320, 240)
|
||||||
|
|||||||
@@ -1,589 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
const fixtures = require('../fixtures');
|
|
||||||
const sharp = require('../../');
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
const getPaths = function (baseName, extension) {
|
|
||||||
if (typeof extension === 'undefined') {
|
|
||||||
extension = 'png';
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
actual: fixtures.path('output.' + baseName + '.' + extension),
|
|
||||||
expected: fixtures.expected(baseName + '.' + extension)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Test
|
|
||||||
describe('Overlays', function () {
|
|
||||||
it('Overlay transparent PNG file on solid background', function (done) {
|
|
||||||
const paths = getPaths('alpha-layer-01');
|
|
||||||
|
|
||||||
sharp(fixtures.inputPngOverlayLayer0)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer1)
|
|
||||||
.toFile(paths.actual, function (error) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay transparent PNG Buffer on solid background', function (done) {
|
|
||||||
const paths = getPaths('alpha-layer-01');
|
|
||||||
|
|
||||||
sharp(fixtures.inputPngOverlayLayer0)
|
|
||||||
.overlayWith(fs.readFileSync(fixtures.inputPngOverlayLayer1))
|
|
||||||
.toFile(paths.actual, function (error) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay low-alpha transparent PNG on solid background', function (done) {
|
|
||||||
const paths = getPaths('alpha-layer-01-low-alpha');
|
|
||||||
|
|
||||||
sharp(fixtures.inputPngOverlayLayer0)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer1LowAlpha)
|
|
||||||
.toFile(paths.actual, function (error) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite three transparent PNGs into one', function (done) {
|
|
||||||
const paths = getPaths('alpha-layer-012');
|
|
||||||
|
|
||||||
sharp(fixtures.inputPngOverlayLayer0)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer1)
|
|
||||||
.toBuffer(function (error, data) {
|
|
||||||
if (error) return done(error);
|
|
||||||
sharp(data)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer2)
|
|
||||||
.toFile(paths.actual, function (error) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite two transparent PNGs into one', function (done) {
|
|
||||||
const paths = getPaths('alpha-layer-12');
|
|
||||||
|
|
||||||
sharp(fixtures.inputPngOverlayLayer1)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer2)
|
|
||||||
.toFile(paths.actual, function (error) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite two low-alpha transparent PNGs into one', function (done) {
|
|
||||||
const paths = getPaths('alpha-layer-12-low-alpha');
|
|
||||||
|
|
||||||
sharp(fixtures.inputPngOverlayLayer1LowAlpha)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer2LowAlpha)
|
|
||||||
.toFile(paths.actual, function (error) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected, 2);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite three low-alpha transparent PNGs into one', function (done) {
|
|
||||||
const paths = getPaths('alpha-layer-012-low-alpha');
|
|
||||||
|
|
||||||
sharp(fixtures.inputPngOverlayLayer0)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer1LowAlpha)
|
|
||||||
.toBuffer(function (error, data) {
|
|
||||||
if (error) return done(error);
|
|
||||||
|
|
||||||
sharp(data)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer2LowAlpha)
|
|
||||||
.toFile(paths.actual, function (error) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite rgb+alpha PNG onto JPEG', function (done) {
|
|
||||||
const paths = getPaths('overlay-jpeg-with-rgb', 'jpg');
|
|
||||||
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(2048, 1536)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer1)
|
|
||||||
.toFile(paths.actual, function (error, info) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected, 102);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite greyscale+alpha PNG onto JPEG', function (done) {
|
|
||||||
const paths = getPaths('overlay-jpeg-with-greyscale', 'jpg');
|
|
||||||
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400, 300)
|
|
||||||
.overlayWith(fixtures.inputPngWithGreyAlpha)
|
|
||||||
.toFile(paths.actual, function (error, info) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected, 102);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite WebP onto JPEG', function (done) {
|
|
||||||
const paths = getPaths('overlay-jpeg-with-webp', 'jpg');
|
|
||||||
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(300, 300)
|
|
||||||
.overlayWith(fixtures.inputWebPWithTransparency)
|
|
||||||
.toFile(paths.actual, function (error, info) {
|
|
||||||
if (error) return done(error);
|
|
||||||
fixtures.assertMaxColourDistance(paths.actual, paths.expected, 102);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite JPEG onto PNG, ensure premultiply', function (done) {
|
|
||||||
sharp(fixtures.inputPngOverlayLayer1)
|
|
||||||
.overlayWith(fixtures.inputJpgWithLandscapeExif1)
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual(true, info.premultiplied);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite opaque JPEG onto JPEG, ensure premultiply', function (done) {
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.overlayWith(fixtures.inputJpgWithLandscapeExif1)
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual(true, info.premultiplied);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Fail when overlay is larger', function (done) {
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(320)
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer1)
|
|
||||||
.toBuffer(function (error) {
|
|
||||||
assert.strictEqual(true, error instanceof Error);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Fail with empty String parameter', function () {
|
|
||||||
assert.throws(function () {
|
|
||||||
sharp().overlayWith('');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Fail with non-String parameter', function () {
|
|
||||||
assert.throws(function () {
|
|
||||||
sharp().overlayWith(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Fail with unsupported gravity', function () {
|
|
||||||
assert.throws(function () {
|
|
||||||
sharp()
|
|
||||||
.overlayWith(fixtures.inputPngOverlayLayer1, {
|
|
||||||
gravity: 9
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Empty options', function () {
|
|
||||||
assert.doesNotThrow(function () {
|
|
||||||
sharp().overlayWith(fixtures.inputPngOverlayLayer1, {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Overlay with numeric gravity', function () {
|
|
||||||
Object.keys(sharp.gravity).forEach(function (gravity) {
|
|
||||||
it(gravity, function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-gravity-' + gravity + '.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(80)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
gravity: gravity
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(80, info.width);
|
|
||||||
assert.strictEqual(65, info.height);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Overlay with string-based gravity', function () {
|
|
||||||
Object.keys(sharp.gravity).forEach(function (gravity) {
|
|
||||||
it(gravity, function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-gravity-' + gravity + '.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(80)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
gravity: sharp.gravity[gravity]
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(80, info.width);
|
|
||||||
assert.strictEqual(65, info.height);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Overlay with tile enabled and gravity', function () {
|
|
||||||
Object.keys(sharp.gravity).forEach(function (gravity) {
|
|
||||||
it(gravity, function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-tile-gravity-' + gravity + '.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(80)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
tile: true,
|
|
||||||
gravity: gravity
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(80, info.width);
|
|
||||||
assert.strictEqual(65, info.height);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Overlay with top-left offsets', function () {
|
|
||||||
it('Overlay with 10px top & 10px left offsets', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-valid-offsets-10-10.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
top: 10,
|
|
||||||
left: 10
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with 100px top & 300px left offsets', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-valid-offsets-100-300.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
top: 100,
|
|
||||||
left: 300
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with only top offset', function () {
|
|
||||||
assert.throws(function () {
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
top: 1000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with only left offset', function () {
|
|
||||||
assert.throws(function () {
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
left: 1000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with negative offsets', function () {
|
|
||||||
assert.throws(function () {
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
top: -1000,
|
|
||||||
left: -1000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with 0 offset', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-offset-0.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
top: 0,
|
|
||||||
left: 0
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with offset and gravity', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-offset-with-gravity.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
left: 10,
|
|
||||||
top: 10,
|
|
||||||
gravity: 4
|
|
||||||
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with offset and gravity and tile', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-offset-with-gravity-tile.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
left: 10,
|
|
||||||
top: 10,
|
|
||||||
gravity: 4,
|
|
||||||
tile: true
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with offset and tile', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-offset-with-tile.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
left: 10,
|
|
||||||
top: 10,
|
|
||||||
tile: true
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with invalid tile option', function () {
|
|
||||||
assert.throws(function () {
|
|
||||||
sharp().overlayWith('ignore', { tile: 1 });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay with very large offset', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-very-large-offset.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(400)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
left: 10000,
|
|
||||||
top: 10000
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Overlay 100x100 with 50x50 so bottom edges meet', function (done) {
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(50, 50)
|
|
||||||
.toBuffer(function (err, overlay) {
|
|
||||||
if (err) throw err;
|
|
||||||
sharp(fixtures.inputJpgWithLandscapeExif1)
|
|
||||||
.resize(100, 100)
|
|
||||||
.overlayWith(overlay, {
|
|
||||||
top: 50,
|
|
||||||
left: 40
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(100, info.width);
|
|
||||||
assert.strictEqual(100, info.height);
|
|
||||||
fixtures.assertSimilar(fixtures.expected('overlay-bottom-edges-meet.jpg'), data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('With tile enabled and image rotated 90 degrees', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-tile-rotated90.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.rotate(90)
|
|
||||||
.resize(80)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
tile: true
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(80, info.width);
|
|
||||||
assert.strictEqual(98, info.height);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('With tile enabled and image rotated 90 degrees and gravity northwest', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-tile-rotated90-gravity-northwest.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.rotate(90)
|
|
||||||
.resize(80)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
tile: true,
|
|
||||||
gravity: 'northwest'
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(80, info.width);
|
|
||||||
assert.strictEqual(98, info.height);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Overlay with cutout enabled and gravity', function () {
|
|
||||||
Object.keys(sharp.gravity).forEach(function (gravity) {
|
|
||||||
it(gravity, function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-cutout-gravity-' + gravity + '.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(80)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
cutout: true,
|
|
||||||
gravity: gravity
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(80, info.width);
|
|
||||||
assert.strictEqual(65, info.height);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('With cutout enabled and image rotated 90 degrees', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-cutout-rotated90.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.rotate(90)
|
|
||||||
.resize(80)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
cutout: true
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(80, info.width);
|
|
||||||
assert.strictEqual(98, info.height);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('With cutout enabled and image rotated 90 degrees and gravity northwest', function (done) {
|
|
||||||
const expected = fixtures.expected('overlay-cutout-rotated90-gravity-northwest.jpg');
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.rotate(90)
|
|
||||||
.resize(80)
|
|
||||||
.overlayWith(fixtures.inputPngWithTransparency16bit, {
|
|
||||||
cutout: true,
|
|
||||||
gravity: 'northwest'
|
|
||||||
})
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(80, info.width);
|
|
||||||
assert.strictEqual(98, info.height);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
fixtures.assertSimilar(expected, data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite RGBA raw buffer onto JPEG', function (done) {
|
|
||||||
sharp(fixtures.inputPngOverlayLayer1)
|
|
||||||
.raw()
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(2048, 1536)
|
|
||||||
.overlayWith(data, { raw: info })
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual(true, info.premultiplied);
|
|
||||||
fixtures.assertSimilar(fixtures.expected('overlay-jpeg-with-rgb.jpg'), data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Returns an error when called with an invalid file', function (done) {
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.overlayWith('notfound.png')
|
|
||||||
.toBuffer(function (err) {
|
|
||||||
assert(err instanceof Error);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Composite JPEG onto JPEG', function (done) {
|
|
||||||
sharp(fixtures.inputJpg)
|
|
||||||
.resize(480, 320)
|
|
||||||
.overlayWith(fixtures.inputJpgBooleanTest)
|
|
||||||
.toBuffer(function (err, data, info) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual('jpeg', info.format);
|
|
||||||
assert.strictEqual(480, info.width);
|
|
||||||
assert.strictEqual(320, info.height);
|
|
||||||
assert.strictEqual(3, info.channels);
|
|
||||||
assert.strictEqual(true, info.premultiplied);
|
|
||||||
fixtures.assertSimilar(fixtures.expected('overlay-jpeg-with-jpeg.jpg'), data, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -53,7 +53,8 @@ describe('Raw pixel data', function () {
|
|||||||
width: info.width,
|
width: info.width,
|
||||||
height: info.height,
|
height: info.height,
|
||||||
channels: info.channels
|
channels: info.channels
|
||||||
} })
|
}
|
||||||
|
})
|
||||||
.jpeg()
|
.jpeg()
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@@ -81,7 +82,8 @@ describe('Raw pixel data', function () {
|
|||||||
width: info.width,
|
width: info.width,
|
||||||
height: info.height,
|
height: info.height,
|
||||||
channels: info.channels
|
channels: info.channels
|
||||||
} })
|
}
|
||||||
|
})
|
||||||
.png()
|
.png()
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ describe('Resize fit=cover', function () {
|
|||||||
assert.strictEqual(3, info.channels);
|
assert.strictEqual(3, info.channels);
|
||||||
assert.strictEqual(80, info.width);
|
assert.strictEqual(80, info.width);
|
||||||
assert.strictEqual(320, info.height);
|
assert.strictEqual(320, info.height);
|
||||||
assert.strictEqual(-143, info.cropOffsetLeft);
|
assert.strictEqual(-107, info.cropOffsetLeft);
|
||||||
assert.strictEqual(0, info.cropOffsetTop);
|
assert.strictEqual(0, info.cropOffsetTop);
|
||||||
fixtures.assertSimilar(fixtures.expected('crop-strategy-attention.jpg'), data, done);
|
fixtures.assertSimilar(fixtures.expected('crop-strategy-attention.jpg'), data, done);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -87,6 +87,18 @@ describe('Resize dimensions', function () {
|
|||||||
}, /Expected positive integer for height but received 1.5 of type number/);
|
}, /Expected positive integer for height but received 1.5 of type number/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Invalid width - via options', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().resize({ width: 1.5, height: 240 });
|
||||||
|
}, /Expected positive integer for width but received 1.5 of type number/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid height - via options', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().resize({ width: 320, height: 1.5 });
|
||||||
|
}, /Expected positive integer for height but received 1.5 of type number/);
|
||||||
|
});
|
||||||
|
|
||||||
it('Invalid width - too large', function (done) {
|
it('Invalid width - too large', function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(0x4000, 1)
|
.resize(0x4000, 1)
|
||||||
|
|||||||
@@ -27,52 +27,52 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0]['min']);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(255, stats.channels[0]['max']);
|
assert.strictEqual(255, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 615101275));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 615101275));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 83061892917));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 83061892917));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 101.44954540768993));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 101.44954540768993));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 58.373870588815414));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 58.373870588815414));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 2725));
|
||||||
|
|
||||||
// green channel
|
// green channel
|
||||||
assert.strictEqual(0, stats.channels[1]['min']);
|
assert.strictEqual(0, stats.channels[1].min);
|
||||||
assert.strictEqual(255, stats.channels[1]['max']);
|
assert.strictEqual(255, stats.channels[1].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 462824115));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 462824115));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 47083677255));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 47083677255));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 76.33425255128337));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 76.33425255128337));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 44.03023262954866));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 44.03023262954866));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[1].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxY, 0, 2725));
|
||||||
|
|
||||||
// blue channel
|
// blue channel
|
||||||
assert.strictEqual(0, stats.channels[2]['min']);
|
assert.strictEqual(0, stats.channels[2].min);
|
||||||
assert.strictEqual(255, stats.channels[2]['max']);
|
assert.strictEqual(255, stats.channels[2].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 372986756));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 372986756));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 32151543524));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 32151543524));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 61.51724663436759));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 61.51724663436759));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 38.96702865090125));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 38.96702865090125));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[2].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxY, 0, 2725));
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -86,20 +86,20 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3409031108021736));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3409031108021736));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0]['min']);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(255, stats.channels[0]['max']);
|
assert.strictEqual(255, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 1391368230));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 1391368230));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 354798898650));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 354798898650));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 238.8259925648822));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 238.8259925648822));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 62.15121915523771));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 62.15121915523771));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2809));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2809));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2074));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 2074));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2809));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2809));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2074));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 2074));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -112,68 +112,68 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.06778064835816622));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.06778064835816622));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0]['min']);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(255, stats.channels[0]['max']);
|
assert.strictEqual(255, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 795678795));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 795678795));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 202898092725));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 202898092725));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 252.9394769668579));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 252.9394769668579));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 22.829537532816));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 22.829537532816));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2048));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2048));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 1536));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 1536));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2048));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2048));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 1536));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 1536));
|
||||||
|
|
||||||
// green channel
|
// green channel
|
||||||
assert.strictEqual(0, stats.channels[1]['min']);
|
assert.strictEqual(0, stats.channels[1].min);
|
||||||
assert.strictEqual(255, stats.channels[1]['max']);
|
assert.strictEqual(255, stats.channels[1].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 795678795));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 795678795));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 202898092725));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 202898092725));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 252.9394769668579));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 252.9394769668579));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 22.829537532816));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 22.829537532816));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 2048));
|
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 2048));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 1536));
|
assert.strictEqual(true, isInRange(stats.channels[1].minY, 0, 1536));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 2048));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxX, 0, 2048));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 1536));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxY, 0, 1536));
|
||||||
|
|
||||||
// blue channel
|
// blue channel
|
||||||
assert.strictEqual(0, stats.channels[2]['min']);
|
assert.strictEqual(0, stats.channels[2].min);
|
||||||
assert.strictEqual(255, stats.channels[2]['max']);
|
assert.strictEqual(255, stats.channels[2].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 795678795));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 795678795));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 202898092725));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 202898092725));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 252.9394769668579));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 252.9394769668579));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 22.829537532816));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 22.829537532816));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minX'], 0, 2048));
|
assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 2048));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 1536));
|
assert.strictEqual(true, isInRange(stats.channels[2].minY, 0, 1536));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 2048));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxX, 0, 2048));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 1536));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxY, 0, 1536));
|
||||||
|
|
||||||
// alpha channel
|
// alpha channel
|
||||||
assert.strictEqual(0, stats.channels[3]['min']);
|
assert.strictEqual(0, stats.channels[3].min);
|
||||||
assert.strictEqual(255, stats.channels[3]['max']);
|
assert.strictEqual(255, stats.channels[3].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['sum'], 5549142));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].sum, 5549142));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['squaresSum'], 1333571132));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].squaresSum, 1333571132));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['mean'], 1.7640247344970703));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].mean, 1.7640247344970703));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['stdev'], 20.51387814157297));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].stdev, 20.51387814157297));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[3]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[3].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[3]['minX'], 0, 2048));
|
assert.strictEqual(true, isInRange(stats.channels[3].minX, 0, 2048));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[3]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[3].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[3]['minY'], 0, 1536));
|
assert.strictEqual(true, isInRange(stats.channels[3].minY, 0, 1536));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[3]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[3].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[3]['maxX'], 0, 2048));
|
assert.strictEqual(true, isInRange(stats.channels[3].maxX, 0, 2048));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[3]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[3].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[3]['maxY'], 0, 1536));
|
assert.strictEqual(true, isInRange(stats.channels[3].maxY, 0, 1536));
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -187,20 +187,20 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0));
|
||||||
|
|
||||||
// alpha channel
|
// alpha channel
|
||||||
assert.strictEqual(0, stats.channels[3]['min']);
|
assert.strictEqual(0, stats.channels[3].min);
|
||||||
assert.strictEqual(0, stats.channels[3]['max']);
|
assert.strictEqual(0, stats.channels[3].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['sum'], 0));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].sum, 0));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['squaresSum'], 0));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].squaresSum, 0));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['mean'], 0));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].mean, 0));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['stdev'], 0));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].stdev, 0));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[3]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[3].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[3]['minX'], 0, 300));
|
assert.strictEqual(true, isInRange(stats.channels[3].minX, 0, 300));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[3]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[3].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[3]['minY'], 0, 300));
|
assert.strictEqual(true, isInRange(stats.channels[3].minY, 0, 300));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[3]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[3].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[3]['maxX'], 0, 300));
|
assert.strictEqual(true, isInRange(stats.channels[3].maxX, 0, 300));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[3]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[3].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[3]['maxY'], 0, 300));
|
assert.strictEqual(true, isInRange(stats.channels[3].maxY, 0, 300));
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -214,20 +214,20 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3851250782608986));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3851250782608986));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0]['min']);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(255, stats.channels[0]['max']);
|
assert.strictEqual(255, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 1887266220));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 1887266220));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 481252886100));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 481252886100));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 235.81772349417824));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 235.81772349417824));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 67.25712856093298));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 67.25712856093298));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2464));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2464));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 3248));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 3248));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2464));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2464));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 3248));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 3248));
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -241,52 +241,52 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.51758075132966));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.51758075132966));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0]['min']);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(255, stats.channels[0]['max']);
|
assert.strictEqual(255, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 83291370));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 83291370));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 11379783198));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 11379783198));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 105.36169496842616));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 105.36169496842616));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 57.39412151419967));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 57.39412151419967));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 1024));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 1024));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 772));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 772));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 1024));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 1024));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 772));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 772));
|
||||||
|
|
||||||
// green channel
|
// green channel
|
||||||
assert.strictEqual(0, stats.channels[1]['min']);
|
assert.strictEqual(0, stats.channels[1].min);
|
||||||
assert.strictEqual(255, stats.channels[1]['max']);
|
assert.strictEqual(255, stats.channels[1].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 120877425));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 120877425));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 20774687595));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 20774687595));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 152.9072025279307));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 152.9072025279307));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 53.84143349689916));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 53.84143349689916));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 1024));
|
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 1024));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 772));
|
assert.strictEqual(true, isInRange(stats.channels[1].minY, 0, 772));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 1024));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxX, 0, 1024));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 772));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxY, 0, 772));
|
||||||
|
|
||||||
// blue channel
|
// blue channel
|
||||||
assert.strictEqual(0, stats.channels[2]['min']);
|
assert.strictEqual(0, stats.channels[2].min);
|
||||||
assert.strictEqual(255, stats.channels[2]['max']);
|
assert.strictEqual(255, stats.channels[2].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 138938859));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 138938859));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 28449125593));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 28449125593));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 175.75450711423252));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 175.75450711423252));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 71.39929031070358));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 71.39929031070358));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minX'], 0, 1024));
|
assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 1024));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 772));
|
assert.strictEqual(true, isInRange(stats.channels[2].minY, 0, 772));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 1024));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxX, 0, 1024));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 772));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxY, 0, 772));
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -300,52 +300,52 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 6.087309412541799));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 6.087309412541799));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(35, stats.channels[0]['min']);
|
assert.strictEqual(35, stats.channels[0].min);
|
||||||
assert.strictEqual(254, stats.channels[0]['max']);
|
assert.strictEqual(254, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 56088385));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 56088385));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 8002132113));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 8002132113));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 131.53936444652908));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 131.53936444652908));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 38.26389131415863));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 38.26389131415863));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 800));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 800));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 533));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 533));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 800));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 800));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 533));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 533));
|
||||||
|
|
||||||
// green channel
|
// green channel
|
||||||
assert.strictEqual(43, stats.channels[1]['min']);
|
assert.strictEqual(43, stats.channels[1].min);
|
||||||
assert.strictEqual(255, stats.channels[1]['max']);
|
assert.strictEqual(255, stats.channels[1].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 58612156));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 58612156));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 8548344254));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 8548344254));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 137.45815196998123));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 137.45815196998123));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 33.955424103758205));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 33.955424103758205));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 800));
|
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 800));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 533));
|
assert.strictEqual(true, isInRange(stats.channels[1].minY, 0, 533));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 800));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxX, 0, 800));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 533));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxY, 0, 533));
|
||||||
|
|
||||||
// blue channel
|
// blue channel
|
||||||
assert.strictEqual(51, stats.channels[2]['min']);
|
assert.strictEqual(51, stats.channels[2].min);
|
||||||
assert.strictEqual(254, stats.channels[2]['max']);
|
assert.strictEqual(254, stats.channels[2].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 49628525));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 49628525));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 6450556071));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 6450556071));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 116.38959896810506));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 116.38959896810506));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 39.7669551046809));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 39.7669551046809));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minX'], 0, 800));
|
assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 800));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 533));
|
assert.strictEqual(true, isInRange(stats.channels[2].minY, 0, 533));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 800));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxX, 0, 800));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 533));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxY, 0, 533));
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -359,41 +359,50 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 1));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 1));
|
||||||
|
|
||||||
// gray channel
|
// gray channel
|
||||||
assert.strictEqual(0, stats.channels[0]['min']);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(101, stats.channels[0]['max']);
|
assert.strictEqual(101, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 101));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 101));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 10201));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 10201));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 50.5));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 50.5));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 71.4177848998413));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 71.4177848998413));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 1));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 1));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 1));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 1));
|
||||||
|
|
||||||
// alpha channel
|
// alpha channel
|
||||||
assert.strictEqual(0, stats.channels[1]['min']);
|
assert.strictEqual(0, stats.channels[1].min);
|
||||||
assert.strictEqual(255, stats.channels[1]['max']);
|
assert.strictEqual(255, stats.channels[1].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 255));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 255));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 65025));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 65025));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 127.5));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 127.5));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 180.31222920256963));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 180.31222920256963));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 2));
|
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 2));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 1));
|
assert.strictEqual(true, isInRange(stats.channels[1].minY, 0, 1));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 2));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxX, 0, 2));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 1));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxY, 0, 1));
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('CMYK input without profile', () =>
|
||||||
|
sharp(fixtures.inputJpgWithCmykNoProfile)
|
||||||
|
.stats()
|
||||||
|
.then(stats => {
|
||||||
|
assert.strictEqual(4, stats.channels.length);
|
||||||
|
assert.strictEqual(true, stats.isOpaque);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
it('Stream in, Callback out', function (done) {
|
it('Stream in, Callback out', function (done) {
|
||||||
const readable = fs.createReadStream(fixtures.inputJpg);
|
const readable = fs.createReadStream(fixtures.inputJpg);
|
||||||
const pipeline = sharp().stats(function (err, stats) {
|
const pipeline = sharp().stats(function (err, stats) {
|
||||||
@@ -403,52 +412,52 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0]['min']);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(255, stats.channels[0]['max']);
|
assert.strictEqual(255, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 615101275));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 615101275));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 83061892917));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 83061892917));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 101.44954540768993));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 101.44954540768993));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 58.373870588815414));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 58.373870588815414));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 2725));
|
||||||
|
|
||||||
// green channel
|
// green channel
|
||||||
assert.strictEqual(0, stats.channels[1]['min']);
|
assert.strictEqual(0, stats.channels[1].min);
|
||||||
assert.strictEqual(255, stats.channels[1]['max']);
|
assert.strictEqual(255, stats.channels[1].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 462824115));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 462824115));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 47083677255));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 47083677255));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 76.33425255128337));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 76.33425255128337));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 44.03023262954866));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 44.03023262954866));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[1].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[1].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[1].maxY, 0, 2725));
|
||||||
|
|
||||||
// blue channel
|
// blue channel
|
||||||
assert.strictEqual(0, stats.channels[2]['min']);
|
assert.strictEqual(0, stats.channels[2].min);
|
||||||
assert.strictEqual(255, stats.channels[2]['max']);
|
assert.strictEqual(255, stats.channels[2].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 372986756));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 372986756));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 32151543524));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 32151543524));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 61.51724663436759));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 61.51724663436759));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 38.96702865090125));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 38.96702865090125));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[2].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[2].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[2].maxY, 0, 2725));
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -465,52 +474,52 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0]['min']);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(255, stats.channels[0]['max']);
|
assert.strictEqual(255, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 615101275));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 615101275));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 83061892917));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 83061892917));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 101.44954540768993));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 101.44954540768993));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 58.373870588815414));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 58.373870588815414));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 2725));
|
||||||
|
|
||||||
// green channel
|
// green channel
|
||||||
assert.strictEqual(0, stats.channels[1]['min']);
|
assert.strictEqual(0, stats.channels[1].min);
|
||||||
assert.strictEqual(255, stats.channels[1]['max']);
|
assert.strictEqual(255, stats.channels[1].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 462824115));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 462824115));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 47083677255));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 47083677255));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 76.33425255128337));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 76.33425255128337));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 44.03023262954866));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 44.03023262954866));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 2725));
|
||||||
|
|
||||||
// blue channel
|
// blue channel
|
||||||
assert.strictEqual(0, stats.channels[2]['min']);
|
assert.strictEqual(0, stats.channels[2].min);
|
||||||
assert.strictEqual(255, stats.channels[2]['max']);
|
assert.strictEqual(255, stats.channels[2].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 372986756));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 372986756));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 32151543524));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 32151543524));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 61.51724663436759));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 61.51724663436759));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 38.96702865090125));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 38.96702865090125));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 2725));
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
@@ -522,52 +531,52 @@ describe('Image Stats', function () {
|
|||||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||||
|
|
||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0]['min']);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(255, stats.channels[0]['max']);
|
assert.strictEqual(255, stats.channels[0].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 615101275));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 615101275));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 83061892917));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 83061892917));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 101.44954540768993));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 101.44954540768993));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 58.373870588815414));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 58.373870588815414));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 2725));
|
||||||
|
|
||||||
// green channel
|
// green channel
|
||||||
assert.strictEqual(0, stats.channels[1]['min']);
|
assert.strictEqual(0, stats.channels[1].min);
|
||||||
assert.strictEqual(255, stats.channels[1]['max']);
|
assert.strictEqual(255, stats.channels[1].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 462824115));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 462824115));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 47083677255));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 47083677255));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 76.33425255128337));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 76.33425255128337));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 44.03023262954866));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 44.03023262954866));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 2725));
|
||||||
|
|
||||||
// blue channel
|
// blue channel
|
||||||
assert.strictEqual(0, stats.channels[2]['min']);
|
assert.strictEqual(0, stats.channels[2].min);
|
||||||
assert.strictEqual(255, stats.channels[2]['max']);
|
assert.strictEqual(255, stats.channels[2].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 372986756));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 372986756));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 32151543524));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 32151543524));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 61.51724663436759));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 61.51724663436759));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 38.96702865090125));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 38.96702865090125));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].minY, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxX, 0, 2725));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
|
assert.strictEqual(true, isInteger(stats.channels[0].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
|
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 2725));
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -85,6 +85,19 @@ describe('TIFF', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Increasing TIFF quality increases file size', () =>
|
||||||
|
sharp(fixtures.inputJpgWithLandscapeExif1)
|
||||||
|
.tiff({ quality: 40 })
|
||||||
|
.toBuffer()
|
||||||
|
.then(tiff40 => sharp(fixtures.inputJpgWithLandscapeExif1)
|
||||||
|
.tiff({ quality: 90 })
|
||||||
|
.toBuffer()
|
||||||
|
.then(tiff90 =>
|
||||||
|
assert.strictEqual(true, tiff40.length < tiff90.length)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
it('Invalid TIFF quality throws error', function () {
|
it('Invalid TIFF quality throws error', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().tiff({ quality: 101 });
|
sharp().tiff({ quality: 101 });
|
||||||
@@ -109,7 +122,7 @@ describe('TIFF', function () {
|
|||||||
.toFile(fixtures.outputTiff, (err, info) => {
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('tiff', info.format);
|
assert.strictEqual('tiff', info.format);
|
||||||
assert(info.size === startSize);
|
assert.strictEqual(startSize, info.size);
|
||||||
rimraf(fixtures.outputTiff, done);
|
rimraf(fixtures.outputTiff, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const assertZoomifyTiles = function (directory, expectedTileSize, expectedLevels
|
|||||||
fs.readdirSync(path.join(directory, 'TileGroup0')).forEach(function (tile) {
|
fs.readdirSync(path.join(directory, 'TileGroup0')).forEach(function (tile) {
|
||||||
// Verify tile file name
|
// Verify tile file name
|
||||||
assert.ok(/^[0-9]+-[0-9]+-[0-9]+\.jpg$/.test(tile));
|
assert.ok(/^[0-9]+-[0-9]+-[0-9]+\.jpg$/.test(tile));
|
||||||
let level = parseInt(tile.split('-')[0]);
|
const level = parseInt(tile.split('-')[0]);
|
||||||
maxTileLevel = Math.max(maxTileLevel, level);
|
maxTileLevel = Math.max(maxTileLevel, level);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -246,6 +246,26 @@ describe('Tile', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Valid skipBlanks threshold values pass', function () {
|
||||||
|
[-1, 0, 255, 65535].forEach(function (skipBlanksThreshold) {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tile({
|
||||||
|
skipBlanks: skipBlanksThreshold
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('InvalidskipBlanks threshold values fail', function () {
|
||||||
|
['zoinks', -2, 65536].forEach(function (skipBlanksThreshold) {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tile({
|
||||||
|
skipBlanks: skipBlanksThreshold
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Deep Zoom layout', function (done) {
|
it('Deep Zoom layout', function (done) {
|
||||||
const directory = fixtures.path('output.dzi_files');
|
const directory = fixtures.path('output.dzi_files');
|
||||||
rimraf(directory, function () {
|
rimraf(directory, function () {
|
||||||
@@ -299,7 +319,7 @@ describe('Tile', function () {
|
|||||||
assert.strictEqual('undefined', typeof info.size);
|
assert.strictEqual('undefined', typeof info.size);
|
||||||
assertDeepZoomTiles(directory, 512, 13, done);
|
assertDeepZoomTiles(directory, 512, 13, done);
|
||||||
// Verifies tiles in 10th level are rotated
|
// Verifies tiles in 10th level are rotated
|
||||||
let tile = path.join(directory, '10', '0_1.jpeg');
|
const tile = path.join(directory, '10', '0_1.jpeg');
|
||||||
// verify that the width and height correspond to the rotated image
|
// verify that the width and height correspond to the rotated image
|
||||||
// expected are w=512 and h=170 for the 0_1.jpeg.
|
// expected are w=512 and h=170 for the 0_1.jpeg.
|
||||||
// if a 0 angle is supplied to the .tile function
|
// if a 0 angle is supplied to the .tile function
|
||||||
@@ -364,6 +384,25 @@ describe('Tile', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Deep Zoom layout with skipBlanks', function (done) {
|
||||||
|
const directory = fixtures.path('output.256_skip_blanks.dzi_files');
|
||||||
|
rimraf(directory, function () {
|
||||||
|
sharp(fixtures.inputJpgOverlayLayer2)
|
||||||
|
.tile({
|
||||||
|
size: 256,
|
||||||
|
skipBlanks: 0
|
||||||
|
})
|
||||||
|
.toFile(fixtures.path('output.256_skip_blanks.dzi'), function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
// assert them 0_0.jpeg doesn't exist because it's a white tile
|
||||||
|
const whiteTilePath = path.join(directory, '11', '0_0.jpeg');
|
||||||
|
assert.strictEqual(fs.existsSync(whiteTilePath), false, 'Tile should not exist');
|
||||||
|
// Verify only one depth generated
|
||||||
|
assertDeepZoomTiles(directory, 256, 12, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Zoomify layout', function (done) {
|
it('Zoomify layout', function (done) {
|
||||||
const directory = fixtures.path('output.zoomify.dzi');
|
const directory = fixtures.path('output.zoomify.dzi');
|
||||||
rimraf(directory, function () {
|
rimraf(directory, function () {
|
||||||
@@ -451,6 +490,30 @@ describe('Tile', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Zoomify layout with skip blanks', function (done) {
|
||||||
|
const directory = fixtures.path('output.zoomify.skipBlanks.dzi');
|
||||||
|
rimraf(directory, function () {
|
||||||
|
sharp(fixtures.inputJpgOverlayLayer2)
|
||||||
|
.tile({
|
||||||
|
size: 256,
|
||||||
|
layout: 'zoomify',
|
||||||
|
skipBlanks: 0
|
||||||
|
})
|
||||||
|
.toFile(directory, function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
// assert them 0_0.jpeg doesn't exist because it's a white tile
|
||||||
|
const whiteTilePath = path.join(directory, 'TileGroup0', '2-0-0.jpg');
|
||||||
|
assert.strictEqual(fs.existsSync(whiteTilePath), false, 'Tile should not exist');
|
||||||
|
assert.strictEqual('dz', info.format);
|
||||||
|
assert.strictEqual(2048, info.width);
|
||||||
|
assert.strictEqual(1536, info.height);
|
||||||
|
assert.strictEqual(3, info.channels);
|
||||||
|
assert.strictEqual('number', typeof info.size);
|
||||||
|
assertZoomifyTiles(directory, 256, 4, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Google layout', function (done) {
|
it('Google layout', function (done) {
|
||||||
const directory = fixtures.path('output.google.dzi');
|
const directory = fixtures.path('output.google.dzi');
|
||||||
rimraf(directory, function () {
|
rimraf(directory, function () {
|
||||||
@@ -652,6 +715,31 @@ describe('Tile', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Google layout with default skip Blanks', function (done) {
|
||||||
|
const directory = fixtures.path('output.google_depth_skipBlanks.dzi');
|
||||||
|
rimraf(directory, function () {
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.tile({
|
||||||
|
layout: 'google',
|
||||||
|
size: 256
|
||||||
|
})
|
||||||
|
.toFile(directory, function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
const whiteTilePath = path.join(directory, '4', '8', '0.jpg');
|
||||||
|
assert.strictEqual(fs.existsSync(whiteTilePath), false, 'Tile should not exist');
|
||||||
|
|
||||||
|
assert.strictEqual('dz', info.format);
|
||||||
|
assert.strictEqual(2809, info.width);
|
||||||
|
assert.strictEqual(2074, info.height);
|
||||||
|
assert.strictEqual(3, info.channels);
|
||||||
|
assert.strictEqual('number', typeof info.size);
|
||||||
|
|
||||||
|
assertGoogleTiles(directory, 256, 5, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Write to ZIP container using file extension', function (done) {
|
it('Write to ZIP container using file extension', function (done) {
|
||||||
const container = fixtures.path('output.dz.container.zip');
|
const container = fixtures.path('output.dz.container.zip');
|
||||||
const extractTo = fixtures.path('output.dz.container');
|
const extractTo = fixtures.path('output.dz.container');
|
||||||
|
|||||||
19
test/unit/toBuffer.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('toBuffer', () => {
|
||||||
|
it('reusing same sharp object does not reset previously passed parameters to toBuffer', (done) => {
|
||||||
|
const image = sharp(fixtures.inputJpg);
|
||||||
|
image.toBuffer({ resolveWithObject: true }).then((obj) => {
|
||||||
|
image.toBuffer().then((buff) => {
|
||||||
|
assert.strict.equal(Buffer.isBuffer(buff), true);
|
||||||
|
assert.strict.equal(typeof obj, 'object');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -58,6 +58,27 @@ describe('Trim borders', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Attempt to trim 2x2 pixel image fails', function (done) {
|
||||||
|
sharp({
|
||||||
|
create: {
|
||||||
|
width: 2,
|
||||||
|
height: 2,
|
||||||
|
channels: 3,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.trim()
|
||||||
|
.toBuffer()
|
||||||
|
.then(() => {
|
||||||
|
done(new Error('Expected an error'));
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
assert.strictEqual('Image to trim must be at least 3x3 pixels', err.message);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
describe('Invalid thresholds', function () {
|
describe('Invalid thresholds', function () {
|
||||||
[-1, 'fail', {}].forEach(function (threshold) {
|
[-1, 'fail', {}].forEach(function (threshold) {
|
||||||
it(JSON.stringify(threshold), function () {
|
it(JSON.stringify(threshold), function () {
|
||||||
|
|||||||
@@ -118,6 +118,13 @@ describe('Utilities', function () {
|
|||||||
assert.strictEqual(true, sharp.format.raw[direction].stream);
|
assert.strictEqual(true, sharp.format.raw[direction].stream);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('vips format supports filesystem only', function () {
|
||||||
|
['input', 'output'].forEach(function (direction) {
|
||||||
|
assert.strictEqual(true, sharp.format.vips[direction].file);
|
||||||
|
assert.strictEqual(false, sharp.format.vips[direction].buffer);
|
||||||
|
assert.strictEqual(false, sharp.format.vips[direction].stream);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Versions', function () {
|
describe('Versions', function () {
|
||||||
|
|||||||