Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
978a788f40 | ||
|
|
6e91d55971 | ||
|
|
d4ce0a1e36 | ||
|
|
148608b377 | ||
|
|
f725f4acb7 | ||
|
|
d07a549438 | ||
|
|
551441cedd | ||
|
|
46c14e939b | ||
|
|
6084647795 | ||
|
|
e0a598ae62 | ||
|
|
28833eb04a | ||
|
|
b24c9c86d1 | ||
|
|
b7add480c7 | ||
|
|
eabb671b10 | ||
|
|
513ed02b76 | ||
|
|
b7ddbe71f7 | ||
|
|
21d1a7ca62 | ||
|
|
4c2d28a7ad | ||
|
|
2afec9e3ed | ||
|
|
69790421b7 | ||
|
|
3f08f6a359 | ||
|
|
719c2db8da | ||
|
|
a9aa55c32d | ||
|
|
3f6d9d6ee9 | ||
|
|
b32568159f | ||
|
|
bb48d0d857 | ||
|
|
536412515f | ||
|
|
fcc6eaadd3 | ||
|
|
7dc78e8796 | ||
|
|
c65de3fe6d | ||
|
|
d000f57773 | ||
|
|
75cddbdb6d | ||
|
|
e418d91511 | ||
|
|
6c2e6c5432 | ||
|
|
8c6d9fdc62 | ||
|
|
468e95427e | ||
|
|
6d7a5ace6b | ||
|
|
cbaec198a5 | ||
|
|
7467fa8b50 | ||
|
|
61640fb5c7 | ||
|
|
19cb4b62b0 | ||
|
|
81ee8bc30f | ||
|
|
9f384e1c6c | ||
|
|
35e8c8b25e | ||
|
|
dc53f1baff | ||
|
|
70139600b5 | ||
|
|
1b4d1521e0 | ||
|
|
ed3377cb2d | ||
|
|
d72852b3aa | ||
|
|
4b6b6189bf | ||
|
|
b69a54fc75 | ||
|
|
81e388a4cc | ||
|
|
5bd5e5052a | ||
|
|
a2d3fa729f | ||
|
|
cb6811bc47 | ||
|
|
53c6e80869 | ||
|
|
e71dca586c | ||
|
|
b3cd48db5f | ||
|
|
476448b9d4 | ||
|
|
070534df5b | ||
|
|
9a1e8ed574 | ||
|
|
309918a878 | ||
|
|
cac83b94c1 | ||
|
|
9c06df08a1 | ||
|
|
52e4543d31 | ||
|
|
a688468378 | ||
|
|
e1760d64fb | ||
|
|
84d4e3cf8f | ||
|
|
f8a76372ad | ||
|
|
4237f5520f | ||
|
|
8c0c01c702 | ||
|
|
9c100830e0 | ||
|
|
ed5d753b89 | ||
|
|
d1ca756bd8 | ||
|
|
16cf9f0ef2 | ||
|
|
133f69d66c | ||
|
|
bc60daff9e | ||
|
|
43a085d1ae | ||
|
|
8c33d0aa56 | ||
|
|
fe0767df13 | ||
|
|
08a25a0c8f | ||
|
|
cd410080bd |
77
.circleci/config.yml
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
version: 2.1
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
build:
|
||||||
|
jobs:
|
||||||
|
- linux-arm64-glibc-node-12:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^v.*/
|
||||||
|
- linux-arm64-musl-node-12:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^v.*/
|
||||||
|
- linux-arm64-glibc-node-16:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^v.*/
|
||||||
|
- linux-arm64-musl-node-16:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^v.*/
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux-arm64-glibc-node-12:
|
||||||
|
resource_class: arm.medium
|
||||||
|
machine:
|
||||||
|
image: ubuntu-2004:202101-01
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: |
|
||||||
|
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
||||||
|
sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl"
|
||||||
|
sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
||||||
|
sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_12.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
||||||
|
sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
||||||
|
- run: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
||||||
|
- run: sudo docker exec sharp sh -c "npm test"
|
||||||
|
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 5 --upload=$prebuild_upload\" || true"
|
||||||
|
linux-arm64-glibc-node-16:
|
||||||
|
resource_class: arm.medium
|
||||||
|
machine:
|
||||||
|
image: ubuntu-2004:202101-01
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: |
|
||||||
|
sudo chown 0.0 ${PWD}
|
||||||
|
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
||||||
|
sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl"
|
||||||
|
sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
||||||
|
sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_16.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
||||||
|
sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
||||||
|
- run: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
||||||
|
- run: sudo docker exec sharp sh -c "npm test"
|
||||||
|
linux-arm64-musl-node-12:
|
||||||
|
resource_class: arm.medium
|
||||||
|
machine:
|
||||||
|
image: ubuntu-2004:202101-01
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: |
|
||||||
|
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12-alpine3.11
|
||||||
|
sudo docker exec sharp sh -c "apk add build-base git python3 --update-cache"
|
||||||
|
- run: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
||||||
|
- run: sudo docker exec sharp sh -c "npm test"
|
||||||
|
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 5 --upload=$prebuild_upload\" || true"
|
||||||
|
linux-arm64-musl-node-16:
|
||||||
|
resource_class: arm.medium
|
||||||
|
machine:
|
||||||
|
image: ubuntu-2004:202101-01
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run: |
|
||||||
|
sudo chown 0.0 ${PWD}
|
||||||
|
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:16-alpine3.11
|
||||||
|
sudo docker exec sharp sh -c "apk add build-base git python3 --update-cache"
|
||||||
|
- run: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
||||||
|
- run: sudo docker exec sharp sh -c "npm test"
|
||||||
@@ -5,6 +5,7 @@ task:
|
|||||||
name: FreeBSD 13.0
|
name: FreeBSD 13.0
|
||||||
env:
|
env:
|
||||||
IGNORE_OSVERSION: yes
|
IGNORE_OSVERSION: yes
|
||||||
|
skip_notifications: true
|
||||||
prerequisites_script:
|
prerequisites_script:
|
||||||
- pkg update -f
|
- pkg update -f
|
||||||
- pkg upgrade -y
|
- pkg upgrade -y
|
||||||
|
|||||||
27
.github/workflows/ci-darwin-arm64v8.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: CI (MacStadium)
|
||||||
|
on:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
jobs:
|
||||||
|
CI:
|
||||||
|
runs-on: macos-m1
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: /usr/bin/arch -arch arm64e /bin/bash -l {0}
|
||||||
|
steps:
|
||||||
|
- name: Dependencies
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
architecture: arm64
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Install
|
||||||
|
run: npm install --build-from-source --unsafe-perm
|
||||||
|
- name: Test
|
||||||
|
run: npm test
|
||||||
|
- name: Prebuild
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
env:
|
||||||
|
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: npx prebuild --runtime napi --target 5
|
||||||
32
.github/workflows/ci.yml
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
name: CI (GitHub)
|
||||||
on:
|
on:
|
||||||
- push
|
- push
|
||||||
- pull_request
|
- pull_request
|
||||||
@@ -11,53 +12,46 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
container: centos:7
|
container: centos:7
|
||||||
nodejs_version: 10
|
nodejs_version: 12
|
||||||
coverage: true
|
coverage: true
|
||||||
prebuild: true
|
prebuild: true
|
||||||
- os: ubuntu-20.04
|
|
||||||
container: centos:7
|
|
||||||
nodejs_version: 12
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
container: centos:7
|
container: centos:7
|
||||||
nodejs_version: 14
|
nodejs_version: 14
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
container: centos:7
|
container: centos:7
|
||||||
nodejs_version: 15
|
nodejs_version: 16
|
||||||
- os: ubuntu-20.04
|
|
||||||
container: node:10-alpine3.11
|
|
||||||
prebuild: true
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
container: node:12-alpine3.11
|
container: node:12-alpine3.11
|
||||||
|
prebuild: true
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
container: node:14-alpine3.11
|
container: node:14-alpine3.11
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
container: node:14-alpine3.13
|
container: node:14-alpine3.13
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
container: node:15-alpine3.11
|
container: node:16-alpine3.11
|
||||||
- os: macos-10.15
|
|
||||||
nodejs_version: 10
|
|
||||||
prebuild: true
|
|
||||||
- os: macos-10.15
|
- os: macos-10.15
|
||||||
nodejs_version: 12
|
nodejs_version: 12
|
||||||
|
prebuild: true
|
||||||
- os: macos-10.15
|
- os: macos-10.15
|
||||||
nodejs_version: 14
|
nodejs_version: 14
|
||||||
- os: macos-10.15
|
- os: macos-10.15
|
||||||
nodejs_version: 15
|
nodejs_version: 16
|
||||||
- os: windows-2019
|
|
||||||
nodejs_version: 10
|
|
||||||
prebuild: true
|
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
nodejs_version: 12
|
nodejs_version: 12
|
||||||
|
prebuild: true
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
nodejs_version: 14
|
nodejs_version: 14
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
nodejs_version: 15
|
nodejs_version: 16
|
||||||
steps:
|
steps:
|
||||||
- name: Dependencies (Linux glibc)
|
- name: Dependencies (Linux glibc)
|
||||||
if: contains(matrix.container, 'centos')
|
if: contains(matrix.container, 'centos')
|
||||||
run: |
|
run: |
|
||||||
curl -sL https://rpm.nodesource.com/setup_${{ matrix.nodejs_version }}.x | bash -
|
curl -sL https://rpm.nodesource.com/setup_${{ matrix.nodejs_version }}.x | bash -
|
||||||
yum install -y gcc-c++ make git python3 nodejs
|
yum install -y centos-release-scl
|
||||||
|
yum install -y devtoolset-10-gcc-c++ make git python3 nodejs
|
||||||
|
echo "/opt/rh/devtoolset-10/root/usr/bin" >> $GITHUB_PATH
|
||||||
- name: Dependencies (Linux musl)
|
- name: Dependencies (Linux musl)
|
||||||
if: contains(matrix.container, 'alpine')
|
if: contains(matrix.container, 'alpine')
|
||||||
run: apk add build-base git python3 --update-cache
|
run: apk add build-base git python3 --update-cache
|
||||||
@@ -84,4 +78,4 @@ jobs:
|
|||||||
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
|
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
|
||||||
env:
|
env:
|
||||||
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
|
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: npx prebuild --runtime napi --target 3
|
run: npx prebuild --runtime napi --target 5
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"include-regex": "(sharp\\.node|libvips-cpp\\.dll)",
|
"include-regex": "(sharp-.+\\.node|libvips-cpp\\.dll)",
|
||||||
"strip": true
|
"strip": true
|
||||||
}
|
}
|
||||||
|
|||||||
108
.travis.yml
@@ -1,108 +0,0 @@
|
|||||||
jobs:
|
|
||||||
include:
|
|
||||||
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 10"
|
|
||||||
arch: arm64
|
|
||||||
os: linux
|
|
||||||
dist: bionic
|
|
||||||
language: shell
|
|
||||||
before_install:
|
|
||||||
- sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl"
|
|
||||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
|
||||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_10.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs=10.*"
|
|
||||||
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
|
||||||
after_success: "[[ -n $TRAVIS_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 3\""
|
|
||||||
|
|
||||||
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 12"
|
|
||||||
arch: arm64
|
|
||||||
os: linux
|
|
||||||
dist: bionic
|
|
||||||
language: shell
|
|
||||||
before_install:
|
|
||||||
- sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl"
|
|
||||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
|
||||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_12.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
|
||||||
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
|
||||||
|
|
||||||
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 14"
|
|
||||||
arch: arm64
|
|
||||||
os: linux
|
|
||||||
dist: bionic
|
|
||||||
language: shell
|
|
||||||
before_install:
|
|
||||||
- sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl"
|
|
||||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
|
||||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_14.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
|
||||||
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
|
||||||
|
|
||||||
- name: "Linux ARM64v8 (Debian 11, glibc 2.29) - Node.js 15"
|
|
||||||
arch: arm64
|
|
||||||
os: linux
|
|
||||||
dist: bionic
|
|
||||||
language: shell
|
|
||||||
before_install:
|
|
||||||
- sudo chown 0.0 ${PWD}
|
|
||||||
- sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl"
|
|
||||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
|
||||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_15.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
|
||||||
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
|
||||||
|
|
||||||
- name: "Linux ARM64v8 (Alpine 3.11, musl 1.1.24) - Node.js 10"
|
|
||||||
arch: arm64
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
language: shell
|
|
||||||
before_install:
|
|
||||||
- sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:10-alpine3.11
|
|
||||||
- sudo docker exec sharp sh -c "apk add build-base git python3 --update-cache"
|
|
||||||
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
|
||||||
after_success: "[[ -n $TRAVIS_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 3\""
|
|
||||||
|
|
||||||
- name: "Linux ARM64v8 (Alpine 3.11, musl 1.1.24) - Node.js 12"
|
|
||||||
arch: arm64
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
language: shell
|
|
||||||
before_install:
|
|
||||||
- sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12-alpine3.11
|
|
||||||
- sudo docker exec sharp sh -c "apk add build-base git python3 --update-cache"
|
|
||||||
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
|
||||||
|
|
||||||
- name: "Linux ARM64v8 (Alpine 3.11, musl 1.1.24) - Node.js 14"
|
|
||||||
arch: arm64
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
language: shell
|
|
||||||
before_install:
|
|
||||||
- sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:14-alpine3.11
|
|
||||||
- sudo docker exec sharp sh -c "apk add build-base git python3 --update-cache"
|
|
||||||
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
|
||||||
|
|
||||||
- name: "Linux ARM64v8 (Alpine 3.11, musl 1.1.24) - Node.js 15"
|
|
||||||
arch: arm64
|
|
||||||
os: linux
|
|
||||||
dist: focal
|
|
||||||
language: shell
|
|
||||||
before_install:
|
|
||||||
- sudo chown 0.0 ${PWD}
|
|
||||||
- sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:15-alpine3.11
|
|
||||||
- sudo docker exec sharp sh -c "apk add build-base git python3 --update-cache"
|
|
||||||
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
|
||||||
|
|
||||||
cache:
|
|
||||||
npm: false
|
|
||||||
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
|
|||||||
As well as image resizing, operations such as
|
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 macOS, Windows and Linux systems running Node.js v10+
|
Most modern macOS, Windows and Linux systems running Node.js >= 12.13.0
|
||||||
do not require any additional install or runtime dependencies.
|
do not require any additional install or runtime dependencies.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
@@ -99,7 +99,7 @@ A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/C
|
|||||||
covers reporting bugs, requesting features and submitting code changes.
|
covers reporting bugs, requesting features and submitting code changes.
|
||||||
|
|
||||||
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
||||||
[](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
|
[](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
|
||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ build: off
|
|||||||
platform: x86
|
platform: x86
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- nodejs_version: "10"
|
|
||||||
prebuild: true
|
|
||||||
- nodejs_version: "12"
|
- nodejs_version: "12"
|
||||||
|
prebuild: true
|
||||||
- nodejs_version: "14"
|
- nodejs_version: "14"
|
||||||
- nodejs_version: "15"
|
- nodejs_version: "16"
|
||||||
install:
|
install:
|
||||||
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)
|
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)
|
||||||
- npm install --build-from-source
|
- npm install --build-from-source
|
||||||
test_script:
|
test_script:
|
||||||
- npm test
|
- npm test
|
||||||
on_success:
|
on_success:
|
||||||
- if [%prebuild%] == [true] if [%APPVEYOR_REPO_TAG%] == [true] npx prebuild --runtime napi --target 3
|
- if [%prebuild%] == [true] if [%APPVEYOR_REPO_TAG%] == [true] npx prebuild --runtime napi --target 5
|
||||||
|
|||||||
21
binding.gyp
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
'variables': {
|
'variables': {
|
||||||
'vips_version': '<!(node -p "require(\'./lib/libvips\').minimumLibvipsVersion")',
|
'vips_version': '<!(node -p "require(\'./lib/libvips\').minimumLibvipsVersion")',
|
||||||
'sharp_vendor_dir': '<(module_root_dir)/vendor/<(vips_version)'
|
'platform_and_arch': '<!(node -p "require(\'./lib/platform\')()")',
|
||||||
|
'sharp_vendor_dir': './vendor/<(vips_version)/<(platform_and_arch)'
|
||||||
},
|
},
|
||||||
'targets': [{
|
'targets': [{
|
||||||
'target_name': 'libvips-cpp',
|
'target_name': 'libvips-cpp',
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
'msvs_settings': {
|
'msvs_settings': {
|
||||||
'VCCLCompilerTool': {
|
'VCCLCompilerTool': {
|
||||||
'ExceptionHandling': 1,
|
'ExceptionHandling': 1,
|
||||||
|
'Optimization': 1,
|
||||||
'WholeProgramOptimization': 'true'
|
'WholeProgramOptimization': 'true'
|
||||||
},
|
},
|
||||||
'VCLibrarianTool': {
|
'VCLibrarianTool': {
|
||||||
@@ -65,9 +67,9 @@
|
|||||||
}]
|
}]
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
'target_name': 'sharp',
|
'target_name': 'sharp-<(platform_and_arch)',
|
||||||
'defines': [
|
'defines': [
|
||||||
'NAPI_VERSION=3'
|
'NAPI_VERSION=5'
|
||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'<!(node -p "require(\'node-addon-api\').gyp")',
|
'<!(node -p "require(\'node-addon-api\').gyp")',
|
||||||
@@ -138,7 +140,7 @@
|
|||||||
}],
|
}],
|
||||||
['OS == "mac"', {
|
['OS == "mac"', {
|
||||||
'link_settings': {
|
'link_settings': {
|
||||||
'library_dirs': ['<(sharp_vendor_dir)/lib'],
|
'library_dirs': ['../<(sharp_vendor_dir)/lib'],
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'libvips-cpp.42.dylib'
|
'libvips-cpp.42.dylib'
|
||||||
]
|
]
|
||||||
@@ -146,7 +148,7 @@
|
|||||||
'xcode_settings': {
|
'xcode_settings': {
|
||||||
'OTHER_LDFLAGS': [
|
'OTHER_LDFLAGS': [
|
||||||
# Ensure runtime linking is relative to sharp.node
|
# Ensure runtime linking is relative to sharp.node
|
||||||
'-Wl,-rpath,\'@loader_path/../../vendor/<(vips_version)/lib\''
|
'-Wl,-rpath,\'@loader_path/../../<(sharp_vendor_dir)/lib\''
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
@@ -155,13 +157,13 @@
|
|||||||
'_GLIBCXX_USE_CXX11_ABI=1'
|
'_GLIBCXX_USE_CXX11_ABI=1'
|
||||||
],
|
],
|
||||||
'link_settings': {
|
'link_settings': {
|
||||||
'library_dirs': ['<(sharp_vendor_dir)/lib'],
|
'library_dirs': ['../<(sharp_vendor_dir)/lib'],
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'-l:libvips-cpp.so.42'
|
'-l:libvips-cpp.so.42'
|
||||||
],
|
],
|
||||||
'ldflags': [
|
'ldflags': [
|
||||||
# Ensure runtime linking is relative to sharp.node
|
# Ensure runtime linking is relative to sharp.node
|
||||||
'-Wl,-s -Wl,--disable-new-dtags -Wl,-rpath=\'$$ORIGIN/../../vendor/<(vips_version)/lib\''
|
'-Wl,-s -Wl,--disable-new-dtags -Wl,-rpath=\'$$ORIGIN/../../<(sharp_vendor_dir)/lib\''
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@@ -172,7 +174,7 @@
|
|||||||
'-std=c++0x',
|
'-std=c++0x',
|
||||||
'-fexceptions',
|
'-fexceptions',
|
||||||
'-Wall',
|
'-Wall',
|
||||||
'-O3'
|
'-Os'
|
||||||
],
|
],
|
||||||
'xcode_settings': {
|
'xcode_settings': {
|
||||||
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
|
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
|
||||||
@@ -182,7 +184,7 @@
|
|||||||
'OTHER_CPLUSPLUSFLAGS': [
|
'OTHER_CPLUSPLUSFLAGS': [
|
||||||
'-fexceptions',
|
'-fexceptions',
|
||||||
'-Wall',
|
'-Wall',
|
||||||
'-O3'
|
'-Oz'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'configurations': {
|
'configurations': {
|
||||||
@@ -202,6 +204,7 @@
|
|||||||
'msvs_settings': {
|
'msvs_settings': {
|
||||||
'VCCLCompilerTool': {
|
'VCCLCompilerTool': {
|
||||||
'ExceptionHandling': 1,
|
'ExceptionHandling': 1,
|
||||||
|
'Optimization': 1,
|
||||||
'WholeProgramOptimization': 'true'
|
'WholeProgramOptimization': 'true'
|
||||||
},
|
},
|
||||||
'VCLibrarianTool': {
|
'VCLibrarianTool': {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
|
|||||||
As well as image resizing, operations such as
|
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 macOS, Windows and Linux systems running Node.js v10+
|
Most modern macOS, Windows and Linux systems running Node.js >= 12.13.0
|
||||||
do not require any additional install or runtime dependencies.
|
do not require any additional install or runtime dependencies.
|
||||||
|
|
||||||
### Formats
|
### Formats
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
|
Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
|
||||||
|
|
||||||
|
See also [flatten][1].
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@@ -18,23 +20,38 @@ Returns **Sharp**
|
|||||||
|
|
||||||
## ensureAlpha
|
## ensureAlpha
|
||||||
|
|
||||||
Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
|
Ensure the output image has an alpha transparency channel.
|
||||||
|
If missing, the added alpha channel will have the specified
|
||||||
|
transparency level, defaulting to fully-opaque (1).
|
||||||
|
This is a no-op if the image already has an alpha channel.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* `alpha` **[number][2]** alpha transparency level (0=fully-transparent, 1=fully-opaque) (optional, default `1`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp('rgb.jpg')
|
// rgba.png will be a 4 channel image with a fully-opaque alpha channel
|
||||||
|
await sharp('rgb.jpg')
|
||||||
.ensureAlpha()
|
.ensureAlpha()
|
||||||
.toFile('rgba.png', function(err, info) {
|
.toFile('rgba.png')
|
||||||
// rgba.png is a 4 channel image with a fully opaque alpha channel
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// rgba is a 4 channel image with a fully-transparent alpha channel
|
||||||
|
const rgba = await sharp(rgb)
|
||||||
|
.ensureAlpha(0)
|
||||||
|
.toBuffer();
|
||||||
|
```
|
||||||
|
|
||||||
|
* Throws **[Error][3]** Invalid alpha transparency level
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
**Meta**
|
**Meta**
|
||||||
|
|
||||||
- **since**: 0.21.2
|
* **since**: 0.21.2
|
||||||
|
|
||||||
## extractChannel
|
## extractChannel
|
||||||
|
|
||||||
@@ -42,21 +59,26 @@ Extract a single channel from a multi-channel image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `channel` **([number][1] \| [string][2])** zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`.
|
* `channel` **([number][2] | [string][4])** zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp(input)
|
// green.jpg is a greyscale image containing the green channel of the input
|
||||||
|
await sharp(input)
|
||||||
.extractChannel('green')
|
.extractChannel('green')
|
||||||
.toColourspace('b-w')
|
.toFile('green.jpg');
|
||||||
.toFile('green.jpg', function(err, info) {
|
|
||||||
// info.channels === 1
|
|
||||||
// green.jpg is a greyscale image containing the green channel of the input
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid channel
|
```javascript
|
||||||
|
// red1 is the red value of the first pixel, red2 the second pixel etc.
|
||||||
|
const [red1, red2, ...] = await sharp(input)
|
||||||
|
.extractChannel(0)
|
||||||
|
.raw()
|
||||||
|
.toBuffer();
|
||||||
|
```
|
||||||
|
|
||||||
|
* Throws **[Error][3]** Invalid channel
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -67,19 +89,20 @@ The meaning of the added channels depends on the output colourspace, set with `t
|
|||||||
By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
||||||
Channel ordering follows vips convention:
|
Channel ordering follows vips convention:
|
||||||
|
|
||||||
- sRGB: 0: Red, 1: Green, 2: Blue, 3: Alpha.
|
* sRGB: 0: Red, 1: Green, 2: Blue, 3: Alpha.
|
||||||
- CMYK: 0: Magenta, 1: Cyan, 2: Yellow, 3: Black, 4: Alpha.
|
* CMYK: 0: Magenta, 1: Cyan, 2: Yellow, 3: Black, 4: Alpha.
|
||||||
|
|
||||||
Buffers may be any of the image formats supported by sharp.
|
Buffers may be any of the image formats supported by sharp.
|
||||||
For raw pixel input, the `options` object should contain a `raw` attribute, which follows the format of the attribute of the same name in the `sharp()` constructor.
|
For raw pixel input, the `options` object should contain a `raw` attribute, which follows the format of the attribute of the same name in the `sharp()` constructor.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `images` **([Array][4]<([string][2] \| [Buffer][5])> | [string][2] \| [Buffer][5])** one or more images (file paths, Buffers).
|
* `images` **([Array][5]<([string][4] | [Buffer][6])> | [string][4] | [Buffer][6])** one or more images (file paths, Buffers).
|
||||||
- `options` **[Object][6]** image options, see `sharp()` constructor.
|
* `options` **[Object][7]** image options, see `sharp()` constructor.
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid parameters
|
* Throws **[Error][3]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -89,7 +112,7 @@ Perform a bitwise boolean operation on all input image channels (bands) to produ
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `boolOp` **[string][2]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
|
* `boolOp` **[string][4]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -103,18 +126,20 @@ sharp('3-channel-rgb-input.png')
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid parameters
|
* Throws **[Error][3]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
[1]: /api-operation#flatten
|
||||||
|
|
||||||
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||||
|
|
||||||
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||||
|
|
||||||
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||||
|
|
||||||
[5]: https://nodejs.org/api/buffer.html
|
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
||||||
|
|
||||||
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
[6]: https://nodejs.org/api/buffer.html
|
||||||
|
|
||||||
|
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ An alpha channel may be present and will be unchanged by the operation.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `rgb` **([string][1] \| [Object][2])** parsed by the [color][3] module to extract chroma values.
|
* `rgb` **([string][1] | [Object][2])** parsed by the [color][3] module to extract chroma values.
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid parameter
|
* Throws **[Error][4]** Invalid parameter
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ An alpha channel may be present, and will be unchanged by the operation.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `greyscale` **[Boolean][5]** (optional, default `true`)
|
* `greyscale` **[Boolean][5]** (optional, default `true`)
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -35,7 +36,52 @@ Alternative spelling of `greyscale`.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `grayscale` **[Boolean][5]** (optional, default `true`)
|
* `grayscale` **[Boolean][5]** (optional, default `true`)
|
||||||
|
|
||||||
|
Returns **Sharp**
|
||||||
|
|
||||||
|
## pipelineColourspace
|
||||||
|
|
||||||
|
Set the pipeline colourspace.
|
||||||
|
|
||||||
|
The input image will be converted to the provided colourspace at the start of the pipeline.
|
||||||
|
All operations will use this colourspace before converting to the output colourspace, as defined by [toColourspace][6].
|
||||||
|
|
||||||
|
This feature is experimental and has not yet been fully-tested with all operations.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* `colourspace` **[string][1]?** pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...][7]
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Run pipeline in 16 bits per channel RGB while converting final result to 8 bits per channel sRGB.
|
||||||
|
await sharp(input)
|
||||||
|
.pipelineColourspace('rgb16')
|
||||||
|
.toColourspace('srgb')
|
||||||
|
.toFile('16bpc-pipeline-to-8bpc-output.png')
|
||||||
|
```
|
||||||
|
|
||||||
|
* Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
|
Returns **Sharp**
|
||||||
|
|
||||||
|
**Meta**
|
||||||
|
|
||||||
|
* **since**: 0.29.0
|
||||||
|
|
||||||
|
## pipelineColorspace
|
||||||
|
|
||||||
|
Alternative spelling of `pipelineColourspace`.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* `colorspace` **[string][1]?** pipeline colorspace.
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
|
* Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -46,7 +92,7 @@ By default output image will be web-friendly sRGB, with additional channels inte
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `colourspace` **[string][1]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][6]
|
* `colourspace` **[string][1]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][8]
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -57,7 +103,7 @@ await sharp(input)
|
|||||||
.toFile('16-bpp.png')
|
.toFile('16-bpp.png')
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid parameters
|
* Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -67,10 +113,11 @@ Alternative spelling of `toColourspace`.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `colorspace` **[string][1]?** output colorspace.
|
* `colorspace` **[string][1]?** output colorspace.
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid parameters
|
* Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -84,4 +131,8 @@ Returns **Sharp**
|
|||||||
|
|
||||||
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||||
|
|
||||||
[6]: https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568
|
[6]: #tocolourspace
|
||||||
|
|
||||||
|
[7]: https://github.com/libvips/libvips/blob/41cff4e9d0838498487a00623462204eb10ee5b8/libvips/iofuncs/enumtypes.c#L774
|
||||||
|
|
||||||
|
[8]: https://github.com/libvips/libvips/blob/3c0bfdf74ce1dc37a6429bed47fa76f16e2cd70a/libvips/iofuncs/enumtypes.c#L777-L794
|
||||||
|
|||||||
@@ -19,24 +19,30 @@ and [https://www.cairographics.org/operators/][2]
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `images` **[Array][3]<[Object][4]>** Ordered list of images to composite
|
* `images` **[Array][3]<[Object][4]>** Ordered list of images to composite
|
||||||
- `images[].input` **([Buffer][5] \| [String][6])?** Buffer containing image data, String containing the path to an image file, or Create object (see below)
|
|
||||||
- `images[].input.create` **[Object][4]?** describes a blank overlay to be created.
|
* `images[].input` **([Buffer][5] | [String][6])?** Buffer containing image data, String containing the path to an image file, or Create object (see below)
|
||||||
- `images[].input.create.width` **[Number][7]?**
|
|
||||||
- `images[].input.create.height` **[Number][7]?**
|
* `images[].input.create` **[Object][4]?** describes a blank overlay to be created.
|
||||||
- `images[].input.create.channels` **[Number][7]?** 3-4
|
|
||||||
- `images[].input.create.background` **([String][6] \| [Object][4])?** parsed by the [color][8] module to extract values for red, green, blue and alpha.
|
* `images[].input.create.width` **[Number][7]?**
|
||||||
- `images[].blend` **[String][6]** how to blend this image with the image below. (optional, default `'over'`)
|
* `images[].input.create.height` **[Number][7]?**
|
||||||
- `images[].gravity` **[String][6]** gravity at which to place the overlay. (optional, default `'centre'`)
|
* `images[].input.create.channels` **[Number][7]?** 3-4
|
||||||
- `images[].top` **[Number][7]?** the pixel offset from the top edge.
|
* `images[].input.create.background` **([String][6] | [Object][4])?** parsed by the [color][8] module to extract values for red, green, blue and alpha.
|
||||||
- `images[].left` **[Number][7]?** the pixel offset from the left edge.
|
* `images[].blend` **[String][6]** how to blend this image with the image below. (optional, default `'over'`)
|
||||||
- `images[].tile` **[Boolean][9]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
|
* `images[].gravity` **[String][6]** gravity at which to place the overlay. (optional, default `'centre'`)
|
||||||
- `images[].premultiplied` **[Boolean][9]** set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option. (optional, default `false`)
|
* `images[].top` **[Number][7]?** the pixel offset from the top edge.
|
||||||
- `images[].density` **[Number][7]** number representing the DPI for vector overlay image. (optional, default `72`)
|
* `images[].left` **[Number][7]?** the pixel offset from the left edge.
|
||||||
- `images[].raw` **[Object][4]?** describes overlay when using raw pixel data.
|
* `images[].tile` **[Boolean][9]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
|
||||||
- `images[].raw.width` **[Number][7]?**
|
* `images[].premultiplied` **[Boolean][9]** set to true to avoid premultipling the image below. Equivalent to the `--premultiplied` vips option. (optional, default `false`)
|
||||||
- `images[].raw.height` **[Number][7]?**
|
* `images[].density` **[Number][7]** number representing the DPI for vector overlay image. (optional, default `72`)
|
||||||
- `images[].raw.channels` **[Number][7]?**
|
* `images[].raw` **[Object][4]?** describes overlay when using raw pixel data.
|
||||||
|
|
||||||
|
* `images[].raw.width` **[Number][7]?**
|
||||||
|
* `images[].raw.height` **[Number][7]?**
|
||||||
|
* `images[].raw.channels` **[Number][7]?**
|
||||||
|
* `images[].failOnError` **[boolean][9]** @see [constructor parameters][10] (optional, default `true`)
|
||||||
|
* `images[].limitInputPixels` **([number][7] | [boolean][9])** @see [constructor parameters][10] (optional, default `268402689`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -57,13 +63,13 @@ sharp('input.png')
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][10]** Invalid parameters
|
* Throws **[Error][11]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
**Meta**
|
**Meta**
|
||||||
|
|
||||||
- **since**: 0.22.0
|
* **since**: 0.22.0
|
||||||
|
|
||||||
[1]: https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
|
[1]: https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
|
||||||
|
|
||||||
@@ -83,4 +89,6 @@ Returns **Sharp**
|
|||||||
|
|
||||||
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||||
|
|
||||||
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
[10]: /api-constructor#parameters
|
||||||
|
|
||||||
|
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||||
|
|||||||
@@ -13,36 +13,44 @@ Implements the [stream.Duplex][1] class.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `input` **([Buffer][2] \| [Uint8Array][3] \| [Uint8ClampedArray][4] \| [string][5])?** if present, can be
|
* `input` **([Buffer][2] | [Uint8Array][3] | [Uint8ClampedArray][4] | [Int8Array][5] | [Uint16Array][6] | [Int16Array][7] | [Uint32Array][8] | [Int32Array][9] | [Float32Array][10] | [Float64Array][11] | [string][12])?** if present, can be
|
||||||
a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
|
||||||
a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
a TypedArray containing raw pixel image data, or
|
||||||
JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
||||||
- `options` **[Object][6]?** if present, is an Object with optional attributes.
|
JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||||
- `options.failOnError` **[boolean][7]** by default halt processing and raise an error when loading invalid images.
|
* `options` **[Object][13]?** if present, is an Object with optional attributes.
|
||||||
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
|
|
||||||
- `options.limitInputPixels` **([number][8] \| [boolean][7])** Do not process input images where the number of pixels
|
* `options.failOnError` **[boolean][14]** by default halt processing and raise an error when loading invalid images.
|
||||||
(width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
|
||||||
An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default `268402689`)
|
* `options.limitInputPixels` **([number][15] | [boolean][14])** Do not process input images where the number of pixels
|
||||||
- `options.sequentialRead` **[boolean][7]** Set this to `true` to use sequential rather than random access where possible.
|
(width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
||||||
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
|
An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default `268402689`)
|
||||||
- `options.density` **[number][8]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
|
* `options.sequentialRead` **[boolean][14]** Set this to `true` to use sequential rather than random access where possible.
|
||||||
- `options.pages` **[number][8]** number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
|
||||||
- `options.page` **[number][8]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
|
* `options.density` **[number][15]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
|
||||||
- `options.level` **[number][8]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
* `options.pages` **[number][15]** number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
||||||
- `options.animated` **[boolean][7]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
|
* `options.page` **[number][15]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
|
||||||
- `options.raw` **[Object][6]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
* `options.subifd` **[number][15]** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default `-1`)
|
||||||
- `options.raw.width` **[number][8]?** integral number of pixels wide.
|
* `options.level` **[number][15]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
||||||
- `options.raw.height` **[number][8]?** integral number of pixels high.
|
* `options.animated` **[boolean][14]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
|
||||||
- `options.raw.channels` **[number][8]?** integral number of channels, between 1 and 4.
|
* `options.raw` **[Object][13]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||||
- `options.create` **[Object][6]?** describes a new image to be created.
|
|
||||||
- `options.create.width` **[number][8]?** integral number of pixels wide.
|
* `options.raw.width` **[number][15]?** integral number of pixels wide.
|
||||||
- `options.create.height` **[number][8]?** integral number of pixels high.
|
* `options.raw.height` **[number][15]?** integral number of pixels high.
|
||||||
- `options.create.channels` **[number][8]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
|
* `options.raw.channels` **[number][15]?** integral number of channels, between 1 and 4.
|
||||||
- `options.create.background` **([string][5] \| [Object][6])?** parsed by the [color][9] module to extract values for red, green, blue and alpha.
|
* `options.raw.premultiplied` **[boolean][14]?** specifies that the raw input has already been premultiplied, set to `true`
|
||||||
- `options.create.noise` **[Object][6]?** describes a noise to be created.
|
to avoid sharp premultiplying the image. (optional, default `false`)
|
||||||
- `options.create.noise.type` **[string][5]?** type of generated noise, currently only `gaussian` is supported.
|
* `options.create` **[Object][13]?** describes a new image to be created.
|
||||||
- `options.create.noise.mean` **[number][8]?** mean of pixels in generated noise.
|
|
||||||
- `options.create.noise.sigma` **[number][8]?** standard deviation of pixels in generated noise.
|
* `options.create.width` **[number][15]?** integral number of pixels wide.
|
||||||
|
* `options.create.height` **[number][15]?** integral number of pixels high.
|
||||||
|
* `options.create.channels` **[number][15]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
|
||||||
|
* `options.create.background` **([string][12] | [Object][13])?** parsed by the [color][16] module to extract values for red, green, blue and alpha.
|
||||||
|
* `options.create.noise` **[Object][13]?** describes a noise to be created.
|
||||||
|
|
||||||
|
* `options.create.noise.type` **[string][12]?** type of generated noise, currently only `gaussian` is supported.
|
||||||
|
* `options.create.noise.mean` **[number][15]?** mean of pixels in generated noise.
|
||||||
|
* `options.create.noise.sigma` **[number][15]?** standard deviation of pixels in generated noise.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -119,9 +127,9 @@ await sharp({
|
|||||||
}).toFile('noise.png');
|
}).toFile('noise.png');
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][10]** Invalid parameters
|
* Throws **[Error][17]** Invalid parameters
|
||||||
|
|
||||||
Returns **[Sharp][11]**
|
Returns **[Sharp][18]**
|
||||||
|
|
||||||
## clone
|
## clone
|
||||||
|
|
||||||
@@ -189,7 +197,7 @@ Promise.all(promises)
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns **[Sharp][11]**
|
Returns **[Sharp][18]**
|
||||||
|
|
||||||
[1]: http://nodejs.org/api/stream.html#stream_class_stream_duplex
|
[1]: http://nodejs.org/api/stream.html#stream_class_stream_duplex
|
||||||
|
|
||||||
@@ -199,16 +207,30 @@ Returns **[Sharp][11]**
|
|||||||
|
|
||||||
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray
|
||||||
|
|
||||||
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int8Array
|
||||||
|
|
||||||
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array
|
||||||
|
|
||||||
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int16Array
|
||||||
|
|
||||||
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array
|
||||||
|
|
||||||
[9]: https://www.npmjs.org/package/color
|
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int32Array
|
||||||
|
|
||||||
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float32Array
|
||||||
|
|
||||||
[11]: #sharp
|
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float64Array
|
||||||
|
|
||||||
|
[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||||
|
|
||||||
|
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||||
|
|
||||||
|
[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||||
|
|
||||||
|
[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||||
|
|
||||||
|
[16]: https://www.npmjs.org/package/color
|
||||||
|
|
||||||
|
[17]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||||
|
|
||||||
|
[18]: #sharp
|
||||||
|
|||||||
@@ -5,34 +5,37 @@
|
|||||||
Fast access to (uncached) image metadata without decoding any compressed image data.
|
Fast access to (uncached) image metadata without decoding any compressed image data.
|
||||||
A `Promise` is returned when `callback` is not provided.
|
A `Promise` is returned when `callback` is not provided.
|
||||||
|
|
||||||
- `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
|
* `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
|
||||||
- `size`: Total size of image in bytes, for Stream and Buffer input only
|
* `size`: Total size of image in bytes, for Stream and Buffer input only
|
||||||
- `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
|
* `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
|
||||||
- `height`: Number of pixels high (EXIF orientation is not taken into consideration)
|
* `height`: Number of pixels high (EXIF orientation is not taken into consideration)
|
||||||
- `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][1]
|
* `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][1]
|
||||||
- `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
* `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
||||||
- `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...][2]
|
* `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...][2]
|
||||||
- `density`: Number of pixels per inch (DPI), if present
|
* `density`: Number of pixels per inch (DPI), if present
|
||||||
- `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, HEIF, 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 a multi-page image will be.
|
* `pageHeight`: Number of pixels high each page in a multi-page image will be.
|
||||||
- `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
* `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
||||||
- `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
* `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
||||||
- `pagePrimary`: Number of the primary page in a HEIF image
|
* `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
- `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
|
* `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
|
||||||
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
* `subifds`: Number of Sub Image File Directories in an OME-TIFF image
|
||||||
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
* `background`: Default background colour, if present, for PNG (bKGD) and GIF images, either an RGB Object or a single greyscale value
|
||||||
- `orientation`: Number value of the EXIF Orientation header, if present
|
* `compression`: The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC)
|
||||||
- `exif`: Buffer containing raw EXIF data, if present
|
* `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
||||||
- `icc`: Buffer containing raw [ICC][3] profile data, if present
|
* `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||||
- `iptc`: Buffer containing raw IPTC data, if present
|
* `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
- `xmp`: Buffer containing raw XMP data, if present
|
* `exif`: Buffer containing raw EXIF data, if present
|
||||||
- `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
* `icc`: Buffer containing raw [ICC][3] profile data, if present
|
||||||
|
* `iptc`: Buffer containing raw IPTC data, if present
|
||||||
|
* `xmp`: Buffer containing raw XMP data, if present
|
||||||
|
* `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `callback` **[Function][4]?** called with the arguments `(err, metadata)`
|
* `callback` **[Function][4]?** called with the arguments `(err, metadata)`
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -51,32 +54,35 @@ image
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns **([Promise][5]<[Object][6]> | Sharp)**
|
Returns **([Promise][5]<[Object][6]> | Sharp)**
|
||||||
|
|
||||||
## stats
|
## stats
|
||||||
|
|
||||||
Access to pixel-derived image statistics for every channel in the image.
|
Access to pixel-derived image statistics for every channel in the image.
|
||||||
A `Promise` is returned when `callback` is not provided.
|
A `Promise` is returned when `callback` is not provided.
|
||||||
|
|
||||||
- `channels`: Array of channel statistics for each channel in the image. Each channel statistic contains
|
* `channels`: Array of channel statistics for each channel in the image. Each channel statistic contains
|
||||||
- `min` (minimum value in the channel)
|
* `min` (minimum value in the channel)
|
||||||
- `max` (maximum value in the channel)
|
* `max` (maximum value in the channel)
|
||||||
- `sum` (sum of all values in a channel)
|
* `sum` (sum of all values in a channel)
|
||||||
- `squaresSum` (sum of squared values in a channel)
|
* `squaresSum` (sum of squared values in a channel)
|
||||||
- `mean` (mean of the values in a channel)
|
* `mean` (mean of the values in a channel)
|
||||||
- `stdev` (standard deviation for the values in a channel)
|
* `stdev` (standard deviation for the values in a channel)
|
||||||
- `minX` (x-coordinate of one of the pixel where the minimum lies)
|
* `minX` (x-coordinate of one of the pixel where the minimum lies)
|
||||||
- `minY` (y-coordinate of one of the pixel where the minimum lies)
|
* `minY` (y-coordinate of one of the pixel where the minimum lies)
|
||||||
- `maxX` (x-coordinate of one of the pixel where the maximum lies)
|
* `maxX` (x-coordinate of one of the pixel where the maximum lies)
|
||||||
- `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
* `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
||||||
- `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
|
* `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
|
||||||
- `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
* `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
||||||
- `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
|
* `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
|
||||||
- `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
|
* `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
|
||||||
|
|
||||||
|
**Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
|
||||||
|
written to a buffer in order to run `stats` on the result (see third example).
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `callback` **[Function][4]?** called with the arguments `(err, stats)`
|
* `callback` **[Function][4]?** called with the arguments `(err, stats)`
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -94,7 +100,15 @@ const { entropy, sharpness, dominant } = await sharp(input).stats();
|
|||||||
const { r, g, b } = dominant;
|
const { r, g, b } = dominant;
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns **[Promise][5]<[Object][6]>**
|
```javascript
|
||||||
|
const image = sharp(input);
|
||||||
|
// store intermediate result
|
||||||
|
const part = await image.extract(region).toBuffer();
|
||||||
|
// create new instance to obtain statistics of extracted region
|
||||||
|
const stats = await sharp(part).stats();
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns **[Promise][5]<[Object][6]>**
|
||||||
|
|
||||||
[1]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation
|
[1]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,10 @@ for example `rotate(x).extract(y)` will produce a different result to `extract(y
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `angle` **[number][1]** angle of rotation. (optional, default `auto`)
|
* `angle` **[number][1]** angle of rotation. (optional, default `auto`)
|
||||||
- `options` **[Object][2]?** if present, is an Object with optional attributes.
|
* `options` **[Object][2]?** if present, is an Object with optional attributes.
|
||||||
- `options.background` **([string][3] \| [Object][2])** parsed by the [color][4] module to extract values for red, green, blue and alpha. (optional, default `"#000000"`)
|
|
||||||
|
* `options.background` **([string][3] | [Object][2])** parsed by the [color][4] module to extract values for red, green, blue and alpha. (optional, default `"#000000"`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ const pipeline = sharp()
|
|||||||
readableStream.pipe(pipeline);
|
readableStream.pipe(pipeline);
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `flip` **[Boolean][6]** (optional, default `true`)
|
* `flip` **[Boolean][6]** (optional, default `true`)
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `flop` **[Boolean][6]** (optional, default `true`)
|
* `flop` **[Boolean][6]** (optional, default `true`)
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -75,25 +76,26 @@ A particular interpolator may also be specified. Set the `interpolator` option t
|
|||||||
|
|
||||||
In the case of a 2x2 matrix, the transform is:
|
In the case of a 2x2 matrix, the transform is:
|
||||||
|
|
||||||
- X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
|
* X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
|
||||||
- Y = `matrix[1, 0]` \* (x + `idx`) + `matrix[1, 1]` \* (y + `idy`) + `ody`
|
* Y = `matrix[1, 0]` \* (x + `idx`) + `matrix[1, 1]` \* (y + `idy`) + `ody`
|
||||||
|
|
||||||
where:
|
where:
|
||||||
|
|
||||||
- x and y are the coordinates in input image.
|
* x and y are the coordinates in input image.
|
||||||
- X and Y are the coordinates in output image.
|
* X and Y are the coordinates in output image.
|
||||||
- (0,0) is the upper left corner.
|
* (0,0) is the upper left corner.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `matrix` **([Array][7]<[Array][7]<[number][1]>> | [Array][7]<[number][1]>)** affine transformation matrix
|
* `matrix` **([Array][7]<[Array][7]<[number][1]>> | [Array][7]<[number][1]>)** affine transformation matrix
|
||||||
- `options` **[Object][2]?** if present, is an Object with optional attributes.
|
* `options` **[Object][2]?** if present, is an Object with optional attributes.
|
||||||
- `options.background` **([String][3] \| [Object][2])** parsed by the [color][4] module to extract values for red, green, blue and alpha. (optional, default `"#000000"`)
|
|
||||||
- `options.idx` **[Number][1]** input horizontal offset (optional, default `0`)
|
* `options.background` **([String][3] | [Object][2])** parsed by the [color][4] module to extract values for red, green, blue and alpha. (optional, default `"#000000"`)
|
||||||
- `options.idy` **[Number][1]** input vertical offset (optional, default `0`)
|
* `options.idx` **[Number][1]** input horizontal offset (optional, default `0`)
|
||||||
- `options.odx` **[Number][1]** output horizontal offset (optional, default `0`)
|
* `options.idy` **[Number][1]** input vertical offset (optional, default `0`)
|
||||||
- `options.ody` **[Number][1]** output vertical offset (optional, default `0`)
|
* `options.odx` **[Number][1]** output horizontal offset (optional, default `0`)
|
||||||
- `options.interpolator` **[String][3]** interpolator (optional, default `sharp.interpolators.bicubic`)
|
* `options.ody` **[Number][1]** output vertical offset (optional, default `0`)
|
||||||
|
* `options.interpolator` **[String][3]** interpolator (optional, default `sharp.interpolators.bicubic`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -112,13 +114,13 @@ inputStream
|
|||||||
.pipe(pipeline);
|
.pipe(pipeline);
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
**Meta**
|
**Meta**
|
||||||
|
|
||||||
- **since**: 0.27.0
|
* **since**: 0.27.0
|
||||||
|
|
||||||
## sharpen
|
## sharpen
|
||||||
|
|
||||||
@@ -129,12 +131,13 @@ Separate control over the level of sharpening in "flat" and "jagged" areas is av
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `sigma` **[number][1]?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
* `sigma` **[number][1]?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||||
- `flat` **[number][1]** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
|
* `flat` **[number][1]** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
|
||||||
- `jagged` **[number][1]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
|
* `jagged` **[number][1]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -145,10 +148,11 @@ When used without parameters the default window is 3x3.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `size` **[number][1]** square mask size: size x size (optional, default `3`)
|
* `size` **[number][1]** square mask size: size x size (optional, default `3`)
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -160,10 +164,11 @@ When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `sigma` **[number][1]?** a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
* `sigma` **[number][1]?** a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -171,16 +176,19 @@ Returns **Sharp**
|
|||||||
|
|
||||||
Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
||||||
|
|
||||||
|
See also [removeAlpha][8].
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][2]?**
|
* `options` **[Object][2]?**
|
||||||
- `options.background` **([string][3] \| [Object][2])** background colour, parsed by the [color][4] module, defaults to black. (optional, default `{r:0,g:0,b:0}`)
|
|
||||||
|
* `options.background` **([string][3] | [Object][2])** background colour, parsed by the [color][4] module, defaults to black. (optional, default `{r:0,g:0,b:0}`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
await sharp(rgbaInput)
|
await sharp(rgbaInput)
|
||||||
.flatten('#F0A703')
|
.flatten({ background: '#F0A703' })
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -198,11 +206,12 @@ Supply a second argument to use a different output gamma value, otherwise the fi
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `gamma` **[number][1]** value between 1.0 and 3.0. (optional, default `2.2`)
|
* `gamma` **[number][1]** value between 1.0 and 3.0. (optional, default `2.2`)
|
||||||
- `gammaOut` **[number][1]?** value between 1.0 and 3.0. (optional, defaults to same as `gamma`)
|
* `gammaOut` **[number][1]?** value between 1.0 and 3.0. (optional, defaults to same as `gamma`)
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -212,7 +221,9 @@ Produce the "negative" of the image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `negate` **[Boolean][6]** (optional, default `true`)
|
* `options` **[Object][2]?**
|
||||||
|
|
||||||
|
* `options.alpha` **[Boolean][6]** Whether or not to negate any alpha channel (optional, default `true`)
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -222,7 +233,7 @@ Enhance output image contrast by stretching its luminance to cover the full dyna
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `normalise` **[Boolean][6]** (optional, default `true`)
|
* `normalise` **[Boolean][6]** (optional, default `true`)
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -232,22 +243,50 @@ Alternative spelling of normalise.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `normalize` **[Boolean][6]** (optional, default `true`)
|
* `normalize` **[Boolean][6]** (optional, default `true`)
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
|
## clahe
|
||||||
|
|
||||||
|
Perform contrast limiting adaptive histogram equalization
|
||||||
|
[CLAHE][9].
|
||||||
|
|
||||||
|
This will, in general, enhance the clarity of the image by bringing out darker details.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* `options` **[Object][2]**
|
||||||
|
|
||||||
|
* `options.width` **[number][1]** integer width of the region in pixels.
|
||||||
|
* `options.height` **[number][1]** integer height of the region in pixels.
|
||||||
|
* `options.maxSlope` **[number][1]** maximum value for the slope of the
|
||||||
|
cumulative histogram. A value of 0 disables contrast limiting. Valid values
|
||||||
|
are integers in the range 0-100 (inclusive) (optional, default `3`)
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
|
Returns **Sharp**
|
||||||
|
|
||||||
|
**Meta**
|
||||||
|
|
||||||
|
* **since**: 0.28.3
|
||||||
|
|
||||||
## convolve
|
## convolve
|
||||||
|
|
||||||
Convolve the image with the specified kernel.
|
Convolve the image with the specified kernel.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `kernel` **[Object][2]**
|
* `kernel` **[Object][2]**
|
||||||
- `kernel.width` **[number][1]** width of the kernel in pixels.
|
|
||||||
- `kernel.height` **[number][1]** width of the kernel in pixels.
|
* `kernel.width` **[number][1]** width of the kernel in pixels.
|
||||||
- `kernel.kernel` **[Array][7]<[number][1]>** Array of length `width*height` containing the kernel values.
|
* `kernel.height` **[number][1]** height of the kernel in pixels.
|
||||||
- `kernel.scale` **[number][1]** the scale of the kernel in pixels. (optional, default `sum`)
|
* `kernel.kernel` **[Array][7]<[number][1]>** Array of length `width*height` containing the kernel values.
|
||||||
- `kernel.offset` **[number][1]** the offset of the kernel in pixels. (optional, default `0`)
|
* `kernel.scale` **[number][1]** the scale of the kernel in pixels. (optional, default `sum`)
|
||||||
|
* `kernel.offset` **[number][1]** the offset of the kernel in pixels. (optional, default `0`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -265,7 +304,7 @@ sharp(input)
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -275,13 +314,15 @@ Any pixel value greater than or equal to the threshold value will be set to 255,
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `threshold` **[number][1]** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`)
|
* `threshold` **[number][1]** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`)
|
||||||
- `options` **[Object][2]?**
|
* `options` **[Object][2]?**
|
||||||
- `options.greyscale` **[Boolean][6]** convert to single channel greyscale. (optional, default `true`)
|
|
||||||
- `options.grayscale` **[Boolean][6]** alternative spelling for greyscale. (optional, default `true`)
|
|
||||||
|
|
||||||
|
* `options.greyscale` **[Boolean][6]** convert to single channel greyscale. (optional, default `true`)
|
||||||
|
* `options.grayscale` **[Boolean][6]** alternative spelling for greyscale. (optional, default `true`)
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
<!---->
|
||||||
|
|
||||||
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -294,16 +335,19 @@ the selected bitwise boolean `operation` between the corresponding pixels of the
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `operand` **([Buffer][8] \| [string][3])** Buffer containing image data or string containing the path to an image file.
|
* `operand` **([Buffer][10] | [string][3])** Buffer containing image data or string containing the path to an image file.
|
||||||
- `operator` **[string][3]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
|
* `operator` **[string][3]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
|
||||||
- `options` **[Object][2]?**
|
* `options` **[Object][2]?**
|
||||||
- `options.raw` **[Object][2]?** describes operand when using raw pixel data.
|
|
||||||
- `options.raw.width` **[number][1]?**
|
|
||||||
- `options.raw.height` **[number][1]?**
|
|
||||||
- `options.raw.channels` **[number][1]?**
|
|
||||||
|
|
||||||
|
* `options.raw` **[Object][2]?** describes operand when using raw pixel data.
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* `options.raw.width` **[number][1]?**
|
||||||
|
* `options.raw.height` **[number][1]?**
|
||||||
|
* `options.raw.channels` **[number][1]?**
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -313,11 +357,12 @@ Apply the linear formula a \* input + b to the image (levels adjustment)
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `a` **[number][1]** multiplier (optional, default `1.0`)
|
* `a` **[number][1]** multiplier (optional, default `1.0`)
|
||||||
- `b` **[number][1]** offset (optional, default `0.0`)
|
* `b` **[number][1]** offset (optional, default `0.0`)
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -327,7 +372,7 @@ Recomb the image with the specified matrix.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `inputMatrix` **[Array][7]<[Array][7]<[number][1]>>** 3x3 Recombination matrix
|
* `inputMatrix` **[Array][7]<[Array][7]<[number][1]>>** 3x3 Recombination matrix
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -345,13 +390,13 @@ sharp(input)
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][5]** Invalid parameters
|
* Throws **[Error][5]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
**Meta**
|
**Meta**
|
||||||
|
|
||||||
- **since**: 0.21.1
|
* **since**: 0.21.1
|
||||||
|
|
||||||
## modulate
|
## modulate
|
||||||
|
|
||||||
@@ -359,10 +404,11 @@ Transforms the image using brightness, saturation and hue rotation.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][2]?**
|
* `options` **[Object][2]?**
|
||||||
- `options.brightness` **[number][1]?** Brightness multiplier
|
|
||||||
- `options.saturation` **[number][1]?** Saturation multiplier
|
* `options.brightness` **[number][1]?** Brightness multiplier
|
||||||
- `options.hue` **[number][1]?** Degrees for hue rotation
|
* `options.saturation` **[number][1]?** Saturation multiplier
|
||||||
|
* `options.hue` **[number][1]?** Degrees for hue rotation
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -390,7 +436,7 @@ Returns **Sharp**
|
|||||||
|
|
||||||
**Meta**
|
**Meta**
|
||||||
|
|
||||||
- **since**: 0.22.1
|
* **since**: 0.22.1
|
||||||
|
|
||||||
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||||
|
|
||||||
@@ -406,4 +452,8 @@ Returns **Sharp**
|
|||||||
|
|
||||||
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
||||||
|
|
||||||
[8]: https://nodejs.org/api/buffer.html
|
[8]: /api-channel#removealpha
|
||||||
|
|
||||||
|
[9]: https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE
|
||||||
|
|
||||||
|
[10]: https://nodejs.org/api/buffer.html
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ A `Promise` is returned when `callback` is not provided.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `fileOut` **[string][2]** the path to write the image data to.
|
* `fileOut` **[string][2]** the path to write the image data to.
|
||||||
- `callback` **[Function][3]?** 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`.
|
||||||
@@ -35,9 +35,9 @@ sharp(input)
|
|||||||
.catch(err => { ... });
|
.catch(err => { ... });
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid parameters
|
* Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
Returns **[Promise][5]<[Object][6]>** when no callback is provided
|
Returns **[Promise][5]<[Object][6]>** when no callback is provided
|
||||||
|
|
||||||
## toBuffer
|
## toBuffer
|
||||||
|
|
||||||
@@ -51,19 +51,21 @@ 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:
|
||||||
|
|
||||||
- `err` is an error, if any.
|
* `err` is an error, if any.
|
||||||
- `data` is the output image data.
|
* `data` is the output image data.
|
||||||
- `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).
|
|
||||||
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
|
`channels` and `premultiplied` (indicating if premultiplication was used).
|
||||||
|
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
|
||||||
|
|
||||||
A `Promise` is returned when `callback` is not provided.
|
A `Promise` is returned when `callback` is not provided.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?**
|
* `options` **[Object][6]?**
|
||||||
- `options.resolveWithObject` **[boolean][7]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
|
||||||
- `callback` **[Function][3]?**
|
* `options.resolveWithObject` **[boolean][7]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
||||||
|
* `callback` **[Function][3]?**
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -103,7 +105,7 @@ await sharp(pixelArray, { raw: { width, height, channels } })
|
|||||||
.toFile('my-changed-image.jpg');
|
.toFile('my-changed-image.jpg');
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
|
Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
|
||||||
|
|
||||||
## withMetadata
|
## withMetadata
|
||||||
|
|
||||||
@@ -116,9 +118,12 @@ sRGB colour space and strip all metadata, including the removal of any ICC profi
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?**
|
* `options` **[Object][6]?**
|
||||||
- `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
|
||||||
- `options.icc` **[string][2]?** filesystem path to output ICC profile, defaults to sRGB.
|
* `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
||||||
|
* `options.icc` **[string][2]?** filesystem path to output ICC profile, defaults to sRGB.
|
||||||
|
* `options.exif` **[Object][6]<[Object][6]>** Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data. (optional, default `{}`)
|
||||||
|
* `options.density` **[number][9]?** Number of pixels per inch (DPI).
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -129,7 +134,26 @@ sharp('input.jpg')
|
|||||||
.then(info => { ... });
|
.then(info => { ... });
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid parameters
|
```javascript
|
||||||
|
// Set "IFD0-Copyright" in output EXIF metadata
|
||||||
|
const data = await sharp(input)
|
||||||
|
.withMetadata({
|
||||||
|
exif: {
|
||||||
|
IFD0: {
|
||||||
|
Copyright: 'Wernham Hogg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
* @example
|
||||||
|
// Set output metadata to 96 DPI
|
||||||
|
const data = await sharp(input)
|
||||||
|
.withMetadata({ density: 96 })
|
||||||
|
.toBuffer();
|
||||||
|
```
|
||||||
|
|
||||||
|
* Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -139,8 +163,8 @@ Force output to a given format.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `format` **([string][2] \| [Object][6])** 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][6]** output options
|
* `options` **[Object][6]** output options
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -151,7 +175,7 @@ const data = await sharp(input)
|
|||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** unsupported format or options
|
* Throws **[Error][4]** unsupported format or options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -161,20 +185,21 @@ Use these JPEG options for output image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?** output options
|
* `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
|
||||||
- `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
|
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
||||||
- `options.chromaSubsampling` **[string][2]** set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling (optional, default `'4:2:0'`)
|
* `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
|
||||||
- `options.optimiseCoding` **[boolean][7]** optimise Huffman coding tables (optional, default `true`)
|
* `options.chromaSubsampling` **[string][2]** set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling (optional, default `'4:2:0'`)
|
||||||
- `options.optimizeCoding` **[boolean][7]** alternative spelling of optimiseCoding (optional, default `true`)
|
* `options.optimiseCoding` **[boolean][7]** optimise Huffman coding tables (optional, default `true`)
|
||||||
- `options.mozjpeg` **[boolean][7]** use mozjpeg defaults, equivalent to `{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }` (optional, default `false`)
|
* `options.optimizeCoding` **[boolean][7]** alternative spelling of optimiseCoding (optional, default `true`)
|
||||||
- `options.trellisQuantisation` **[boolean][7]** apply trellis quantisation (optional, default `false`)
|
* `options.mozjpeg` **[boolean][7]** use mozjpeg defaults, equivalent to `{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }` (optional, default `false`)
|
||||||
- `options.overshootDeringing` **[boolean][7]** apply overshoot deringing (optional, default `false`)
|
* `options.trellisQuantisation` **[boolean][7]** apply trellis quantisation (optional, default `false`)
|
||||||
- `options.optimiseScans` **[boolean][7]** optimise progressive scans, forces progressive (optional, default `false`)
|
* `options.overshootDeringing` **[boolean][7]** apply overshoot deringing (optional, default `false`)
|
||||||
- `options.optimizeScans` **[boolean][7]** alternative spelling of optimiseScans (optional, default `false`)
|
* `options.optimiseScans` **[boolean][7]** optimise progressive scans, forces progressive (optional, default `false`)
|
||||||
- `options.quantisationTable` **[number][9]** quantization table to use, integer 0-8 (optional, default `0`)
|
* `options.optimizeScans` **[boolean][7]** alternative spelling of optimiseScans (optional, default `false`)
|
||||||
- `options.quantizationTable` **[number][9]** alternative spelling of quantisationTable (optional, default `0`)
|
* `options.quantisationTable` **[number][9]** quantization table to use, integer 0-8 (optional, default `0`)
|
||||||
- `options.force` **[boolean][7]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
|
* `options.quantizationTable` **[number][9]** alternative spelling of quantisationTable (optional, default `0`)
|
||||||
|
* `options.force` **[boolean][7]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -195,7 +220,7 @@ const data = await sharp(input)
|
|||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid options
|
* Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -209,16 +234,17 @@ Set `palette` to `true` for slower, indexed PNG output.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?**
|
* `options` **[Object][6]?**
|
||||||
- `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
|
|
||||||
- `options.compressionLevel` **[number][9]** zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) (optional, default `6`)
|
* `options.progressive` **[boolean][7]** use progressive (interlace) scan (optional, default `false`)
|
||||||
- `options.adaptiveFiltering` **[boolean][7]** use adaptive row filtering (optional, default `false`)
|
* `options.compressionLevel` **[number][9]** zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) (optional, default `6`)
|
||||||
- `options.palette` **[boolean][7]** quantise to a palette-based image with alpha transparency support (optional, default `false`)
|
* `options.adaptiveFiltering` **[boolean][7]** use adaptive row filtering (optional, default `false`)
|
||||||
- `options.quality` **[number][9]** use the lowest number of colours needed to achieve given quality, sets `palette` to `true` (optional, default `100`)
|
* `options.palette` **[boolean][7]** quantise to a palette-based image with alpha transparency support (optional, default `false`)
|
||||||
- `options.colours` **[number][9]** maximum number of palette entries, sets `palette` to `true` (optional, default `256`)
|
* `options.quality` **[number][9]** use the lowest number of colours needed to achieve given quality, sets `palette` to `true` (optional, default `100`)
|
||||||
- `options.colors` **[number][9]** alternative spelling of `options.colours`, sets `palette` to `true` (optional, default `256`)
|
* `options.colours` **[number][9]** maximum number of palette entries, sets `palette` to `true` (optional, default `256`)
|
||||||
- `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, sets `palette` to `true` (optional, default `1.0`)
|
* `options.colors` **[number][9]** alternative spelling of `options.colours`, sets `palette` to `true` (optional, default `256`)
|
||||||
- `options.force` **[boolean][7]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
* `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, sets `palette` to `true` (optional, default `1.0`)
|
||||||
|
* `options.force` **[boolean][7]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -236,7 +262,7 @@ const data = await sharp(input)
|
|||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid options
|
* Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -246,17 +272,18 @@ Use these WebP options for output image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?** output options
|
* `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
|
||||||
- `options.alphaQuality` **[number][9]** quality of alpha layer, integer 0-100 (optional, default `100`)
|
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
||||||
- `options.lossless` **[boolean][7]** use lossless compression mode (optional, default `false`)
|
* `options.alphaQuality` **[number][9]** quality of alpha layer, integer 0-100 (optional, default `100`)
|
||||||
- `options.nearLossless` **[boolean][7]** use near_lossless compression mode (optional, default `false`)
|
* `options.lossless` **[boolean][7]** use lossless compression mode (optional, default `false`)
|
||||||
- `options.smartSubsample` **[boolean][7]** use high quality chroma subsampling (optional, default `false`)
|
* `options.nearLossless` **[boolean][7]** use near_lossless compression mode (optional, default `false`)
|
||||||
- `options.reductionEffort` **[number][9]** level of CPU effort to reduce file size, integer 0-6 (optional, default `4`)
|
* `options.smartSubsample` **[boolean][7]** use high quality chroma subsampling (optional, default `false`)
|
||||||
- `options.pageHeight` **[number][9]?** page height for animated output
|
* `options.reductionEffort` **[number][9]** level of CPU effort to reduce file size, integer 0-6 (optional, default `4`)
|
||||||
- `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
|
* `options.pageHeight` **[number][9]?** page height for animated output
|
||||||
- `options.delay` **[Array][10]<[number][9]>?** list of delays between animation frames (in milliseconds)
|
* `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
|
||||||
- `options.force` **[boolean][7]** force WebP output, otherwise attempt to use input format (optional, default `true`)
|
* `options.delay` **[Array][10]<[number][9]>?** list of delays between animation frames (in milliseconds)
|
||||||
|
* `options.force` **[boolean][7]** force WebP output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -267,7 +294,14 @@ const data = await sharp(input)
|
|||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid options
|
```javascript
|
||||||
|
// Optimise the file size of an animated WebP
|
||||||
|
const outputWebp = await sharp(inputWebp, { animated: true })
|
||||||
|
.webp({ reductionEffort: 6 })
|
||||||
|
.toBuffer();
|
||||||
|
```
|
||||||
|
|
||||||
|
* Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -281,14 +315,16 @@ The prebuilt binaries do not include this - see
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?** output options
|
* `options` **[Object][6]?** output options
|
||||||
- `options.pageHeight` **[number][9]?** page height for animated output
|
|
||||||
- `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
|
|
||||||
- `options.delay` **[Array][10]<[number][9]>?** list of delays between animation frames (in milliseconds)
|
|
||||||
- `options.force` **[boolean][7]** force GIF output, otherwise attempt to use input format (optional, default `true`)
|
|
||||||
|
|
||||||
|
* `options.pageHeight` **[number][9]?** page height for animated output
|
||||||
|
* `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
|
||||||
|
* `options.delay` **[Array][10]<[number][9]>?** list of delays between animation frames (in milliseconds)
|
||||||
|
* `options.force` **[boolean][7]** force GIF output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid options
|
<!---->
|
||||||
|
|
||||||
|
* Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -298,18 +334,19 @@ Use these TIFF options for output image.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?** output options
|
* `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
|
||||||
- `options.force` **[boolean][7]** force TIFF output, otherwise attempt to use input format (optional, default `true`)
|
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
||||||
- `options.compression` **[string][2]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
|
* `options.force` **[boolean][7]** force TIFF output, otherwise attempt to use input format (optional, default `true`)
|
||||||
- `options.predictor` **[string][2]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
|
* `options.compression` **[string][2]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
|
||||||
- `options.pyramid` **[boolean][7]** write an image pyramid (optional, default `false`)
|
* `options.predictor` **[string][2]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
|
||||||
- `options.tile` **[boolean][7]** write a tiled tiff (optional, default `false`)
|
* `options.pyramid` **[boolean][7]** write an image pyramid (optional, default `false`)
|
||||||
- `options.tileWidth` **[number][9]** horizontal tile size (optional, default `256`)
|
* `options.tile` **[boolean][7]** write a tiled tiff (optional, default `false`)
|
||||||
- `options.tileHeight` **[number][9]** vertical tile size (optional, default `256`)
|
* `options.tileWidth` **[number][9]** horizontal tile size (optional, default `256`)
|
||||||
- `options.xres` **[number][9]** horizontal resolution in pixels/mm (optional, default `1.0`)
|
* `options.tileHeight` **[number][9]** vertical tile size (optional, default `256`)
|
||||||
- `options.yres` **[number][9]** vertical resolution in pixels/mm (optional, default `1.0`)
|
* `options.xres` **[number][9]** horizontal resolution in pixels/mm (optional, default `1.0`)
|
||||||
- `options.bitdepth` **[number][9]** reduce bitdepth to 1, 2 or 4 bit (optional, default `8`)
|
* `options.yres` **[number][9]** vertical resolution in pixels/mm (optional, default `1.0`)
|
||||||
|
* `options.bitdepth` **[number][9]** reduce bitdepth to 1, 2 or 4 bit (optional, default `8`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -324,7 +361,7 @@ sharp('input.svg')
|
|||||||
.then(info => { ... });
|
.then(info => { ... });
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid options
|
* Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -337,20 +374,22 @@ most web browsers do not display these properly.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?** output options
|
* `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
|
|
||||||
- `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
|
|
||||||
- `options.speed` **[number][9]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
|
|
||||||
- `options.chromaSubsampling` **[string][2]** set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0 (optional, default `'4:2:0'`)
|
|
||||||
|
|
||||||
|
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
|
||||||
|
* `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
|
||||||
|
* `options.speed` **[number][9]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
|
||||||
|
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid options
|
<!---->
|
||||||
|
|
||||||
|
* Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
**Meta**
|
**Meta**
|
||||||
|
|
||||||
- **since**: 0.27.0
|
* **since**: 0.27.0
|
||||||
|
|
||||||
## heif
|
## heif
|
||||||
|
|
||||||
@@ -361,48 +400,56 @@ globally-installed libvips compiled with support for libheif, libde265 and x265.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?** output options
|
* `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
|
|
||||||
- `options.compression` **[string][2]** compression format: av1, hevc (optional, default `'av1'`)
|
|
||||||
- `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
|
|
||||||
- `options.speed` **[number][9]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
|
|
||||||
- `options.chromaSubsampling` **[string][2]** set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0 (optional, default `'4:2:0'`)
|
|
||||||
|
|
||||||
|
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
|
||||||
|
* `options.compression` **[string][2]** compression format: av1, hevc (optional, default `'av1'`)
|
||||||
|
* `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
|
||||||
|
* `options.speed` **[number][9]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
|
||||||
|
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid options
|
<!---->
|
||||||
|
|
||||||
|
* Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
**Meta**
|
**Meta**
|
||||||
|
|
||||||
- **since**: 0.23.0
|
* **since**: 0.23.0
|
||||||
|
|
||||||
## raw
|
## raw
|
||||||
|
|
||||||
Force output to be raw, uncompressed, 8-bit unsigned integer (unit8) pixel data.
|
Force output to be raw, uncompressed pixel data.
|
||||||
Pixel ordering is left-to-right, top-to-bottom, without padding.
|
Pixel ordering is left-to-right, top-to-bottom, without padding.
|
||||||
Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
|
Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* `options` **[Object][6]?** output options
|
||||||
|
|
||||||
|
* `options.depth` **[string][2]** bit depth, one of: char, uchar (default), short, ushort, int, uint, float, complex, double, dpcomplex (optional, default `'uchar'`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Extract raw RGB pixel data from JPEG input
|
// Extract raw, unsigned 8-bit RGB pixel data from JPEG input
|
||||||
const { data, info } = await sharp('input.jpg')
|
const { data, info } = await sharp('input.jpg')
|
||||||
.raw()
|
.raw()
|
||||||
.toBuffer({ resolveWithObject: true });
|
.toBuffer({ resolveWithObject: true });
|
||||||
```
|
```
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Extract alpha channel as raw pixel data from PNG input
|
// Extract alpha channel as raw, unsigned 16-bit pixel data from PNG input
|
||||||
const data = await sharp('input.png')
|
const data = await sharp('input.png')
|
||||||
.ensureAlpha()
|
.ensureAlpha()
|
||||||
.extractChannel(3)
|
.extractChannel(3)
|
||||||
.toColourspace('b-w')
|
.toColourspace('b-w')
|
||||||
.raw()
|
.raw({ depth: 'ushort' })
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns **Sharp**
|
* Throws **[Error][4]** Invalid options
|
||||||
|
|
||||||
## tile
|
## tile
|
||||||
|
|
||||||
@@ -414,18 +461,19 @@ Warning: multiple sharp instances concurrently producing tile output can expose
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?**
|
* `options` **[Object][6]?**
|
||||||
- `options.size` **[number][9]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
|
|
||||||
- `options.overlap` **[number][9]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
|
* `options.size` **[number][9]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
|
||||||
- `options.angle` **[number][9]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
|
* `options.overlap` **[number][9]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
|
||||||
- `options.background` **([string][2] \| [Object][6])** background colour, parsed by the [color][12] module, defaults to white without transparency. (optional, default `{r:255,g:255,b:255,alpha:1}`)
|
* `options.angle` **[number][9]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
|
||||||
- `options.depth` **[string][2]?** 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][12] module, defaults to white without transparency. (optional, default `{r:255,g:255,b:255,alpha:1}`)
|
||||||
- `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.depth` **[string][2]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
|
||||||
- `options.container` **[string][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
|
* `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.layout` **[string][2]** filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`. (optional, default `'dz'`)
|
* `options.container` **[string][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
|
||||||
- `options.centre` **[boolean][7]** centre image in tile. (optional, default `false`)
|
* `options.layout` **[string][2]** filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`. (optional, default `'dz'`)
|
||||||
- `options.center` **[boolean][7]** alternative spelling of centre. (optional, default `false`)
|
* `options.centre` **[boolean][7]** centre image in tile. (optional, default `false`)
|
||||||
- `options.id` **[string][2]** when `layout` is `iiif`, sets the `@id` attribute of `info.json` (optional, default `'https://example.com/iiif'`)
|
* `options.center` **[boolean][7]** alternative spelling of centre. (optional, default `false`)
|
||||||
|
* `options.id` **[string][2]** when `layout` is `iiif`, sets the `@id` attribute of `info.json` (optional, default `'https://example.com/iiif'`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -441,7 +489,7 @@ sharp('input.tiff')
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid parameters
|
* Throws **[Error][4]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
|
|||||||
@@ -6,49 +6,50 @@ Resize image to `width`, `height` or `width x height`.
|
|||||||
|
|
||||||
When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
|
When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
|
||||||
|
|
||||||
- `cover`: (default) Preserving aspect ratio, ensure the image covers both provided dimensions by cropping/clipping to fit.
|
* `cover`: (default) Preserving aspect ratio, ensure the image covers both provided dimensions by cropping/clipping to fit.
|
||||||
- `contain`: Preserving aspect ratio, contain within both provided dimensions using "letterboxing" where necessary.
|
* `contain`: Preserving aspect ratio, contain within both provided dimensions using "letterboxing" where necessary.
|
||||||
- `fill`: Ignore the aspect ratio of the input and stretch to both provided dimensions.
|
* `fill`: Ignore the aspect ratio of the input and stretch to both provided dimensions.
|
||||||
- `inside`: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
|
* `inside`: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
|
||||||
- `outside`: Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified.
|
* `outside`: Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified.
|
||||||
|
|
||||||
Some of these values are based on the [object-fit][1] CSS property.
|
Some of these values are based on the [object-fit][1] CSS property.
|
||||||
|
|
||||||
When using a `fit` of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
When using a `fit` of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
||||||
|
|
||||||
- `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
* `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
||||||
- `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
|
* `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
|
||||||
- `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
|
* `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
|
||||||
|
|
||||||
Some of these values are based on the [object-position][2] CSS property.
|
Some of these values are based on the [object-position][2] CSS property.
|
||||||
|
|
||||||
The experimental strategy-based approach resizes so one dimension is at its target length
|
The experimental strategy-based approach resizes so one dimension is at its target length
|
||||||
then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
|
then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
|
||||||
|
|
||||||
- `entropy`: focus on the region with the highest [Shannon entropy][3].
|
* `entropy`: focus on the region with the highest [Shannon entropy][3].
|
||||||
- `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
|
* `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
|
||||||
|
|
||||||
Possible interpolation kernels are:
|
Possible interpolation kernels are:
|
||||||
|
|
||||||
- `nearest`: Use [nearest neighbour interpolation][4].
|
* `nearest`: Use [nearest neighbour interpolation][4].
|
||||||
- `cubic`: Use a [Catmull-Rom spline][5].
|
* `cubic`: Use a [Catmull-Rom spline][5].
|
||||||
- `mitchell`: Use a [Mitchell-Netravali spline][6].
|
* `mitchell`: Use a [Mitchell-Netravali spline][6].
|
||||||
- `lanczos2`: Use a [Lanczos kernel][7] with `a=2`.
|
* `lanczos2`: Use a [Lanczos kernel][7] with `a=2`.
|
||||||
- `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
* `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `width` **[number][8]?** pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
|
* `width` **[number][8]?** pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
|
||||||
- `height` **[number][8]?** pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
|
* `height` **[number][8]?** pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
|
||||||
- `options` **[Object][9]?**
|
* `options` **[Object][9]?**
|
||||||
- `options.width` **[String][10]?** alternative means of specifying `width`. If both are present this take priority.
|
|
||||||
- `options.height` **[String][10]?** alternative means of specifying `height`. If both are present this take priority.
|
* `options.width` **[String][10]?** alternative means of specifying `width`. If both are present this take priority.
|
||||||
- `options.fit` **[String][10]** how the image should be resized to fit both provided dimensions, one of `cover`, `contain`, `fill`, `inside` or `outside`. (optional, default `'cover'`)
|
* `options.height` **[String][10]?** alternative means of specifying `height`. If both are present this take priority.
|
||||||
- `options.position` **[String][10]** position, gravity or strategy to use when `fit` is `cover` or `contain`. (optional, default `'centre'`)
|
* `options.fit` **[String][10]** how the image should be resized to fit both provided dimensions, one of `cover`, `contain`, `fill`, `inside` or `outside`. (optional, default `'cover'`)
|
||||||
- `options.background` **([String][10] \| [Object][9])** background colour when using a `fit` of `contain`, parsed by the [color][11] module, defaults to black without transparency. (optional, default `{r:0,g:0,b:0,alpha:1}`)
|
* `options.position` **[String][10]** position, gravity or strategy to use when `fit` is `cover` or `contain`. (optional, default `'centre'`)
|
||||||
- `options.kernel` **[String][10]** the kernel to use for image reduction. (optional, default `'lanczos3'`)
|
* `options.background` **([String][10] | [Object][9])** background colour when using a `fit` of `contain`, parsed by the [color][11] module, defaults to black without transparency. (optional, default `{r:0,g:0,b:0,alpha:1}`)
|
||||||
- `options.withoutEnlargement` **[Boolean][12]** do not enlarge if the width _or_ height are already less than the specified dimensions, equivalent to GraphicsMagick's `>` geometry option. (optional, default `false`)
|
* `options.kernel` **[String][10]** the kernel to use for image reduction. (optional, default `'lanczos3'`)
|
||||||
- `options.fastShrinkOnLoad` **[Boolean][12]** take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern on some images. (optional, default `true`)
|
* `options.withoutEnlargement` **[Boolean][12]** do not enlarge if the width *or* height are already less than the specified dimensions, equivalent to GraphicsMagick's `>` geometry option. (optional, default `false`)
|
||||||
|
* `options.fastShrinkOnLoad` **[Boolean][12]** take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern on some images. (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ const scaleByHalf = await sharp(input)
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][13]** Invalid parameters
|
* Throws **[Error][13]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -136,12 +137,13 @@ This operation will always occur after resizing and extraction, if any.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `extend` **([number][8] \| [Object][9])** single pixel count to add to all edges or an Object with per-edge counts
|
* `extend` **([number][8] | [Object][9])** single pixel count to add to all edges or an Object with per-edge counts
|
||||||
- `extend.top` **[number][8]** (optional, default `0`)
|
|
||||||
- `extend.left` **[number][8]** (optional, default `0`)
|
* `extend.top` **[number][8]** (optional, default `0`)
|
||||||
- `extend.bottom` **[number][8]** (optional, default `0`)
|
* `extend.left` **[number][8]** (optional, default `0`)
|
||||||
- `extend.right` **[number][8]** (optional, default `0`)
|
* `extend.bottom` **[number][8]** (optional, default `0`)
|
||||||
- `extend.background` **([String][10] \| [Object][9])** background colour, parsed by the [color][11] module, defaults to black without transparency. (optional, default `{r:0,g:0,b:0,alpha:1}`)
|
* `extend.right` **[number][8]** (optional, default `0`)
|
||||||
|
* `extend.background` **([String][10] | [Object][9])** background colour, parsed by the [color][11] module, defaults to black without transparency. (optional, default `{r:0,g:0,b:0,alpha:1}`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -170,7 +172,7 @@ sharp(input)
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][13]** Invalid parameters
|
* Throws **[Error][13]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -178,17 +180,18 @@ Returns **Sharp**
|
|||||||
|
|
||||||
Extract/crop a region of the image.
|
Extract/crop a region of the image.
|
||||||
|
|
||||||
- Use `extract` before `resize` for pre-resize extraction.
|
* Use `extract` before `resize` for pre-resize extraction.
|
||||||
- Use `extract` after `resize` for post-resize extraction.
|
* Use `extract` after `resize` for post-resize extraction.
|
||||||
- Use `extract` before and after for both.
|
* Use `extract` before and after for both.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][9]** describes the region to extract using integral pixel values
|
* `options` **[Object][9]** describes the region to extract using integral pixel values
|
||||||
- `options.left` **[number][8]** zero-indexed offset from left edge
|
|
||||||
- `options.top` **[number][8]** zero-indexed offset from top edge
|
* `options.left` **[number][8]** zero-indexed offset from left edge
|
||||||
- `options.width` **[number][8]** width of region to extract
|
* `options.top` **[number][8]** zero-indexed offset from top edge
|
||||||
- `options.height` **[number][8]** height of region to extract
|
* `options.width` **[number][8]** width of region to extract
|
||||||
|
* `options.height` **[number][8]** height of region to extract
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -210,7 +213,7 @@ sharp(input)
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][13]** Invalid parameters
|
* Throws **[Error][13]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
@@ -224,10 +227,11 @@ will contain `trimOffsetLeft` and `trimOffsetTop` properties.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `threshold` **[number][8]** the allowed difference from the top-left pixel, a number greater than zero. (optional, default `10`)
|
* `threshold` **[number][8]** the allowed difference from the top-left pixel, a number greater than zero. (optional, default `10`)
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
- Throws **[Error][13]** Invalid parameters
|
* Throws **[Error][13]** Invalid parameters
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
|
|||||||
@@ -54,17 +54,18 @@ console.log(sharp.versions);
|
|||||||
|
|
||||||
## cache
|
## cache
|
||||||
|
|
||||||
Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
|
Gets or, when options are provided, sets the limits of *libvips'* operation cache.
|
||||||
Existing entries in the cache will be trimmed after any change in limits.
|
Existing entries in the cache will be trimmed after any change in limits.
|
||||||
This method always returns cache statistics,
|
This method always returns cache statistics,
|
||||||
useful for determining how much working memory is required for a particular task.
|
useful for determining how much working memory is required for a particular task.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **([Object][1] \| [boolean][10])** Object with the following attributes, or boolean where true uses default cache settings and false removes all caching (optional, default `true`)
|
* `options` **([Object][1] | [boolean][10])** Object with the following attributes, or boolean where true uses default cache settings and false removes all caching (optional, default `true`)
|
||||||
- `options.memory` **[number][11]** is the maximum memory in MB to use for this cache (optional, default `50`)
|
|
||||||
- `options.files` **[number][11]** is the maximum number of files to hold open (optional, default `20`)
|
* `options.memory` **[number][11]** is the maximum memory in MB to use for this cache (optional, default `50`)
|
||||||
- `options.items` **[number][11]** is the maximum number of operations to cache (optional, default `100`)
|
* `options.files` **[number][11]** is the maximum number of files to hold open (optional, default `20`)
|
||||||
|
* `options.items` **[number][11]** is the maximum number of operations to cache (optional, default `100`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ Returns **[Object][1]**
|
|||||||
## concurrency
|
## concurrency
|
||||||
|
|
||||||
Gets or, when a concurrency is provided, sets
|
Gets or, when a concurrency is provided, sets
|
||||||
the number of threads _libvips'_ should create to process each image.
|
the number of threads *libvips'* should create to process each image.
|
||||||
|
|
||||||
The default value is the number of CPU cores,
|
The default value is the number of CPU cores,
|
||||||
except when using glibc-based Linux without jemalloc,
|
except when using glibc-based Linux without jemalloc,
|
||||||
@@ -98,7 +99,7 @@ This method always returns the current concurrency.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `concurrency` **[number][11]?**
|
* `concurrency` **[number][11]?**
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -114,8 +115,8 @@ Returns **[number][11]** concurrency
|
|||||||
|
|
||||||
An EventEmitter that emits a `change` event when a task is either:
|
An EventEmitter that emits a `change` event when a task is either:
|
||||||
|
|
||||||
- queued, waiting for _libuv_ to provide a worker thread
|
* queued, waiting for *libuv* to provide a worker thread
|
||||||
- complete
|
* complete
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -129,8 +130,8 @@ sharp.queue.on('change', function(queueLength) {
|
|||||||
|
|
||||||
Provides access to internal task counters.
|
Provides access to internal task counters.
|
||||||
|
|
||||||
- queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
|
* queue is the number of tasks this module has queued waiting for *libuv* to provide a worker thread from its pool.
|
||||||
- process is the number of resize tasks currently being processed.
|
* process is the number of resize tasks currently being processed.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -150,7 +151,7 @@ by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM N
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `simd` **[boolean][10]** (optional, default `true`)
|
* `simd` **[boolean][10]** (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,94 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v0.29 - *circle*
|
||||||
|
|
||||||
|
Requires libvips v8.11.3
|
||||||
|
|
||||||
|
### v0.29.0 - 17th August 2021
|
||||||
|
|
||||||
|
* Drop support for Node.js 10, now requires Node.js >= 12.13.0.
|
||||||
|
|
||||||
|
* Add `background` property to PNG and GIF image metadata.
|
||||||
|
|
||||||
|
* Add `compression` property to HEIF image metadata.
|
||||||
|
[#2504](https://github.com/lovell/sharp/issues/2504)
|
||||||
|
|
||||||
|
* AVIF encoding now defaults to `4:4:4` chroma subsampling.
|
||||||
|
[#2562](https://github.com/lovell/sharp/issues/2562)
|
||||||
|
|
||||||
|
* Allow multiple platform-arch binaries in same `node_modules` installation tree.
|
||||||
|
[#2575](https://github.com/lovell/sharp/issues/2575)
|
||||||
|
|
||||||
|
* Default to single-channel `b-w` space when `extractChannel` is used.
|
||||||
|
[#2658](https://github.com/lovell/sharp/issues/2658)
|
||||||
|
|
||||||
|
* Allow installation directory to contain spaces (regression in v0.26.0).
|
||||||
|
[#2777](https://github.com/lovell/sharp/issues/2777)
|
||||||
|
|
||||||
|
* Add `pipelineColourspace` operator to set the processing space.
|
||||||
|
[#2704](https://github.com/lovell/sharp/pull/2704)
|
||||||
|
[@Daiz](https://github.com/Daiz)
|
||||||
|
|
||||||
|
* Allow bit depth to be set when using raw input and output.
|
||||||
|
[#2762](https://github.com/lovell/sharp/pull/2762)
|
||||||
|
[@mart-jansink](https://github.com/mart-jansink)
|
||||||
|
|
||||||
|
* Allow `negate` to act only on non-alpha channels.
|
||||||
|
[#2808](https://github.com/lovell/sharp/pull/2808)
|
||||||
|
[@rexxars](https://github.com/rexxars)
|
||||||
|
|
||||||
## v0.28 - *bijou*
|
## v0.28 - *bijou*
|
||||||
|
|
||||||
Requires libvips v8.10.6
|
Requires libvips v8.10.6
|
||||||
|
|
||||||
|
### v0.28.3 - 24th May 2021
|
||||||
|
|
||||||
|
* Ensure presence of libvips, vendored or global, before invoking node-gyp.
|
||||||
|
|
||||||
|
* Skip shrink-on-load for multi-page WebP.
|
||||||
|
[#2714](https://github.com/lovell/sharp/issues/2714)
|
||||||
|
|
||||||
|
* Add contrast limiting adaptive histogram equalization (CLAHE) operator.
|
||||||
|
[#2726](https://github.com/lovell/sharp/pull/2726)
|
||||||
|
[@baparham](https://github.com/baparham)
|
||||||
|
|
||||||
|
### v0.28.2 - 10th May 2021
|
||||||
|
|
||||||
|
* Allow `withMetadata` to set `density`.
|
||||||
|
[#967](https://github.com/lovell/sharp/issues/967)
|
||||||
|
|
||||||
|
* Skip shrink-on-load where one dimension <4px.
|
||||||
|
[#2653](https://github.com/lovell/sharp/issues/2653)
|
||||||
|
|
||||||
|
* Allow escaped proxy credentials.
|
||||||
|
[#2664](https://github.com/lovell/sharp/pull/2664)
|
||||||
|
[@msalettes](https://github.com/msalettes)
|
||||||
|
|
||||||
|
* Add `premultiplied` flag for raw pixel data input.
|
||||||
|
[#2685](https://github.com/lovell/sharp/pull/2685)
|
||||||
|
[@mnutt](https://github.com/mnutt)
|
||||||
|
|
||||||
|
* Detect empty input and throw a helpful error.
|
||||||
|
[#2687](https://github.com/lovell/sharp/pull/2687)
|
||||||
|
[@JakobJingleheimer](https://github.com/JakobJingleheimer)
|
||||||
|
|
||||||
|
* Add install-time flag to skip version compatibility checks.
|
||||||
|
[#2692](https://github.com/lovell/sharp/pull/2692)
|
||||||
|
[@xemle](https://github.com/xemle)
|
||||||
|
|
||||||
|
### v0.28.1 - 5th April 2021
|
||||||
|
|
||||||
|
* Ensure all installation errors are logged with a more obvious prefix.
|
||||||
|
|
||||||
|
* Allow `withMetadata` to set and update EXIF metadata.
|
||||||
|
[#650](https://github.com/lovell/sharp/issues/650)
|
||||||
|
|
||||||
|
* Add support for OME-TIFF Sub Image File Directories (subIFD).
|
||||||
|
[#2557](https://github.com/lovell/sharp/issues/2557)
|
||||||
|
|
||||||
|
* Allow `ensureAlpha` to set the alpha transparency level.
|
||||||
|
[#2634](https://github.com/lovell/sharp/issues/2634)
|
||||||
|
|
||||||
### v0.28.0 - 29th March 2021
|
### v0.28.0 - 29th March 2021
|
||||||
|
|
||||||
* Prebuilt binaries now include mozjpeg and libimagequant (BSD 2-Clause).
|
* Prebuilt binaries now include mozjpeg and libimagequant (BSD 2-Clause).
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
".*",
|
".*",
|
||||||
"build.js",
|
"build.js",
|
||||||
"firebase.json",
|
"firebase.json",
|
||||||
"*.md",
|
|
||||||
"image/**",
|
"image/**",
|
||||||
"search-index/**"
|
"search-index/**"
|
||||||
],
|
],
|
||||||
@@ -14,10 +13,9 @@
|
|||||||
{
|
{
|
||||||
"source": "**",
|
"source": "**",
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{ "key": "Cache-Control", "value": "max-age=86400" },
|
||||||
"key": "Cache-Control",
|
{ "key": "X-Content-Type-Options", "value": "nosniff" },
|
||||||
"value": "max-age=86400"
|
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" }
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -209,3 +209,12 @@ GitHub: https://github.com/beig
|
|||||||
|
|
||||||
Name: Florian Busch
|
Name: Florian Busch
|
||||||
GitHub: https://github.com/florian-busch
|
GitHub: https://github.com/florian-busch
|
||||||
|
|
||||||
|
Name: Matthieu Salettes
|
||||||
|
GitHub: https://github.com/msalettes
|
||||||
|
|
||||||
|
Name: Taneli Vatanen
|
||||||
|
GitHub: https://github.com/Daiz
|
||||||
|
|
||||||
|
Name: Mart Jansink
|
||||||
|
GitHub: https://github.com/mart-jansink
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<meta name="description" content="Resize large images in common formats to smaller, web-friendly JPEG, PNG, WebP and AVIF images of varying dimensions">
|
<meta name="description" content="Resize large images in common formats to smaller, web-friendly JPEG, PNG, WebP and AVIF images of varying dimensions">
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; object-src 'none'; style-src 'unsafe-inline';
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; object-src 'none'; style-src 'unsafe-inline';
|
||||||
img-src 'unsafe-inline' data: https://pixel.plumbing/px/ https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
img-src 'unsafe-inline' data: https://pixel.plumbing/px/ https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
||||||
connect-src 'self' https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
connect-src 'self' https://www.google-analytics.com;
|
||||||
script-src 'unsafe-inline' 'unsafe-eval'
|
script-src 'unsafe-inline' 'unsafe-eval'
|
||||||
https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.js
|
https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.js
|
||||||
https://www.google-analytics.com/analytics.js;">
|
https://www.google-analytics.com/analytics.js;">
|
||||||
@@ -19,8 +19,6 @@
|
|||||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/sharp-logo.svg">
|
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/sharp-logo.svg">
|
||||||
<link rel="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
|
<link rel="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
|
||||||
<link rel="author" href="/humans.txt" type="text/plain">
|
<link rel="author" href="/humans.txt" type="text/plain">
|
||||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.28.0/docs/README.md" as="fetch" type="text/markdown" crossorigin>
|
|
||||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" as="image" type="image/svg+xml" crossorigin>
|
|
||||||
<link rel="dns-prefetch" href="https://pixel.plumbing">
|
<link rel="dns-prefetch" href="https://pixel.plumbing">
|
||||||
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
@@ -105,7 +103,7 @@
|
|||||||
.then(function (entries) {
|
.then(function (entries) {
|
||||||
const results = [];
|
const results = [];
|
||||||
entries.forEach(function (entry) {
|
entries.forEach(function (entry) {
|
||||||
if (entry.k.includes(keyword)) {
|
if (entry.k.includes(keyword.trim().toLowerCase())) {
|
||||||
results.push({
|
results.push({
|
||||||
title: entry.t,
|
title: entry.t,
|
||||||
link: entry.l,
|
link: entry.l,
|
||||||
@@ -139,7 +137,6 @@
|
|||||||
docuteApiTitlePlugin,
|
docuteApiTitlePlugin,
|
||||||
docuteApiSearchPlugin
|
docuteApiSearchPlugin
|
||||||
],
|
],
|
||||||
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.28.0/docs',
|
|
||||||
nav: [
|
nav: [
|
||||||
{
|
{
|
||||||
title: 'Funding',
|
title: 'Funding',
|
||||||
|
|||||||
@@ -10,20 +10,20 @@ yarn add sharp
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
* Node.js v10+
|
* Node.js >= 12.13.0
|
||||||
|
|
||||||
## Prebuilt binaries
|
## Prebuilt binaries
|
||||||
|
|
||||||
Ready-compiled sharp and libvips binaries are provided for use with
|
Ready-compiled sharp and libvips binaries are provided for use on the most common platforms:
|
||||||
Node.js v10+ on the most common platforms:
|
|
||||||
|
|
||||||
* macOS x64 (>= 10.13)
|
* macOS x64 (>= 10.13)
|
||||||
|
* macOS ARM64
|
||||||
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
|
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
|
||||||
* Linux ARM64 (glibc >= 2.29, musl >= 1.1.24)
|
* Linux ARM64 (glibc >= 2.29, musl >= 1.1.24)
|
||||||
* Windows x64
|
* Windows x64
|
||||||
* Windows x86
|
* Windows x86
|
||||||
|
|
||||||
An ~7.5MB tarball containing libvips and its most commonly used dependencies
|
An ~7MB tarball containing libvips and its most commonly used dependencies
|
||||||
is downloaded via HTTPS and stored within `node_modules/sharp/vendor` during `npm install`.
|
is downloaded via HTTPS and stored within `node_modules/sharp/vendor` during `npm install`.
|
||||||
|
|
||||||
This provides support for the
|
This provides support for the
|
||||||
@@ -31,7 +31,6 @@ JPEG, PNG, WebP, AVIF, TIFF, GIF (input) and SVG (input) image formats.
|
|||||||
|
|
||||||
The following platforms have prebuilt libvips but not sharp:
|
The following platforms have prebuilt libvips but not sharp:
|
||||||
|
|
||||||
* macOS ARM64
|
|
||||||
* Linux ARMv6
|
* Linux ARMv6
|
||||||
* Linux ARMv7 (glibc >= 2.28)
|
* Linux ARMv7 (glibc >= 2.28)
|
||||||
* Windows ARM64
|
* Windows ARM64
|
||||||
@@ -49,6 +48,7 @@ The following platforms require compilation of both libvips and sharp from sourc
|
|||||||
|
|
||||||
The architecture and platform of Node.js used for `npm install`
|
The architecture and platform of Node.js used for `npm install`
|
||||||
must be the same as the architecture and platform of Node.js used at runtime.
|
must be the same as the architecture and platform of Node.js used at runtime.
|
||||||
|
See the [cross-platform](#cross-platform) section if this is not the case.
|
||||||
|
|
||||||
When using npm v6 or earlier, the `npm install --unsafe-perm` flag must be used when installing as `root` or a `sudo` user.
|
When using npm v6 or earlier, the `npm install --unsafe-perm` flag must be used when installing as `root` or a `sudo` user.
|
||||||
|
|
||||||
@@ -60,15 +60,36 @@ Check the output of running `npm install --verbose sharp` for useful error messa
|
|||||||
|
|
||||||
## Apple M1
|
## Apple M1
|
||||||
|
|
||||||
Prebuilt libvips binaries are provided for macOS on ARM64 (since sharp v0.28.0).
|
Prebuilt sharp and libvips binaries are provided for macOS on ARM64 from sharp v0.29.0.
|
||||||
|
|
||||||
During `npm install` sharp will be built locally,
|
Prebuilt libvips binaries were provided for macOS on ARM64 from sharp v0.28.0.
|
||||||
which requires Xcode and Python - see
|
|
||||||
[building from source](#building-from-source).
|
|
||||||
|
|
||||||
When this new ARM64 CPU is made freely available
|
## Cross-platform
|
||||||
to open source projects via a CI service
|
|
||||||
then prebuilt sharp binaries can also be provided.
|
At `npm install` time, prebuilt binaries are automatically selected for the
|
||||||
|
current OS platform and CPU architecture, where available.
|
||||||
|
|
||||||
|
The target platform and/or architecture can be manually selected using the following flags.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --platform=... --arch=... --arm-version=... sharp
|
||||||
|
```
|
||||||
|
|
||||||
|
* `--platform`: one of `linux`, `linuxmusl`, `darwin` or `win32`.
|
||||||
|
* `--arch`: one of `x64`, `ia32`, `arm` or `arm64`.
|
||||||
|
* `--arm-version`: one of `6`, `7` or `8` (`arm` defaults to `6`, `arm64` defaults to `8`).
|
||||||
|
* `--sharp-install-force`: skip version compatibility checks.
|
||||||
|
|
||||||
|
These values can also be set via environment variables,
|
||||||
|
`npm_config_platform`, `npm_config_arch`, `npm_config_arm_version`
|
||||||
|
and `SHARP_INSTALL_FORCE` respectively.
|
||||||
|
|
||||||
|
For example, if the target machine has a 64-bit ARM CPU and is running Alpine Linux,
|
||||||
|
use the following flags:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --arch=arm64 --platform=linuxmusl sharp
|
||||||
|
```
|
||||||
|
|
||||||
## Custom libvips
|
## Custom libvips
|
||||||
|
|
||||||
@@ -102,24 +123,24 @@ To install the prebuilt sharp binaries from a custom URL,
|
|||||||
set the `sharp_binary_host` npm config option
|
set the `sharp_binary_host` npm config option
|
||||||
or the `npm_config_sharp_binary_host` environment variable.
|
or the `npm_config_sharp_binary_host` environment variable.
|
||||||
|
|
||||||
|
To install the prebuilt sharp binaries from a directory on the local filesystem,
|
||||||
|
set the `sharp_local_prebuilds` npm config option
|
||||||
|
or the `npm_config_sharp_local_prebuilds` environment variable.
|
||||||
|
|
||||||
To install the prebuilt libvips binaries from a custom URL,
|
To install the prebuilt libvips binaries from a custom URL,
|
||||||
set the `sharp_libvips_binary_host` npm config option
|
set the `sharp_libvips_binary_host` npm config option
|
||||||
or the `npm_config_sharp_libvips_binary_host` environment variable.
|
or the `npm_config_sharp_libvips_binary_host` environment variable.
|
||||||
|
|
||||||
The version subpath and file name are appended to these. There should be tarballs available
|
The version subpath and file name are appended to these.
|
||||||
that are compressed with both gzip and Brotli, as the format downloaded will vary depending
|
|
||||||
on whether the user's version of Node supports Brotli decompression (Node.js v10.16.0+)
|
|
||||||
|
|
||||||
For example, if `sharp_libvips_binary_host` is set to `https://hostname/path`
|
For example, if `sharp_libvips_binary_host` is set to `https://hostname/path`
|
||||||
and the libvips version is `1.2.3` then the resultant URL will be
|
and the libvips version is `1.2.3` then the resultant URL will be
|
||||||
`https://hostname/path/v1.2.3/libvips-1.2.3-platform-arch.tar.br` or
|
`https://hostname/path/v1.2.3/libvips-1.2.3-platform-arch.tar.br`.
|
||||||
`https://hostname/path/v1.2.3/libvips-1.2.3-platform-arch.tar.gz`.
|
|
||||||
|
|
||||||
See the Chinese mirror below for a further example.
|
See the Chinese mirror below for a further example.
|
||||||
|
|
||||||
## Chinese mirror
|
## Chinese mirror
|
||||||
|
|
||||||
Alibaba provide a mirror site based in China containing binaries for both sharp and libvips.
|
A mirror site based in China, provided by Alibaba, contains binaries for both sharp and libvips.
|
||||||
|
|
||||||
To use this either set the following configuration:
|
To use this either set the following configuration:
|
||||||
|
|
||||||
@@ -178,34 +199,18 @@ to `false` when using the `yarn` package manager.
|
|||||||
|
|
||||||
## AWS Lambda
|
## AWS Lambda
|
||||||
|
|
||||||
Set the Lambda runtime to `nodejs12.x`.
|
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-package.html)
|
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-package.html)
|
||||||
must be for the Linux x64 platform.
|
must include binaries for the Linux x64 platform.
|
||||||
|
|
||||||
When building your deployment package on machines other than Linux x64 (glibc),
|
When building your deployment package on machines other than Linux x64 (glibc),
|
||||||
run the following commands:
|
run the following additional command after `npm install`:
|
||||||
|
|
||||||
macOS:
|
|
||||||
```sh
|
```sh
|
||||||
rm -rf node_modules/sharp
|
npm install
|
||||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux sharp
|
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux sharp
|
||||||
```
|
```
|
||||||
|
|
||||||
Windows:
|
|
||||||
```sh
|
|
||||||
rmdir /s /q node_modules/sharp
|
|
||||||
npm install --arch=x64 --platform=linux sharp
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively a Docker container closely matching the Lambda runtime can be used:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
rm -rf node_modules/sharp
|
|
||||||
docker run -v "$PWD":/var/task lambci/lambda:build-nodejs12.x npm install sharp
|
|
||||||
```
|
|
||||||
|
|
||||||
To get the best performance select the largest memory available.
|
To get the best performance select the largest memory available.
|
||||||
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
|
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ A test to benchmark the performance of this module relative to alternatives.
|
|||||||
## The contenders
|
## The contenders
|
||||||
|
|
||||||
* [jimp](https://www.npmjs.com/package/jimp) v0.16.1 - Image processing in pure JavaScript. Provides bicubic interpolation.
|
* [jimp](https://www.npmjs.com/package/jimp) v0.16.1 - Image processing in pure JavaScript. Provides bicubic interpolation.
|
||||||
* [mapnik](https://www.npmjs.org/package/mapnik) v4.5.6 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
|
* [mapnik](https://www.npmjs.org/package/mapnik) v4.5.8 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
|
||||||
* [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.
|
||||||
|
* [@squoosh/lib](https://www.npmjs.com/package/@squoosh/lib) v0.4.0 - Image libraries transpiled to WebAssembly, includes GPLv3 code.
|
||||||
|
* [@squoosh/cli](https://www.npmjs.com/package/@squoosh/cli) v0.7.2 - Command line wrapper around `@squoosh/lib`, avoids GPLv3 by spawning process.
|
||||||
* sharp v0.28.0 / libvips v8.10.6 - Caching within libvips disabled to ensure a fair comparison.
|
* sharp v0.28.0 / libvips v8.10.6 - Caching within libvips disabled to ensure a fair comparison.
|
||||||
|
|
||||||
## The task
|
## The task
|
||||||
@@ -19,21 +21,23 @@ then compress to JPEG at a "quality" setting of 80.
|
|||||||
## Test environment
|
## Test environment
|
||||||
|
|
||||||
* AWS EC2 eu-west-1 [c5ad.xlarge](https://aws.amazon.com/ec2/instance-types/c5/) (4x AMD EPYC 7R32)
|
* AWS EC2 eu-west-1 [c5ad.xlarge](https://aws.amazon.com/ec2/instance-types/c5/) (4x AMD EPYC 7R32)
|
||||||
* Ubuntu 20.10 (ami-03f10415e8b0bfb86)
|
* Ubuntu 21.04 (ami-0d7626a9c2ceab1ac)
|
||||||
* Node.js v14.16.0
|
* Node.js 16.6.2
|
||||||
|
|
||||||
## Results
|
## Results
|
||||||
|
|
||||||
| Module | Input | Output | Ops/sec | Speed-up |
|
| Module | Input | Output | Ops/sec | Speed-up |
|
||||||
| :----------------- | :----- | :----- | ------: | -------: |
|
| :----------------- | :----- | :----- | ------: | -------: |
|
||||||
| jimp | buffer | buffer | 0.78 | 1.0 |
|
| jimp | buffer | buffer | 0.83 | 1.0 |
|
||||||
| mapnik | buffer | buffer | 3.39 | 4.3 |
|
| squoosh-cli | file | file | 1.09 | 1.3 |
|
||||||
| gm | buffer | buffer | 7.84 | 10.1 |
|
| squoosh-lib | buffer | buffer | 1.83 | 2.2 |
|
||||||
| gm | file | file | 9.24 | 11.8 |
|
| mapnik | buffer | buffer | 3.41 | 4.1 |
|
||||||
| imagemagick | file | file | 9.37 | 12.0 |
|
| gm | buffer | buffer | 8.34 | 10.0 |
|
||||||
| sharp | stream | stream | 26.84 | 34.4 |
|
| imagemagick | file | file | 8.67 | 10.4 |
|
||||||
| sharp | file | file | 29.76 | 38.2 |
|
| gm | file | file | 8.82 | 10.6 |
|
||||||
| sharp | buffer | buffer | 31.60 | 40.5 |
|
| sharp | stream | stream | 29.44 | 35.5 |
|
||||||
|
| sharp | file | file | 29.64 | 35.7 |
|
||||||
|
| sharp | buffer | buffer | 31.09 | 37.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.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { extractDescription, extractKeywords } = require('./extract');
|
const { extractDescription, extractKeywords, extractParameters } = require('./extract');
|
||||||
|
|
||||||
const searchIndex = [];
|
const searchIndex = [];
|
||||||
|
|
||||||
@@ -37,18 +37,19 @@ for (const match of matches) {
|
|||||||
].forEach((section) => {
|
].forEach((section) => {
|
||||||
const contents = fs.readFileSync(path.join(__dirname, '..', `api-${section}.md`), 'utf8');
|
const contents = fs.readFileSync(path.join(__dirname, '..', `api-${section}.md`), 'utf8');
|
||||||
const matches = contents.matchAll(
|
const matches = contents.matchAll(
|
||||||
/\n## (?<title>[A-Za-z]+)\n\n(?<firstparagraph>.+?)\n\n/gs
|
/\n## (?<title>[A-Za-z]+)\n\n(?<firstparagraph>.+?)\n\n(?<parameters>### Parameters.+?Returns)?/gs
|
||||||
);
|
);
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
const { title, firstparagraph } = match.groups;
|
const { title, firstparagraph, parameters } = match.groups;
|
||||||
const description = firstparagraph.startsWith('###')
|
const description = firstparagraph.startsWith('###')
|
||||||
? 'Constructor'
|
? 'Constructor'
|
||||||
: extractDescription(firstparagraph);
|
: extractDescription(firstparagraph);
|
||||||
|
const parameterNames = parameters ? extractParameters(parameters) : '';
|
||||||
|
|
||||||
searchIndex.push({
|
searchIndex.push({
|
||||||
t: title,
|
t: title,
|
||||||
d: description,
|
d: description,
|
||||||
k: extractKeywords(`${title} ${description}`),
|
k: extractKeywords(`${title} ${description} ${parameterNames}`),
|
||||||
l: `/api-${section}#${title.toLowerCase()}`
|
l: `/api-${section}#${title.toLowerCase()}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const stopWords = require('./stop-words');
|
|||||||
|
|
||||||
const extractDescription = (str) =>
|
const extractDescription = (str) =>
|
||||||
str
|
str
|
||||||
|
.replace(/### Examples.*/sg, '')
|
||||||
.replace(/\(http[^)]+/g, '')
|
.replace(/\(http[^)]+/g, '')
|
||||||
.replace(/\s+/g, ' ')
|
.replace(/\s+/g, ' ')
|
||||||
.replace(/[^A-Za-z0-9_/\-,. ]/g, '')
|
.replace(/[^A-Za-z0-9_/\-,. ]/g, '')
|
||||||
@@ -11,6 +12,11 @@ const extractDescription = (str) =>
|
|||||||
.substr(0, 180)
|
.substr(0, 180)
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
|
const extractParameters = (str) =>
|
||||||
|
[...str.matchAll(/options\.(?<name>[^.`]+)/gs)]
|
||||||
|
.map((match) => match.groups.name)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
const extractKeywords = (str) =>
|
const extractKeywords = (str) =>
|
||||||
[
|
[
|
||||||
...new Set(
|
...new Set(
|
||||||
@@ -21,4 +27,4 @@ const extractKeywords = (str) =>
|
|||||||
)
|
)
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
module.exports = { extractDescription, extractKeywords };
|
module.exports = { extractDescription, extractKeywords, extractParameters };
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ module.exports = [
|
|||||||
'before',
|
'before',
|
||||||
'both',
|
'both',
|
||||||
'call',
|
'call',
|
||||||
|
'callback',
|
||||||
'can',
|
'can',
|
||||||
'containing',
|
'containing',
|
||||||
'default',
|
'default',
|
||||||
@@ -26,6 +27,7 @@ module.exports = [
|
|||||||
'ensure',
|
'ensure',
|
||||||
'etc',
|
'etc',
|
||||||
'every',
|
'every',
|
||||||
|
'except',
|
||||||
'for',
|
'for',
|
||||||
'from',
|
'from',
|
||||||
'get',
|
'get',
|
||||||
@@ -49,12 +51,16 @@ module.exports = [
|
|||||||
'occur',
|
'occur',
|
||||||
'occurs',
|
'occurs',
|
||||||
'options',
|
'options',
|
||||||
|
'other',
|
||||||
|
'out',
|
||||||
'over',
|
'over',
|
||||||
'perform',
|
'perform',
|
||||||
'performs',
|
'performs',
|
||||||
'provide',
|
'provide',
|
||||||
'provided',
|
'provided',
|
||||||
'ready',
|
'ready',
|
||||||
|
'requires',
|
||||||
|
'returned',
|
||||||
'same',
|
'same',
|
||||||
'see',
|
'see',
|
||||||
'set',
|
'set',
|
||||||
@@ -67,9 +73,11 @@ module.exports = [
|
|||||||
'supported',
|
'supported',
|
||||||
'sure',
|
'sure',
|
||||||
'take',
|
'take',
|
||||||
|
'than',
|
||||||
'that',
|
'that',
|
||||||
'the',
|
'the',
|
||||||
'their',
|
'their',
|
||||||
|
'then',
|
||||||
'there',
|
'there',
|
||||||
'therefore',
|
'therefore',
|
||||||
'these',
|
'these',
|
||||||
|
|||||||
11
install/can-compile.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const libvips = require('../lib/libvips');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!(libvips.useGlobalLibvips() || libvips.hasVendoredLibvips())) {
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
@@ -4,19 +4,19 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const libvips = require('../lib/libvips');
|
const libvips = require('../lib/libvips');
|
||||||
|
const platform = require('../lib/platform');
|
||||||
|
|
||||||
const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
|
const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
|
||||||
|
|
||||||
const platform = process.env.npm_config_platform || process.platform;
|
const platformAndArch = platform();
|
||||||
if (platform === 'win32') {
|
|
||||||
const buildDir = path.join(__dirname, '..', 'build');
|
if (platformAndArch.startsWith('win32')) {
|
||||||
const buildReleaseDir = path.join(buildDir, 'Release');
|
const buildReleaseDir = path.join(__dirname, '..', 'build', 'Release');
|
||||||
libvips.log(`Creating ${buildReleaseDir}`);
|
libvips.log(`Creating ${buildReleaseDir}`);
|
||||||
try {
|
try {
|
||||||
libvips.mkdirSync(buildDir);
|
|
||||||
libvips.mkdirSync(buildReleaseDir);
|
libvips.mkdirSync(buildReleaseDir);
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
const vendorLibDir = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, 'lib');
|
const vendorLibDir = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch, 'lib');
|
||||||
libvips.log(`Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
|
libvips.log(`Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
|
||||||
try {
|
try {
|
||||||
fs
|
fs
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ const stream = require('stream');
|
|||||||
const zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
|
|
||||||
const detectLibc = require('detect-libc');
|
const detectLibc = require('detect-libc');
|
||||||
const semver = require('semver');
|
const semverLessThan = require('semver/functions/lt');
|
||||||
|
const semverSatisfies = require('semver/functions/satisfies');
|
||||||
const simpleGet = require('simple-get');
|
const simpleGet = require('simple-get');
|
||||||
const tarFs = require('tar-fs');
|
const tarFs = require('tar-fs');
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ const minimumGlibcVersionByArch = {
|
|||||||
|
|
||||||
const hasSharpPrebuild = [
|
const hasSharpPrebuild = [
|
||||||
'darwin-x64',
|
'darwin-x64',
|
||||||
|
'darwin-arm64',
|
||||||
'linux-arm64',
|
'linux-arm64',
|
||||||
'linux-x64',
|
'linux-x64',
|
||||||
'linuxmusl-x64',
|
'linuxmusl-x64',
|
||||||
@@ -34,22 +36,27 @@ const hasSharpPrebuild = [
|
|||||||
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
||||||
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
||||||
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
||||||
const supportsBrotli = ('BrotliDecompress' in zlib);
|
const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
|
||||||
|
|
||||||
const fail = function (err) {
|
const fail = function (err) {
|
||||||
libvips.log(err);
|
libvips.log(err);
|
||||||
if (err.code === 'EACCES') {
|
if (err.code === 'EACCES') {
|
||||||
libvips.log('Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
|
libvips.log('Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
|
||||||
}
|
}
|
||||||
libvips.log('Attempting to build from source via node-gyp but this may fail due to the above error');
|
|
||||||
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
|
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleError = function (err) {
|
||||||
|
if (installationForced) {
|
||||||
|
libvips.log(`Installation warning: ${err.message}`);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const extractTarball = function (tarPath, platformAndArch) {
|
const extractTarball = function (tarPath, platformAndArch) {
|
||||||
const vendorPath = path.join(__dirname, '..', 'vendor');
|
const versionedVendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch);
|
||||||
libvips.mkdirSync(vendorPath);
|
|
||||||
const versionedVendorPath = path.join(vendorPath, minimumLibvipsVersion);
|
|
||||||
libvips.mkdirSync(versionedVendorPath);
|
libvips.mkdirSync(versionedVendorPath);
|
||||||
|
|
||||||
const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;
|
const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;
|
||||||
@@ -59,7 +66,7 @@ const extractTarball = function (tarPath, platformAndArch) {
|
|||||||
|
|
||||||
stream.pipeline(
|
stream.pipeline(
|
||||||
fs.createReadStream(tarPath),
|
fs.createReadStream(tarPath),
|
||||||
supportsBrotli ? new zlib.BrotliDecompress() : new zlib.Gunzip(),
|
new zlib.BrotliDecompress(),
|
||||||
tarFs.extract(versionedVendorPath, { ignore }),
|
tarFs.extract(versionedVendorPath, { ignore }),
|
||||||
function (err) {
|
function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -95,26 +102,25 @@ 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) {
|
// Linux libc version check
|
||||||
if (semver.lt(`${detectLibc.version}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
|
if (detectLibc.family === detectLibc.GLIBC && detectLibc.version && minimumGlibcVersionByArch[arch]) {
|
||||||
throw new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
if (semverLessThan(`${detectLibc.version}.0`, `${minimumGlibcVersionByArch[arch]}.0`)) {
|
||||||
|
handleError(new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (detectLibc.family === detectLibc.MUSL && detectLibc.version) {
|
if (detectLibc.family === detectLibc.MUSL && detectLibc.version) {
|
||||||
if (semver.lt(detectLibc.version, '1.1.24')) {
|
if (semverLessThan(detectLibc.version, '1.1.24')) {
|
||||||
throw new Error(`Use with musl ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
handleError(new Error(`Use with musl ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Node.js minimum version check
|
||||||
const supportedNodeVersion = process.env.npm_package_engines_node || require('../package.json').engines.node;
|
const supportedNodeVersion = process.env.npm_package_engines_node || require('../package.json').engines.node;
|
||||||
if (!semver.satisfies(process.versions.node, supportedNodeVersion)) {
|
if (!semverSatisfies(process.versions.node, supportedNodeVersion)) {
|
||||||
throw new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`);
|
handleError(new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
const extension = supportsBrotli ? 'br' : 'gz';
|
|
||||||
|
|
||||||
// Download to per-process temporary file
|
// Download to per-process temporary file
|
||||||
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.' + extension;
|
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.br';
|
||||||
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
|
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
|
||||||
if (fs.existsSync(tarPathCache)) {
|
if (fs.existsSync(tarPathCache)) {
|
||||||
libvips.log(`Using cached ${tarPathCache}`);
|
libvips.log(`Using cached ${tarPathCache}`);
|
||||||
@@ -145,7 +151,9 @@ try {
|
|||||||
tmpFileStream
|
tmpFileStream
|
||||||
.on('error', function (err) {
|
.on('error', function (err) {
|
||||||
// Clean up temporary file
|
// Clean up temporary file
|
||||||
fs.unlinkSync(tarPathTemp);
|
try {
|
||||||
|
fs.unlinkSync(tarPathTemp);
|
||||||
|
} catch (e) {}
|
||||||
fail(err);
|
fail(err);
|
||||||
})
|
})
|
||||||
.on('close', function () {
|
.on('close', function () {
|
||||||
@@ -157,7 +165,7 @@ try {
|
|||||||
fs.copyFileSync(tarPathTemp, tarPathCache);
|
fs.copyFileSync(tarPathTemp, tarPathCache);
|
||||||
fs.unlinkSync(tarPathTemp);
|
fs.unlinkSync(tarPathTemp);
|
||||||
}
|
}
|
||||||
extractTarball(tarPathCache);
|
extractTarball(tarPathCache, platformAndArch);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ module.exports = function () {
|
|||||||
? tunnelAgent.httpsOverHttps
|
? tunnelAgent.httpsOverHttps
|
||||||
: tunnelAgent.httpsOverHttp;
|
: tunnelAgent.httpsOverHttp;
|
||||||
const proxyAuth = proxy.username && proxy.password
|
const proxyAuth = proxy.username && proxy.password
|
||||||
? `${proxy.username}:${proxy.password}`
|
? `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`
|
||||||
: null;
|
: null;
|
||||||
return tunnel({
|
return tunnel({
|
||||||
proxy: {
|
proxy: {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ const bool = {
|
|||||||
/**
|
/**
|
||||||
* Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
|
* Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
|
||||||
*
|
*
|
||||||
|
* See also {@link /api-operation#flatten|flatten}.
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* sharp('rgba.png')
|
* sharp('rgba.png')
|
||||||
* .removeAlpha()
|
* .removeAlpha()
|
||||||
@@ -30,21 +32,39 @@ function removeAlpha () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
|
* Ensure the output image has an alpha transparency channel.
|
||||||
|
* If missing, the added alpha channel will have the specified
|
||||||
|
* transparency level, defaulting to fully-opaque (1).
|
||||||
|
* This is a no-op if the image already has an alpha channel.
|
||||||
*
|
*
|
||||||
* @since 0.21.2
|
* @since 0.21.2
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* sharp('rgb.jpg')
|
* // rgba.png will be a 4 channel image with a fully-opaque alpha channel
|
||||||
|
* await sharp('rgb.jpg')
|
||||||
* .ensureAlpha()
|
* .ensureAlpha()
|
||||||
* .toFile('rgba.png', function(err, info) {
|
* .toFile('rgba.png')
|
||||||
* // rgba.png is a 4 channel image with a fully opaque alpha channel
|
|
||||||
* });
|
|
||||||
*
|
*
|
||||||
|
* @example
|
||||||
|
* // rgba is a 4 channel image with a fully-transparent alpha channel
|
||||||
|
* const rgba = await sharp(rgb)
|
||||||
|
* .ensureAlpha(0)
|
||||||
|
* .toBuffer();
|
||||||
|
*
|
||||||
|
* @param {number} [alpha=1] - alpha transparency level (0=fully-transparent, 1=fully-opaque)
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
|
* @throws {Error} Invalid alpha transparency level
|
||||||
*/
|
*/
|
||||||
function ensureAlpha () {
|
function ensureAlpha (alpha) {
|
||||||
this.options.ensureAlpha = true;
|
if (is.defined(alpha)) {
|
||||||
|
if (is.number(alpha) && is.inRange(alpha, 0, 1)) {
|
||||||
|
this.options.ensureAlpha = alpha;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('alpha', 'number between 0 and 1', alpha);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.options.ensureAlpha = 1;
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,13 +72,17 @@ function ensureAlpha () {
|
|||||||
* Extract a single channel from a multi-channel image.
|
* Extract a single channel from a multi-channel image.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* sharp(input)
|
* // green.jpg is a greyscale image containing the green channel of the input
|
||||||
|
* await sharp(input)
|
||||||
* .extractChannel('green')
|
* .extractChannel('green')
|
||||||
* .toColourspace('b-w')
|
* .toFile('green.jpg');
|
||||||
* .toFile('green.jpg', function(err, info) {
|
*
|
||||||
* // info.channels === 1
|
* @example
|
||||||
* // green.jpg is a greyscale image containing the green channel of the input
|
* // red1 is the red value of the first pixel, red2 the second pixel etc.
|
||||||
* });
|
* const [red1, red2, ...] = await sharp(input)
|
||||||
|
* .extractChannel(0)
|
||||||
|
* .raw()
|
||||||
|
* .toBuffer();
|
||||||
*
|
*
|
||||||
* @param {number|string} channel - zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`.
|
* @param {number|string} channel - zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`.
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
@@ -74,7 +98,7 @@ function extractChannel (channel) {
|
|||||||
} else {
|
} else {
|
||||||
throw is.invalidParameterError('channel', 'integer or one of: red, green, blue, alpha', channel);
|
throw is.invalidParameterError('channel', 'integer or one of: red, green, blue, alpha', channel);
|
||||||
}
|
}
|
||||||
return this;
|
return this.toColourspace('b-w');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -54,6 +54,45 @@ function grayscale (grayscale) {
|
|||||||
return this.greyscale(grayscale);
|
return this.greyscale(grayscale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the pipeline colourspace.
|
||||||
|
*
|
||||||
|
* The input image will be converted to the provided colourspace at the start of the pipeline.
|
||||||
|
* All operations will use this colourspace before converting to the output colourspace, as defined by {@link toColourspace}.
|
||||||
|
*
|
||||||
|
* This feature is experimental and has not yet been fully-tested with all operations.
|
||||||
|
*
|
||||||
|
* @since 0.29.0
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Run pipeline in 16 bits per channel RGB while converting final result to 8 bits per channel sRGB.
|
||||||
|
* await sharp(input)
|
||||||
|
* .pipelineColourspace('rgb16')
|
||||||
|
* .toColourspace('srgb')
|
||||||
|
* .toFile('16bpc-pipeline-to-8bpc-output.png')
|
||||||
|
*
|
||||||
|
* @param {string} [colourspace] - pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://github.com/libvips/libvips/blob/41cff4e9d0838498487a00623462204eb10ee5b8/libvips/iofuncs/enumtypes.c#L774)
|
||||||
|
* @returns {Sharp}
|
||||||
|
* @throws {Error} Invalid parameters
|
||||||
|
*/
|
||||||
|
function pipelineColourspace (colourspace) {
|
||||||
|
if (!is.string(colourspace)) {
|
||||||
|
throw is.invalidParameterError('colourspace', 'string', colourspace);
|
||||||
|
}
|
||||||
|
this.options.colourspaceInput = colourspace;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternative spelling of `pipelineColourspace`.
|
||||||
|
* @param {string} [colorspace] - pipeline colorspace.
|
||||||
|
* @returns {Sharp}
|
||||||
|
* @throws {Error} Invalid parameters
|
||||||
|
*/
|
||||||
|
function pipelineColorspace (colorspace) {
|
||||||
|
return this.pipelineColourspace(colorspace);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the output colourspace.
|
* Set the output colourspace.
|
||||||
* By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
* By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
||||||
@@ -64,7 +103,7 @@ function grayscale (grayscale) {
|
|||||||
* .toColourspace('rgb16')
|
* .toColourspace('rgb16')
|
||||||
* .toFile('16-bpp.png')
|
* .toFile('16-bpp.png')
|
||||||
*
|
*
|
||||||
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
|
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/3c0bfdf74ce1dc37a6429bed47fa76f16e2cd70a/libvips/iofuncs/enumtypes.c#L777-L794)
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
@@ -119,6 +158,8 @@ module.exports = function (Sharp) {
|
|||||||
tint,
|
tint,
|
||||||
greyscale,
|
greyscale,
|
||||||
grayscale,
|
grayscale,
|
||||||
|
pipelineColourspace,
|
||||||
|
pipelineColorspace,
|
||||||
toColourspace,
|
toColourspace,
|
||||||
toColorspace,
|
toColorspace,
|
||||||
// Private
|
// Private
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ const blend = {
|
|||||||
* @param {Number} [images[].raw.width]
|
* @param {Number} [images[].raw.width]
|
||||||
* @param {Number} [images[].raw.height]
|
* @param {Number} [images[].raw.height]
|
||||||
* @param {Number} [images[].raw.channels]
|
* @param {Number} [images[].raw.channels]
|
||||||
|
* @param {boolean} [images[].failOnError=true] - @see {@link /api-constructor#parameters|constructor parameters}
|
||||||
|
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,32 +5,7 @@ const stream = require('stream');
|
|||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
|
|
||||||
require('./libvips').hasVendoredLibvips();
|
require('./libvips').hasVendoredLibvips();
|
||||||
|
require('./sharp');
|
||||||
/* istanbul ignore next */
|
|
||||||
try {
|
|
||||||
require('../build/Release/sharp.node');
|
|
||||||
} catch (err) {
|
|
||||||
// Bail early if bindings aren't available
|
|
||||||
const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, ''];
|
|
||||||
if (/NODE_MODULE_VERSION/.test(err.message)) {
|
|
||||||
help.push('- Ensure the version of Node.js used at install time matches that used at runtime');
|
|
||||||
} else if (/invalid ELF header/.test(err.message)) {
|
|
||||||
help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
|
|
||||||
} else if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
|
|
||||||
help.push('- Run "brew update && brew upgrade vips"');
|
|
||||||
} else {
|
|
||||||
help.push(
|
|
||||||
'- Remove the "node_modules/sharp" directory then run',
|
|
||||||
' "npm install --ignore-scripts=false --verbose sharp" and look for errors'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
help.push(
|
|
||||||
'- Consult the installation documentation at https://sharp.pixelplumbing.com/install',
|
|
||||||
'- Search for this error at https://github.com/lovell/sharp/issues', ''
|
|
||||||
);
|
|
||||||
const error = help.join('\n');
|
|
||||||
throw new Error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use NODE_DEBUG=sharp to enable libvips warnings
|
// Use NODE_DEBUG=sharp to enable libvips warnings
|
||||||
const debuglog = util.debuglog('sharp');
|
const debuglog = util.debuglog('sharp');
|
||||||
@@ -117,8 +92,9 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* }
|
* }
|
||||||
* }).toFile('noise.png');
|
* }).toFile('noise.png');
|
||||||
*
|
*
|
||||||
* @param {(Buffer|Uint8Array|Uint8ClampedArray|string)} [input] - if present, can be
|
* @param {(Buffer|Uint8Array|Uint8ClampedArray|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array|Float32Array|Float64Array|string)} [input] - if present, can be
|
||||||
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
|
||||||
|
* a TypedArray containing raw pixel image data, or
|
||||||
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
||||||
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||||
* @param {Object} [options] - if present, is an Object with optional attributes.
|
* @param {Object} [options] - if present, is an Object with optional attributes.
|
||||||
@@ -132,12 +108,15 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
|
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
|
||||||
* @param {number} [options.pages=1] - number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages.
|
* @param {number} [options.pages=1] - number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages.
|
||||||
* @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based.
|
* @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based.
|
||||||
|
* @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
|
||||||
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
||||||
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
|
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
|
||||||
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||||
* @param {number} [options.raw.width] - integral number of pixels wide.
|
* @param {number} [options.raw.width] - integral number of pixels wide.
|
||||||
* @param {number} [options.raw.height] - integral number of pixels high.
|
* @param {number} [options.raw.height] - integral number of pixels high.
|
||||||
* @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
|
* @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
|
||||||
|
* @param {boolean} [options.raw.premultiplied] - specifies that the raw input has already been premultiplied, set to `true`
|
||||||
|
* to avoid sharp premultiplying the image. (optional, default `false`)
|
||||||
* @param {Object} [options.create] - describes a new image to be created.
|
* @param {Object} [options.create] - describes a new image to be created.
|
||||||
* @param {number} [options.create.width] - integral number of pixels wide.
|
* @param {number} [options.create.width] - integral number of pixels wide.
|
||||||
* @param {number} [options.create.height] - integral number of pixels high.
|
* @param {number} [options.create.height] - integral number of pixels high.
|
||||||
@@ -201,6 +180,7 @@ const Sharp = function (input, options) {
|
|||||||
flatten: false,
|
flatten: false,
|
||||||
flattenBackground: [0, 0, 0],
|
flattenBackground: [0, 0, 0],
|
||||||
negate: false,
|
negate: false,
|
||||||
|
negateAlpha: true,
|
||||||
medianSize: 0,
|
medianSize: 0,
|
||||||
blurSigma: 0,
|
blurSigma: 0,
|
||||||
sharpenSigma: 0,
|
sharpenSigma: 0,
|
||||||
@@ -213,6 +193,9 @@ const Sharp = function (input, options) {
|
|||||||
gammaOut: 0,
|
gammaOut: 0,
|
||||||
greyscale: false,
|
greyscale: false,
|
||||||
normalise: false,
|
normalise: false,
|
||||||
|
claheWidth: 0,
|
||||||
|
claheHeight: 0,
|
||||||
|
claheMaxSlope: 3,
|
||||||
brightness: 1,
|
brightness: 1,
|
||||||
saturation: 1,
|
saturation: 1,
|
||||||
hue: 0,
|
hue: 0,
|
||||||
@@ -221,8 +204,9 @@ const Sharp = function (input, options) {
|
|||||||
joinChannelIn: [],
|
joinChannelIn: [],
|
||||||
extractChannel: -1,
|
extractChannel: -1,
|
||||||
removeAlpha: false,
|
removeAlpha: false,
|
||||||
ensureAlpha: false,
|
ensureAlpha: -1,
|
||||||
colourspace: 'srgb',
|
colourspace: 'srgb',
|
||||||
|
colourspaceInput: 'last',
|
||||||
composite: [],
|
composite: [],
|
||||||
// output
|
// output
|
||||||
fileOut: '',
|
fileOut: '',
|
||||||
@@ -230,7 +214,9 @@ const Sharp = function (input, options) {
|
|||||||
streamOut: false,
|
streamOut: false,
|
||||||
withMetadata: false,
|
withMetadata: false,
|
||||||
withMetadataOrientation: -1,
|
withMetadataOrientation: -1,
|
||||||
|
withMetadataDensity: 0,
|
||||||
withMetadataIcc: '',
|
withMetadataIcc: '',
|
||||||
|
withMetadataStrs: {},
|
||||||
resolveWithObject: false,
|
resolveWithObject: false,
|
||||||
// output format
|
// output format
|
||||||
jpegQuality: 80,
|
jpegQuality: 80,
|
||||||
@@ -268,7 +254,8 @@ const Sharp = function (input, options) {
|
|||||||
heifLossless: false,
|
heifLossless: false,
|
||||||
heifCompression: 'av1',
|
heifCompression: 'av1',
|
||||||
heifSpeed: 5,
|
heifSpeed: 5,
|
||||||
heifChromaSubsampling: '4:2:0',
|
heifChromaSubsampling: '4:4:4',
|
||||||
|
rawDepth: 'uchar',
|
||||||
tileSize: 256,
|
tileSize: 256,
|
||||||
tileOverlap: 0,
|
tileOverlap: 0,
|
||||||
tileContainer: 'fs',
|
tileContainer: 'fs',
|
||||||
|
|||||||
70
lib/input.js
@@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
const color = require('color');
|
const color = require('color');
|
||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
const sharp = require('../build/Release/sharp.node');
|
const sharp = require('./sharp');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract input options, if any, from an object.
|
* Extract input options, if any, from an object.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function _inputOptionsFromObject (obj) {
|
function _inputOptionsFromObject (obj) {
|
||||||
const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages } = obj;
|
const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd } = obj;
|
||||||
return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages].some(is.defined)
|
return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd].some(is.defined)
|
||||||
? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages }
|
? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages, subifd }
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +30,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
inputDescriptor.file = input;
|
inputDescriptor.file = input;
|
||||||
} else if (is.buffer(input)) {
|
} else if (is.buffer(input)) {
|
||||||
// Buffer
|
// Buffer
|
||||||
|
if (input.length === 0) {
|
||||||
|
throw Error('Input Buffer is empty');
|
||||||
|
}
|
||||||
inputDescriptor.buffer = input;
|
inputDescriptor.buffer = input;
|
||||||
} else if (is.uint8Array(input)) {
|
} else if (is.typedArray(input)) {
|
||||||
// Uint8Array or Uint8ClampedArray
|
if (input.length === 0) {
|
||||||
|
throw Error('Input Bit Array is empty');
|
||||||
|
}
|
||||||
inputDescriptor.buffer = Buffer.from(input.buffer);
|
inputDescriptor.buffer = Buffer.from(input.buffer);
|
||||||
} 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
|
||||||
@@ -97,6 +102,38 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
inputDescriptor.rawWidth = inputOptions.raw.width;
|
inputDescriptor.rawWidth = inputOptions.raw.width;
|
||||||
inputDescriptor.rawHeight = inputOptions.raw.height;
|
inputDescriptor.rawHeight = inputOptions.raw.height;
|
||||||
inputDescriptor.rawChannels = inputOptions.raw.channels;
|
inputDescriptor.rawChannels = inputOptions.raw.channels;
|
||||||
|
inputDescriptor.rawPremultiplied = !!inputOptions.raw.premultiplied;
|
||||||
|
|
||||||
|
switch (input.constructor) {
|
||||||
|
case Uint8Array:
|
||||||
|
case Uint8ClampedArray:
|
||||||
|
inputDescriptor.rawDepth = 'uchar';
|
||||||
|
break;
|
||||||
|
case Int8Array:
|
||||||
|
inputDescriptor.rawDepth = 'char';
|
||||||
|
break;
|
||||||
|
case Uint16Array:
|
||||||
|
inputDescriptor.rawDepth = 'ushort';
|
||||||
|
break;
|
||||||
|
case Int16Array:
|
||||||
|
inputDescriptor.rawDepth = 'short';
|
||||||
|
break;
|
||||||
|
case Uint32Array:
|
||||||
|
inputDescriptor.rawDepth = 'uint';
|
||||||
|
break;
|
||||||
|
case Int32Array:
|
||||||
|
inputDescriptor.rawDepth = 'int';
|
||||||
|
break;
|
||||||
|
case Float32Array:
|
||||||
|
inputDescriptor.rawDepth = 'float';
|
||||||
|
break;
|
||||||
|
case Float64Array:
|
||||||
|
inputDescriptor.rawDepth = 'double';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
inputDescriptor.rawDepth = 'uchar';
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Expected width, height and channels for raw pixel input');
|
throw new Error('Expected width, height and channels for raw pixel input');
|
||||||
}
|
}
|
||||||
@@ -131,6 +168,14 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
|
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Sub Image File Directory (TIFF)
|
||||||
|
if (is.defined(inputOptions.subifd)) {
|
||||||
|
if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
|
||||||
|
inputDescriptor.subifd = inputOptions.subifd;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Create new image
|
// Create new image
|
||||||
if (is.defined(inputOptions.create)) {
|
if (is.defined(inputOptions.create)) {
|
||||||
if (
|
if (
|
||||||
@@ -255,6 +300,9 @@ function _isStreamInput () {
|
|||||||
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
||||||
* - `pagePrimary`: Number of the primary page in a HEIF image
|
* - `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
* - `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
|
* - `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
|
||||||
|
* - `subifds`: Number of Sub Image File Directories in an OME-TIFF image
|
||||||
|
* - `background`: Default background colour, if present, for PNG (bKGD) and GIF images, either an RGB Object or a single greyscale value
|
||||||
|
* - `compression`: The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC)
|
||||||
* - `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
|
||||||
@@ -340,6 +388,9 @@ function metadata (callback) {
|
|||||||
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
|
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental)
|
||||||
* - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
|
* - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental)
|
||||||
*
|
*
|
||||||
|
* **Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
|
||||||
|
* written to a buffer in order to run `stats` on the result (see third example).
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* const image = sharp(inputJpg);
|
* const image = sharp(inputJpg);
|
||||||
* image
|
* image
|
||||||
@@ -352,6 +403,13 @@ function metadata (callback) {
|
|||||||
* const { entropy, sharpness, dominant } = await sharp(input).stats();
|
* const { entropy, sharpness, dominant } = await sharp(input).stats();
|
||||||
* const { r, g, b } = dominant;
|
* const { r, g, b } = dominant;
|
||||||
*
|
*
|
||||||
|
* @example
|
||||||
|
* const image = sharp(input);
|
||||||
|
* // store intermediate result
|
||||||
|
* const part = await image.extract(region).toBuffer();
|
||||||
|
* // create new instance to obtain statistics of extracted region
|
||||||
|
* const stats = await sharp(part).stats();
|
||||||
|
*
|
||||||
* @param {Function} [callback] - called with the arguments `(err, stats)`
|
* @param {Function} [callback] - called with the arguments `(err, stats)`
|
||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
|
|||||||
24
lib/is.js
@@ -49,12 +49,26 @@ const buffer = function (val) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this value a Uint8Array or Uint8ClampedArray object?
|
* Is this value a typed array object?. E.g. Uint8Array or Uint8ClampedArray?
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
const uint8Array = function (val) {
|
const typedArray = function (val) {
|
||||||
// allow both since Uint8ClampedArray simply clamps the values between 0-255
|
if (defined(val)) {
|
||||||
return val instanceof Uint8Array || val instanceof Uint8ClampedArray;
|
switch (val.constructor) {
|
||||||
|
case Uint8Array:
|
||||||
|
case Uint8ClampedArray:
|
||||||
|
case Int8Array:
|
||||||
|
case Uint16Array:
|
||||||
|
case Int16Array:
|
||||||
|
case Uint32Array:
|
||||||
|
case Int32Array:
|
||||||
|
case Float32Array:
|
||||||
|
case Float64Array:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,7 +133,7 @@ module.exports = {
|
|||||||
fn: fn,
|
fn: fn,
|
||||||
bool: bool,
|
bool: bool,
|
||||||
buffer: buffer,
|
buffer: buffer,
|
||||||
uint8Array: uint8Array,
|
typedArray: typedArray,
|
||||||
string: string,
|
string: string,
|
||||||
number: number,
|
number: number,
|
||||||
integer: integer,
|
integer: integer,
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ const fs = require('fs');
|
|||||||
const os = require('os');
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const spawnSync = require('child_process').spawnSync;
|
const spawnSync = require('child_process').spawnSync;
|
||||||
const semver = require('semver');
|
const semverCoerce = require('semver/functions/coerce');
|
||||||
|
const semverGreaterThanOrEqualTo = require('semver/functions/gte');
|
||||||
|
|
||||||
const platform = require('./platform');
|
const platform = require('./platform');
|
||||||
|
|
||||||
const env = process.env;
|
const env = process.env;
|
||||||
const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */
|
const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */
|
||||||
require('../package.json').config.libvips;
|
require('../package.json').config.libvips;
|
||||||
const minimumLibvipsVersion = semver.coerce(minimumLibvipsVersionLabelled).version;
|
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
|
||||||
|
|
||||||
const spawnSyncOptions = {
|
const spawnSyncOptions = {
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
@@ -19,9 +21,9 @@ const spawnSyncOptions = {
|
|||||||
|
|
||||||
const mkdirSync = function (dirPath) {
|
const mkdirSync = function (dirPath) {
|
||||||
try {
|
try {
|
||||||
fs.mkdirSync(dirPath);
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore next */
|
||||||
if (err.code !== 'EEXIST') {
|
if (err.code !== 'EEXIST') {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -39,7 +41,7 @@ const cachePath = function () {
|
|||||||
|
|
||||||
const log = function (item) {
|
const log = function (item) {
|
||||||
if (item instanceof Error) {
|
if (item instanceof Error) {
|
||||||
console.error(`sharp: ${item.message}`);
|
console.error(`sharp: Installation error: ${item.message}`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`sharp: ${item}`);
|
console.log(`sharp: ${item}`);
|
||||||
}
|
}
|
||||||
@@ -65,23 +67,8 @@ const globalLibvipsVersion = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const hasVendoredLibvips = function () {
|
const hasVendoredLibvips = function () {
|
||||||
const currentPlatformId = platform();
|
const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
|
||||||
const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion);
|
return fs.existsSync(vendorPath);
|
||||||
let vendorPlatformId;
|
|
||||||
try {
|
|
||||||
vendorPlatformId = require(path.join(vendorPath, 'platform.json'));
|
|
||||||
} catch (err) {}
|
|
||||||
/* istanbul ignore else */
|
|
||||||
if (vendorPlatformId) {
|
|
||||||
/* istanbul ignore else */
|
|
||||||
if (currentPlatformId === vendorPlatformId) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp' directory and run 'npm install' on the '${currentPlatformId}' platform.`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const pkgConfigPath = function () {
|
const pkgConfigPath = function () {
|
||||||
@@ -105,7 +92,7 @@ const useGlobalLibvips = function () {
|
|||||||
}
|
}
|
||||||
const globalVipsVersion = globalLibvipsVersion();
|
const globalVipsVersion = globalLibvipsVersion();
|
||||||
return !!globalVipsVersion && /* istanbul ignore next */
|
return !!globalVipsVersion && /* istanbul ignore next */
|
||||||
semver.gte(globalVipsVersion, minimumLibvipsVersion);
|
semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -270,9 +270,11 @@ function blur (sigma) {
|
|||||||
/**
|
/**
|
||||||
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
||||||
*
|
*
|
||||||
|
* See also {@link /api-channel#removealpha|removeAlpha}.
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* await sharp(rgbaInput)
|
* await sharp(rgbaInput)
|
||||||
* .flatten('#F0A703')
|
* .flatten({ background: '#F0A703' })
|
||||||
* .toBuffer();
|
* .toBuffer();
|
||||||
*
|
*
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
@@ -323,11 +325,19 @@ function gamma (gamma, gammaOut) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce the "negative" of the image.
|
* Produce the "negative" of the image.
|
||||||
* @param {Boolean} [negate=true]
|
* @param {Object} [options]
|
||||||
|
* @param {Boolean} [options.alpha=true] Whether or not to negate any alpha channel
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
*/
|
*/
|
||||||
function negate (negate) {
|
function negate (options) {
|
||||||
this.options.negate = is.bool(negate) ? negate : true;
|
this.options.negate = is.bool(options) ? options : true;
|
||||||
|
if (is.plainObject(options) && 'alpha' in options) {
|
||||||
|
if (!is.bool(options.alpha)) {
|
||||||
|
throw is.invalidParameterError('alpha', 'should be boolean value', options.alpha);
|
||||||
|
} else {
|
||||||
|
this.options.negateAlpha = options.alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,6 +360,47 @@ function normalize (normalize) {
|
|||||||
return this.normalise(normalize);
|
return this.normalise(normalize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform contrast limiting adaptive histogram equalization
|
||||||
|
* {@link https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE|CLAHE}.
|
||||||
|
*
|
||||||
|
* This will, in general, enhance the clarity of the image by bringing out darker details.
|
||||||
|
*
|
||||||
|
* @since 0.28.3
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {number} options.width - integer width of the region in pixels.
|
||||||
|
* @param {number} options.height - integer height of the region in pixels.
|
||||||
|
* @param {number} [options.maxSlope=3] - maximum value for the slope of the
|
||||||
|
* cumulative histogram. A value of 0 disables contrast limiting. Valid values
|
||||||
|
* are integers in the range 0-100 (inclusive)
|
||||||
|
* @returns {Sharp}
|
||||||
|
* @throws {Error} Invalid parameters
|
||||||
|
*/
|
||||||
|
function clahe (options) {
|
||||||
|
if (!is.plainObject(options)) {
|
||||||
|
throw is.invalidParameterError('options', 'plain object', options);
|
||||||
|
}
|
||||||
|
if (!('width' in options) || !is.integer(options.width) || options.width <= 0) {
|
||||||
|
throw is.invalidParameterError('width', 'integer above zero', options.width);
|
||||||
|
} else {
|
||||||
|
this.options.claheWidth = options.width;
|
||||||
|
}
|
||||||
|
if (!('height' in options) || !is.integer(options.height) || options.height <= 0) {
|
||||||
|
throw is.invalidParameterError('height', 'integer above zero', options.height);
|
||||||
|
} else {
|
||||||
|
this.options.claheHeight = options.height;
|
||||||
|
}
|
||||||
|
if (!is.defined(options.maxSlope)) {
|
||||||
|
this.options.claheMaxSlope = 3;
|
||||||
|
} else if (!is.integer(options.maxSlope) || options.maxSlope < 0 || options.maxSlope > 100) {
|
||||||
|
throw is.invalidParameterError('maxSlope', 'integer 0-100', options.maxSlope);
|
||||||
|
} else {
|
||||||
|
this.options.claheMaxSlope = options.maxSlope;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convolve the image with the specified kernel.
|
* Convolve the image with the specified kernel.
|
||||||
*
|
*
|
||||||
@@ -368,7 +419,7 @@ function normalize (normalize) {
|
|||||||
*
|
*
|
||||||
* @param {Object} kernel
|
* @param {Object} kernel
|
||||||
* @param {number} kernel.width - width of the kernel in pixels.
|
* @param {number} kernel.width - width of the kernel in pixels.
|
||||||
* @param {number} kernel.height - width of the kernel in pixels.
|
* @param {number} kernel.height - height of the kernel in pixels.
|
||||||
* @param {Array<number>} kernel.kernel - Array of length `width*height` containing the kernel values.
|
* @param {Array<number>} kernel.kernel - Array of length `width*height` containing the kernel values.
|
||||||
* @param {number} [kernel.scale=sum] - the scale of the kernel in pixels.
|
* @param {number} [kernel.scale=sum] - the scale of the kernel in pixels.
|
||||||
* @param {number} [kernel.offset=0] - the offset of the kernel in pixels.
|
* @param {number} [kernel.offset=0] - the offset of the kernel in pixels.
|
||||||
@@ -594,6 +645,7 @@ module.exports = function (Sharp) {
|
|||||||
negate,
|
negate,
|
||||||
normalise,
|
normalise,
|
||||||
normalize,
|
normalize,
|
||||||
|
clahe,
|
||||||
convolve,
|
convolve,
|
||||||
threshold,
|
threshold,
|
||||||
boolean,
|
boolean,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
const sharp = require('../build/Release/sharp.node');
|
const sharp = require('./sharp');
|
||||||
|
|
||||||
const formats = new Map([
|
const formats = new Map([
|
||||||
['heic', 'heif'],
|
['heic', 'heif'],
|
||||||
@@ -148,9 +148,29 @@ function toBuffer (options, callback) {
|
|||||||
* .toFile('output-with-metadata.jpg')
|
* .toFile('output-with-metadata.jpg')
|
||||||
* .then(info => { ... });
|
* .then(info => { ... });
|
||||||
*
|
*
|
||||||
|
* @example
|
||||||
|
* // Set "IFD0-Copyright" in output EXIF metadata
|
||||||
|
* const data = await sharp(input)
|
||||||
|
* .withMetadata({
|
||||||
|
* exif: {
|
||||||
|
* IFD0: {
|
||||||
|
* Copyright: 'Wernham Hogg'
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* .toBuffer();
|
||||||
|
*
|
||||||
|
* * @example
|
||||||
|
* // Set output metadata to 96 DPI
|
||||||
|
* const data = await sharp(input)
|
||||||
|
* .withMetadata({ density: 96 })
|
||||||
|
* .toBuffer();
|
||||||
|
*
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {number} [options.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.
|
||||||
* @param {string} [options.icc] filesystem path to output ICC profile, defaults to sRGB.
|
* @param {string} [options.icc] filesystem path to output ICC profile, defaults to sRGB.
|
||||||
|
* @param {Object<Object>} [options.exif={}] Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
|
||||||
|
* @param {number} [options.density] Number of pixels per inch (DPI).
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
@@ -164,6 +184,13 @@ function withMetadata (options) {
|
|||||||
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
|
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is.defined(options.density)) {
|
||||||
|
if (is.number(options.density) && options.density > 0) {
|
||||||
|
this.options.withMetadataDensity = options.density;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('density', 'positive number', options.density);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (is.defined(options.icc)) {
|
if (is.defined(options.icc)) {
|
||||||
if (is.string(options.icc)) {
|
if (is.string(options.icc)) {
|
||||||
this.options.withMetadataIcc = options.icc;
|
this.options.withMetadataIcc = options.icc;
|
||||||
@@ -171,6 +198,25 @@ function withMetadata (options) {
|
|||||||
throw is.invalidParameterError('icc', 'string filesystem path to ICC profile', options.icc);
|
throw is.invalidParameterError('icc', 'string filesystem path to ICC profile', options.icc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is.defined(options.exif)) {
|
||||||
|
if (is.object(options.exif)) {
|
||||||
|
for (const [ifd, entries] of Object.entries(options.exif)) {
|
||||||
|
if (is.object(entries)) {
|
||||||
|
for (const [k, v] of Object.entries(entries)) {
|
||||||
|
if (is.string(v)) {
|
||||||
|
this.options.withMetadataStrs[`exif-${ifd.toLowerCase()}-${k}`] = v;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError(`exif.${ifd}.${k}`, 'string', v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError(`exif.${ifd}`, 'object', entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('exif', 'object', options.exif);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -383,6 +429,12 @@ function png (options) {
|
|||||||
* .webp({ lossless: true })
|
* .webp({ lossless: true })
|
||||||
* .toBuffer();
|
* .toBuffer();
|
||||||
*
|
*
|
||||||
|
* @example
|
||||||
|
* // Optimise the file size of an animated WebP
|
||||||
|
* const outputWebp = await sharp(inputWebp, { animated: true })
|
||||||
|
* .webp({ reductionEffort: 6 })
|
||||||
|
* .toBuffer();
|
||||||
|
*
|
||||||
* @param {Object} [options] - output options
|
* @param {Object} [options] - output options
|
||||||
* @param {number} [options.quality=80] - quality, integer 1-100
|
* @param {number} [options.quality=80] - quality, integer 1-100
|
||||||
* @param {number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
|
* @param {number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
|
||||||
@@ -608,7 +660,7 @@ function tiff (options) {
|
|||||||
* @param {number} [options.quality=50] - quality, integer 1-100
|
* @param {number} [options.quality=50] - quality, integer 1-100
|
||||||
* @param {boolean} [options.lossless=false] - use lossless compression
|
* @param {boolean} [options.lossless=false] - use lossless compression
|
||||||
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
|
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
|
||||||
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
|
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
*/
|
*/
|
||||||
@@ -629,7 +681,7 @@ function avif (options) {
|
|||||||
* @param {string} [options.compression='av1'] - compression format: av1, hevc
|
* @param {string} [options.compression='av1'] - compression format: av1, hevc
|
||||||
* @param {boolean} [options.lossless=false] - use lossless compression
|
* @param {boolean} [options.lossless=false] - use lossless compression
|
||||||
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
|
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
|
||||||
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
|
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
*/
|
*/
|
||||||
@@ -675,28 +727,41 @@ function heif (options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force output to be raw, uncompressed, 8-bit unsigned integer (unit8) pixel data.
|
* Force output to be raw, uncompressed pixel data.
|
||||||
* Pixel ordering is left-to-right, top-to-bottom, without padding.
|
* Pixel ordering is left-to-right, top-to-bottom, without padding.
|
||||||
* Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
|
* Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Extract raw RGB pixel data from JPEG input
|
* // Extract raw, unsigned 8-bit RGB pixel data from JPEG input
|
||||||
* const { data, info } = await sharp('input.jpg')
|
* const { data, info } = await sharp('input.jpg')
|
||||||
* .raw()
|
* .raw()
|
||||||
* .toBuffer({ resolveWithObject: true });
|
* .toBuffer({ resolveWithObject: true });
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Extract alpha channel as raw pixel data from PNG input
|
* // Extract alpha channel as raw, unsigned 16-bit pixel data from PNG input
|
||||||
* const data = await sharp('input.png')
|
* const data = await sharp('input.png')
|
||||||
* .ensureAlpha()
|
* .ensureAlpha()
|
||||||
* .extractChannel(3)
|
* .extractChannel(3)
|
||||||
* .toColourspace('b-w')
|
* .toColourspace('b-w')
|
||||||
* .raw()
|
* .raw({ depth: 'ushort' })
|
||||||
* .toBuffer();
|
* .toBuffer();
|
||||||
*
|
*
|
||||||
* @returns {Sharp}
|
* @param {Object} [options] - output options
|
||||||
|
* @param {string} [options.depth='uchar'] - bit depth, one of: char, uchar (default), short, ushort, int, uint, float, complex, double, dpcomplex
|
||||||
|
* @throws {Error} Invalid options
|
||||||
*/
|
*/
|
||||||
function raw () {
|
function raw (options) {
|
||||||
|
if (is.object(options)) {
|
||||||
|
if (is.defined(options.depth)) {
|
||||||
|
if (is.string(options.depth) && is.inArray(options.depth,
|
||||||
|
['char', 'uchar', 'short', 'ushort', 'int', 'uint', 'float', 'complex', 'double', 'dpcomplex']
|
||||||
|
)) {
|
||||||
|
this.options.rawDepth = options.depth;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('depth', 'one of: char, uchar, short, ushort, int, uint, float, complex, double, dpcomplex', options.depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return this._updateFormatOut('raw');
|
return this._updateFormatOut('raw');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
24
lib/sharp.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const platformAndArch = require('./platform')();
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
try {
|
||||||
|
module.exports = require(`../build/Release/sharp-${platformAndArch}.node`);
|
||||||
|
} catch (err) {
|
||||||
|
// Bail early if bindings aren't available
|
||||||
|
const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, '', 'Possible solutions:'];
|
||||||
|
if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
|
||||||
|
help.push('- Update Homebrew: "brew update && brew upgrade vips"');
|
||||||
|
} else {
|
||||||
|
help.push(
|
||||||
|
'- Install with the --verbose flag and look for errors: "npm install --ignore-scripts=false --verbose sharp"',
|
||||||
|
`- Install for the current runtime: "npm install --platform=${process.platform} --arch=${process.arch} sharp"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
help.push(
|
||||||
|
'- Consult the installation documentation: https://sharp.pixelplumbing.com/install'
|
||||||
|
);
|
||||||
|
console.error(help.join('\n'));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ const events = require('events');
|
|||||||
const detectLibc = require('detect-libc');
|
const detectLibc = require('detect-libc');
|
||||||
|
|
||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
const sharp = require('../build/Release/sharp.node');
|
const sharp = require('./sharp');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An Object containing nested boolean values representing the available input and output formats/methods.
|
* An Object containing nested boolean values representing the available input and output formats/methods.
|
||||||
|
|||||||
37
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, AVIF and TIFF images",
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images",
|
||||||
"version": "0.28.0",
|
"version": "0.29.0",
|
||||||
"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": [
|
||||||
@@ -74,12 +74,17 @@
|
|||||||
"Christian Flintrup <chr@gigahost.dk>",
|
"Christian Flintrup <chr@gigahost.dk>",
|
||||||
"Manan Jadhav <manan@motionden.com>",
|
"Manan Jadhav <manan@motionden.com>",
|
||||||
"Leon Radley <leon@radley.se>",
|
"Leon Radley <leon@radley.se>",
|
||||||
"alza54 <alza54@thiocod.in>"
|
"alza54 <alza54@thiocod.in>",
|
||||||
|
"Jacob Smith <jacob@frende.me>",
|
||||||
|
"Michael Nutt <michael@nutt.im>",
|
||||||
|
"Brad Parham <baparham@gmail.com>",
|
||||||
|
"Taneli Vatanen <taneli.vatanen@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 install/can-compile && node-gyp rebuild && node install/dll-copy)",
|
||||||
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
|
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
|
||||||
"test": "semistandard && cpplint && npm run test-unit && npm run test-licensing",
|
"test": "npm run test-lint && npm run test-unit && npm run test-licensing",
|
||||||
|
"test-lint": "semistandard && cpplint",
|
||||||
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=1000 --timeout=60000 ./test/unit/*.js",
|
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=1000 --timeout=60000 ./test/unit/*.js",
|
||||||
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
|
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
|
||||||
"test-coverage": "./test/coverage/report.sh",
|
"test-coverage": "./test/coverage/report.sh",
|
||||||
@@ -117,45 +122,45 @@
|
|||||||
"vips"
|
"vips"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^3.1.3",
|
"color": "^4.0.1",
|
||||||
"detect-libc": "^1.0.3",
|
"detect-libc": "^1.0.3",
|
||||||
"node-addon-api": "^3.1.0",
|
"node-addon-api": "^4.0.0",
|
||||||
"prebuild-install": "^6.0.1",
|
"prebuild-install": "^6.1.4",
|
||||||
"semver": "^7.3.5",
|
"semver": "^7.3.5",
|
||||||
"simple-get": "^3.1.0",
|
"simple-get": "^3.1.0",
|
||||||
"tar-fs": "^2.1.1",
|
"tar-fs": "^2.1.1",
|
||||||
"tunnel-agent": "^0.6.0"
|
"tunnel-agent": "^0.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"async": "^3.2.0",
|
"async": "^3.2.1",
|
||||||
"cc": "^3.0.1",
|
"cc": "^3.0.1",
|
||||||
"decompress-zip": "^0.3.3",
|
"decompress-zip": "^0.3.3",
|
||||||
"documentation": "^13.2.0",
|
"documentation": "^13.2.5",
|
||||||
"exif-reader": "^1.0.3",
|
"exif-reader": "^1.0.3",
|
||||||
"icc": "^2.0.0",
|
"icc": "^2.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^8.3.2",
|
"mocha": "^9.0.3",
|
||||||
"mock-fs": "^4.13.0",
|
"mock-fs": "^5.0.0",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"prebuild": "^10.0.1",
|
"prebuild": "^10.0.1",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"semistandard": "^16.0.0"
|
"semistandard": "^16.0.1"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"config": {
|
"config": {
|
||||||
"libvips": "8.10.6",
|
"libvips": "8.11.3",
|
||||||
"runtime": "napi",
|
"runtime": "napi",
|
||||||
"target": 3
|
"target": 5
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=12.13.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"binary": {
|
"binary": {
|
||||||
"napi_versions": [
|
"napi_versions": [
|
||||||
3
|
5
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"semistandard": {
|
"semistandard": {
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ namespace sharp {
|
|||||||
std::string AttrAsStr(Napi::Object obj, std::string attr) {
|
std::string AttrAsStr(Napi::Object obj, std::string attr) {
|
||||||
return obj.Get(attr).As<Napi::String>();
|
return obj.Get(attr).As<Napi::String>();
|
||||||
}
|
}
|
||||||
|
std::string AttrAsStr(Napi::Object obj, unsigned int const attr) {
|
||||||
|
return obj.Get(attr).As<Napi::String>();
|
||||||
|
}
|
||||||
uint32_t AttrAsUint32(Napi::Object obj, std::string attr) {
|
uint32_t AttrAsUint32(Napi::Object obj, std::string attr) {
|
||||||
return obj.Get(attr).As<Napi::Number>().Uint32Value();
|
return obj.Get(attr).As<Napi::Number>().Uint32Value();
|
||||||
}
|
}
|
||||||
@@ -89,9 +92,13 @@ namespace sharp {
|
|||||||
}
|
}
|
||||||
// Raw pixel input
|
// Raw pixel input
|
||||||
if (HasAttr(input, "rawChannels")) {
|
if (HasAttr(input, "rawChannels")) {
|
||||||
|
descriptor->rawDepth = static_cast<VipsBandFormat>(
|
||||||
|
vips_enum_from_nick(nullptr, VIPS_TYPE_BAND_FORMAT,
|
||||||
|
AttrAsStr(input, "rawDepth").data()));
|
||||||
descriptor->rawChannels = AttrAsUint32(input, "rawChannels");
|
descriptor->rawChannels = AttrAsUint32(input, "rawChannels");
|
||||||
descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
|
descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
|
||||||
descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
|
descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
|
||||||
|
descriptor->rawPremultiplied = AttrAsBool(input, "rawPremultiplied");
|
||||||
}
|
}
|
||||||
// Multi-page input (GIF, TIFF, PDF)
|
// Multi-page input (GIF, TIFF, PDF)
|
||||||
if (HasAttr(input, "pages")) {
|
if (HasAttr(input, "pages")) {
|
||||||
@@ -104,6 +111,10 @@ namespace sharp {
|
|||||||
if (HasAttr(input, "level")) {
|
if (HasAttr(input, "level")) {
|
||||||
descriptor->level = AttrAsUint32(input, "level");
|
descriptor->level = AttrAsUint32(input, "level");
|
||||||
}
|
}
|
||||||
|
// subIFD (OME-TIFF)
|
||||||
|
if (HasAttr(input, "subifd")) {
|
||||||
|
descriptor->subifd = AttrAsInt32(input, "subifd");
|
||||||
|
}
|
||||||
// Create new image
|
// Create new image
|
||||||
if (HasAttr(input, "createChannels")) {
|
if (HasAttr(input, "createChannels")) {
|
||||||
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
||||||
@@ -228,6 +239,7 @@ namespace sharp {
|
|||||||
{ "VipsForeignLoadFits", ImageType::FITS },
|
{ "VipsForeignLoadFits", ImageType::FITS },
|
||||||
{ "VipsForeignLoadOpenexr", ImageType::EXR },
|
{ "VipsForeignLoadOpenexr", ImageType::EXR },
|
||||||
{ "VipsForeignLoadVips", ImageType::VIPS },
|
{ "VipsForeignLoadVips", ImageType::VIPS },
|
||||||
|
{ "VipsForeignLoadVipsFile", ImageType::VIPS },
|
||||||
{ "VipsForeignLoadRaw", ImageType::RAW }
|
{ "VipsForeignLoadRaw", ImageType::RAW }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -288,12 +300,15 @@ namespace sharp {
|
|||||||
if (descriptor->rawChannels > 0) {
|
if (descriptor->rawChannels > 0) {
|
||||||
// Raw, uncompressed pixel data
|
// Raw, uncompressed pixel data
|
||||||
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
|
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
|
||||||
descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, VIPS_FORMAT_UCHAR);
|
descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, descriptor->rawDepth);
|
||||||
if (descriptor->rawChannels < 3) {
|
if (descriptor->rawChannels < 3) {
|
||||||
image.get_image()->Type = VIPS_INTERPRETATION_B_W;
|
image.get_image()->Type = VIPS_INTERPRETATION_B_W;
|
||||||
} else {
|
} else {
|
||||||
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
|
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
|
||||||
}
|
}
|
||||||
|
if (descriptor->rawPremultiplied) {
|
||||||
|
image = image.unpremultiply();
|
||||||
|
}
|
||||||
imageType = ImageType::RAW;
|
imageType = ImageType::RAW;
|
||||||
} else {
|
} else {
|
||||||
// Compressed data
|
// Compressed data
|
||||||
@@ -319,6 +334,9 @@ namespace sharp {
|
|||||||
if (imageType == ImageType::OPENSLIDE) {
|
if (imageType == ImageType::OPENSLIDE) {
|
||||||
option->set("level", descriptor->level);
|
option->set("level", descriptor->level);
|
||||||
}
|
}
|
||||||
|
if (imageType == ImageType::TIFF) {
|
||||||
|
option->set("subifd", descriptor->subifd);
|
||||||
|
}
|
||||||
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
image = SetDensity(image, descriptor->density);
|
image = SetDensity(image, descriptor->density);
|
||||||
@@ -391,6 +409,9 @@ namespace sharp {
|
|||||||
if (imageType == ImageType::OPENSLIDE) {
|
if (imageType == ImageType::OPENSLIDE) {
|
||||||
option->set("level", descriptor->level);
|
option->set("level", descriptor->level);
|
||||||
}
|
}
|
||||||
|
if (imageType == ImageType::TIFF) {
|
||||||
|
option->set("subifd", descriptor->subifd);
|
||||||
|
}
|
||||||
image = VImage::new_from_file(descriptor->file.data(), option);
|
image = VImage::new_from_file(descriptor->file.data(), option);
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
image = SetDensity(image, descriptor->density);
|
image = SetDensity(image, descriptor->density);
|
||||||
@@ -507,9 +528,8 @@ namespace sharp {
|
|||||||
VImage SetDensity(VImage image, const double density) {
|
VImage SetDensity(VImage image, const double density) {
|
||||||
const double pixelsPerMm = density / 25.4;
|
const double pixelsPerMm = density / 25.4;
|
||||||
VImage copy = image.copy();
|
VImage copy = image.copy();
|
||||||
copy.set("Xres", pixelsPerMm);
|
copy.get_image()->Xres = pixelsPerMm;
|
||||||
copy.set("Yres", pixelsPerMm);
|
copy.get_image()->Yres = pixelsPerMm;
|
||||||
copy.set(VIPS_META_RESOLUTION_UNIT, "in");
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -799,10 +819,10 @@ namespace sharp {
|
|||||||
/*
|
/*
|
||||||
Ensures alpha channel, if missing.
|
Ensures alpha channel, if missing.
|
||||||
*/
|
*/
|
||||||
VImage EnsureAlpha(VImage image) {
|
VImage EnsureAlpha(VImage image, double const value) {
|
||||||
if (!HasAlpha(image)) {
|
if (!HasAlpha(image)) {
|
||||||
std::vector<double> alpha;
|
std::vector<double> alpha;
|
||||||
alpha.push_back(sharp::MaximumImageAlpha(image.interpretation()));
|
alpha.push_back(value * sharp::MaximumImageAlpha(image.interpretation()));
|
||||||
image = image.bandjoin_const(alpha);
|
image = image.bandjoin_const(alpha);
|
||||||
}
|
}
|
||||||
return image;
|
return image;
|
||||||
|
|||||||
15
src/common.h
@@ -25,9 +25,9 @@
|
|||||||
// Verify platform and compiler compatibility
|
// Verify platform and compiler compatibility
|
||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION < 8) || \
|
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10) || \
|
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 11) || \
|
||||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 10 && VIPS_MICRO_VERSION < 6)
|
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 11 && VIPS_MICRO_VERSION < 3)
|
||||||
#error "libvips version 8.10.6+ is required - please see https://sharp.pixelplumbing.com/install"
|
#error "libvips version 8.11.3+ is required - please see https://sharp.pixelplumbing.com/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)))
|
||||||
@@ -54,12 +54,15 @@ namespace sharp {
|
|||||||
size_t bufferLength;
|
size_t bufferLength;
|
||||||
bool isBuffer;
|
bool isBuffer;
|
||||||
double density;
|
double density;
|
||||||
|
VipsBandFormat rawDepth;
|
||||||
int rawChannels;
|
int rawChannels;
|
||||||
int rawWidth;
|
int rawWidth;
|
||||||
int rawHeight;
|
int rawHeight;
|
||||||
|
bool rawPremultiplied;
|
||||||
int pages;
|
int pages;
|
||||||
int page;
|
int page;
|
||||||
int level;
|
int level;
|
||||||
|
int subifd;
|
||||||
int createChannels;
|
int createChannels;
|
||||||
int createWidth;
|
int createWidth;
|
||||||
int createHeight;
|
int createHeight;
|
||||||
@@ -76,12 +79,15 @@ namespace sharp {
|
|||||||
bufferLength(0),
|
bufferLength(0),
|
||||||
isBuffer(FALSE),
|
isBuffer(FALSE),
|
||||||
density(72.0),
|
density(72.0),
|
||||||
|
rawDepth(VIPS_FORMAT_UCHAR),
|
||||||
rawChannels(0),
|
rawChannels(0),
|
||||||
rawWidth(0),
|
rawWidth(0),
|
||||||
rawHeight(0),
|
rawHeight(0),
|
||||||
|
rawPremultiplied(false),
|
||||||
pages(1),
|
pages(1),
|
||||||
page(0),
|
page(0),
|
||||||
level(0),
|
level(0),
|
||||||
|
subifd(-1),
|
||||||
createChannels(0),
|
createChannels(0),
|
||||||
createWidth(0),
|
createWidth(0),
|
||||||
createHeight(0),
|
createHeight(0),
|
||||||
@@ -93,6 +99,7 @@ namespace sharp {
|
|||||||
// Convenience methods to access the attributes of a Napi::Object
|
// Convenience methods to access the attributes of a Napi::Object
|
||||||
bool HasAttr(Napi::Object obj, std::string attr);
|
bool HasAttr(Napi::Object obj, std::string attr);
|
||||||
std::string AttrAsStr(Napi::Object obj, std::string attr);
|
std::string AttrAsStr(Napi::Object obj, std::string attr);
|
||||||
|
std::string AttrAsStr(Napi::Object obj, unsigned int const attr);
|
||||||
uint32_t AttrAsUint32(Napi::Object obj, std::string attr);
|
uint32_t AttrAsUint32(Napi::Object obj, std::string attr);
|
||||||
int32_t AttrAsInt32(Napi::Object obj, std::string attr);
|
int32_t AttrAsInt32(Napi::Object obj, std::string attr);
|
||||||
int32_t AttrAsInt32(Napi::Object obj, unsigned int const attr);
|
int32_t AttrAsInt32(Napi::Object obj, unsigned int const attr);
|
||||||
@@ -296,7 +303,7 @@ namespace sharp {
|
|||||||
/*
|
/*
|
||||||
Ensures alpha channel, if missing.
|
Ensures alpha channel, if missing.
|
||||||
*/
|
*/
|
||||||
VImage EnsureAlpha(VImage image);
|
VImage EnsureAlpha(VImage image, double const value);
|
||||||
|
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
|
||||||
|
|||||||
@@ -110,19 +110,6 @@ VSource::new_from_options( const char *options )
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
VOption *
|
|
||||||
VOption::set( const char *name, const VSource value )
|
|
||||||
{
|
|
||||||
Pair *pair = new Pair( name );
|
|
||||||
|
|
||||||
pair->input = true;
|
|
||||||
g_value_init( &pair->value, VIPS_TYPE_SOURCE );
|
|
||||||
g_value_set_object( &pair->value, value.get_source() );
|
|
||||||
options.push_back( pair );
|
|
||||||
|
|
||||||
return( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
VTarget
|
VTarget
|
||||||
VTarget::new_to_descriptor( int descriptor )
|
VTarget::new_to_descriptor( int descriptor )
|
||||||
{
|
{
|
||||||
@@ -162,17 +149,4 @@ VTarget::new_to_memory()
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
VOption *
|
|
||||||
VOption::set( const char *name, const VTarget value )
|
|
||||||
{
|
|
||||||
Pair *pair = new Pair( name );
|
|
||||||
|
|
||||||
pair->input = true;
|
|
||||||
g_value_init( &pair->value, VIPS_TYPE_TARGET );
|
|
||||||
g_value_set_object( &pair->value, value.get_target() );
|
|
||||||
options.push_back( pair );
|
|
||||||
|
|
||||||
return( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_END
|
VIPS_NAMESPACE_END
|
||||||
|
|||||||
@@ -51,6 +51,12 @@
|
|||||||
|
|
||||||
VIPS_NAMESPACE_START
|
VIPS_NAMESPACE_START
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \namespace vips
|
||||||
|
*
|
||||||
|
* General docs for the vips namespace.
|
||||||
|
*/
|
||||||
|
|
||||||
std::vector<double>
|
std::vector<double>
|
||||||
to_vectorv( int n, ... )
|
to_vectorv( int n, ... )
|
||||||
{
|
{
|
||||||
@@ -140,6 +146,20 @@ VOption::set( const char *name, int value )
|
|||||||
return( this );
|
return( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// input guint64
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, guint64 value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
g_value_init( &pair->value, G_TYPE_UINT64 );
|
||||||
|
g_value_set_uint64( &pair->value, value );
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
// input double
|
// input double
|
||||||
VOption *
|
VOption *
|
||||||
VOption::set( const char *name, double value )
|
VOption::set( const char *name, double value )
|
||||||
@@ -167,39 +187,17 @@ VOption::set( const char *name, const char *value )
|
|||||||
return( this );
|
return( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
// input image
|
// input vips object (image, source, target, etc. etc.)
|
||||||
VOption *
|
VOption *
|
||||||
VOption::set( const char *name, const VImage value )
|
VOption::set( const char *name, const VObject value )
|
||||||
{
|
{
|
||||||
Pair *pair = new Pair( name );
|
Pair *pair = new Pair( name );
|
||||||
|
VipsObject *object = value.get_object();
|
||||||
|
GType type = G_OBJECT_TYPE( object );
|
||||||
|
|
||||||
pair->input = true;
|
pair->input = true;
|
||||||
g_value_init( &pair->value, VIPS_TYPE_IMAGE );
|
g_value_init( &pair->value, type );
|
||||||
g_value_set_object( &pair->value, value.get_image() );
|
g_value_set_object( &pair->value, object );
|
||||||
options.push_back( pair );
|
|
||||||
|
|
||||||
return( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
// input double array
|
|
||||||
VOption *
|
|
||||||
VOption::set( const char *name, std::vector<double> value )
|
|
||||||
{
|
|
||||||
Pair *pair = new Pair( name );
|
|
||||||
|
|
||||||
double *array;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
pair->input = true;
|
|
||||||
|
|
||||||
g_value_init( &pair->value, VIPS_TYPE_ARRAY_DOUBLE );
|
|
||||||
vips_value_set_array_double( &pair->value, NULL,
|
|
||||||
static_cast< int >( value.size() ) );
|
|
||||||
array = vips_value_get_array_double( &pair->value, NULL );
|
|
||||||
|
|
||||||
for( i = 0; i < value.size(); i++ )
|
|
||||||
array[i] = value[i];
|
|
||||||
|
|
||||||
options.push_back( pair );
|
options.push_back( pair );
|
||||||
|
|
||||||
return( this );
|
return( this );
|
||||||
@@ -229,6 +227,30 @@ VOption::set( const char *name, std::vector<int> value )
|
|||||||
return( this );
|
return( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// input double array
|
||||||
|
VOption *
|
||||||
|
VOption::set( const char *name, std::vector<double> value )
|
||||||
|
{
|
||||||
|
Pair *pair = new Pair( name );
|
||||||
|
|
||||||
|
double *array;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
pair->input = true;
|
||||||
|
|
||||||
|
g_value_init( &pair->value, VIPS_TYPE_ARRAY_DOUBLE );
|
||||||
|
vips_value_set_array_double( &pair->value, NULL,
|
||||||
|
static_cast< int >( value.size() ) );
|
||||||
|
array = vips_value_get_array_double( &pair->value, NULL );
|
||||||
|
|
||||||
|
for( i = 0; i < value.size(); i++ )
|
||||||
|
array[i] = value[i];
|
||||||
|
|
||||||
|
options.push_back( pair );
|
||||||
|
|
||||||
|
return( this );
|
||||||
|
}
|
||||||
|
|
||||||
// input image array
|
// input image array
|
||||||
VOption *
|
VOption *
|
||||||
VOption::set( const char *name, std::vector<VImage> value )
|
VOption::set( const char *name, std::vector<VImage> value )
|
||||||
@@ -619,6 +641,22 @@ VImage::new_from_source( VSource source, const char *option_string,
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VImage
|
||||||
|
VImage::new_from_memory_steal( void *data, size_t size,
|
||||||
|
int width, int height, int bands, VipsBandFormat format )
|
||||||
|
{
|
||||||
|
VipsImage *image;
|
||||||
|
|
||||||
|
if( !(image = vips_image_new_from_memory( data, size,
|
||||||
|
width, height, bands, format )) )
|
||||||
|
throw( VError() );
|
||||||
|
|
||||||
|
g_signal_connect( image, "postclose",
|
||||||
|
G_CALLBACK( vips_image_free_buffer ), data);
|
||||||
|
|
||||||
|
return( VImage( image ) );
|
||||||
|
}
|
||||||
|
|
||||||
VImage
|
VImage
|
||||||
VImage::new_matrix( int width, int height )
|
VImage::new_matrix( int width, int height )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,17 +60,4 @@ VInterpolate::new_from_name( const char *name, VOption *options )
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
VOption *
|
|
||||||
VOption::set( const char *name, const VInterpolate value )
|
|
||||||
{
|
|
||||||
Pair *pair = new Pair( name );
|
|
||||||
|
|
||||||
pair->input = true;
|
|
||||||
g_value_init( &pair->value, VIPS_TYPE_INTERPOLATE );
|
|
||||||
g_value_set_object( &pair->value, value.get_interpolate() );
|
|
||||||
options.push_back( pair );
|
|
||||||
|
|
||||||
return( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_END
|
VIPS_NAMESPACE_END
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// bodies for vips operations
|
// bodies for vips operations
|
||||||
// Sun 5 Jul 22:36:37 BST 2020
|
// Wed May 12 11:30:00 AM CEST 2021
|
||||||
// this file is generated automatically, do not edit!
|
// this file is generated automatically, do not edit!
|
||||||
|
|
||||||
VImage VImage::CMC2LCh( VOption *options ) const
|
VImage VImage::CMC2LCh( VOption *options ) const
|
||||||
@@ -1065,6 +1065,18 @@ VImage VImage::fitsload( const char *filename, VOption *options )
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VImage VImage::fitsload_source( VSource source, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "fitsload_source",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "source", source ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
void VImage::fitssave( const char *filename, VOption *options ) const
|
void VImage::fitssave( const char *filename, VOption *options ) const
|
||||||
{
|
{
|
||||||
call( "fitssave",
|
call( "fitssave",
|
||||||
@@ -1656,6 +1668,70 @@ VImage VImage::join( VImage in2, VipsDirection direction, VOption *options ) con
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VImage VImage::jp2kload( const char *filename, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "jp2kload",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "filename", filename ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage VImage::jp2kload_buffer( VipsBlob *buffer, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "jp2kload_buffer",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "buffer", buffer ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage VImage::jp2kload_source( VSource source, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "jp2kload_source",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "source", source ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
void VImage::jp2ksave( const char *filename, VOption *options ) const
|
||||||
|
{
|
||||||
|
call( "jp2ksave",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "in", *this )->
|
||||||
|
set( "filename", filename ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
VipsBlob *VImage::jp2ksave_buffer( VOption *options ) const
|
||||||
|
{
|
||||||
|
VipsBlob *buffer;
|
||||||
|
|
||||||
|
call( "jp2ksave_buffer",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "in", *this )->
|
||||||
|
set( "buffer", &buffer ) );
|
||||||
|
|
||||||
|
return( buffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
void VImage::jp2ksave_target( VTarget target, VOption *options ) const
|
||||||
|
{
|
||||||
|
call( "jp2ksave_target",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "in", *this )->
|
||||||
|
set( "target", target ) );
|
||||||
|
}
|
||||||
|
|
||||||
VImage VImage::jpegload( const char *filename, VOption *options )
|
VImage VImage::jpegload( const char *filename, VOption *options )
|
||||||
{
|
{
|
||||||
VImage out;
|
VImage out;
|
||||||
@@ -1727,6 +1803,70 @@ void VImage::jpegsave_target( VTarget target, VOption *options ) const
|
|||||||
set( "target", target ) );
|
set( "target", target ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VImage VImage::jxlload( const char *filename, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "jxlload",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "filename", filename ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage VImage::jxlload_buffer( VipsBlob *buffer, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "jxlload_buffer",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "buffer", buffer ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
VImage VImage::jxlload_source( VSource source, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "jxlload_source",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "source", source ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
void VImage::jxlsave( const char *filename, VOption *options ) const
|
||||||
|
{
|
||||||
|
call( "jxlsave",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "in", *this )->
|
||||||
|
set( "filename", filename ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
VipsBlob *VImage::jxlsave_buffer( VOption *options ) const
|
||||||
|
{
|
||||||
|
VipsBlob *buffer;
|
||||||
|
|
||||||
|
call( "jxlsave_buffer",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "in", *this )->
|
||||||
|
set( "buffer", &buffer ) );
|
||||||
|
|
||||||
|
return( buffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
void VImage::jxlsave_target( VTarget target, VOption *options ) const
|
||||||
|
{
|
||||||
|
call( "jxlsave_target",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "in", *this )->
|
||||||
|
set( "target", target ) );
|
||||||
|
}
|
||||||
|
|
||||||
VImage VImage::labelregions( VOption *options ) const
|
VImage VImage::labelregions( VOption *options ) const
|
||||||
{
|
{
|
||||||
VImage mask;
|
VImage mask;
|
||||||
@@ -2284,6 +2424,18 @@ VImage VImage::niftiload( const char *filename, VOption *options )
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VImage VImage::niftiload_source( VSource source, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "niftiload_source",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "source", source ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
void VImage::niftisave( const char *filename, VOption *options ) const
|
void VImage::niftisave( const char *filename, VOption *options ) const
|
||||||
{
|
{
|
||||||
call( "niftisave",
|
call( "niftisave",
|
||||||
@@ -2316,6 +2468,18 @@ VImage VImage::openslideload( const char *filename, VOption *options )
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VImage VImage::openslideload_source( VSource source, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "openslideload_source",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "source", source ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
VImage VImage::pdfload( const char *filename, VOption *options )
|
VImage VImage::pdfload( const char *filename, VOption *options )
|
||||||
{
|
{
|
||||||
VImage out;
|
VImage out;
|
||||||
@@ -3388,6 +3552,18 @@ VImage VImage::vipsload( const char *filename, VOption *options )
|
|||||||
return( out );
|
return( out );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VImage VImage::vipsload_source( VSource source, VOption *options )
|
||||||
|
{
|
||||||
|
VImage out;
|
||||||
|
|
||||||
|
call( "vipsload_source",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "out", &out )->
|
||||||
|
set( "source", source ) );
|
||||||
|
|
||||||
|
return( out );
|
||||||
|
}
|
||||||
|
|
||||||
void VImage::vipssave( const char *filename, VOption *options ) const
|
void VImage::vipssave( const char *filename, VOption *options ) const
|
||||||
{
|
{
|
||||||
call( "vipssave",
|
call( "vipssave",
|
||||||
@@ -3396,6 +3572,14 @@ void VImage::vipssave( const char *filename, VOption *options ) const
|
|||||||
set( "filename", filename ) );
|
set( "filename", filename ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VImage::vipssave_target( VTarget target, VOption *options ) const
|
||||||
|
{
|
||||||
|
call( "vipssave_target",
|
||||||
|
(options ? options : VImage::option())->
|
||||||
|
set( "in", *this )->
|
||||||
|
set( "target", target ) );
|
||||||
|
}
|
||||||
|
|
||||||
VImage VImage::webpload( const char *filename, VOption *options )
|
VImage VImage::webpload( const char *filename, VOption *options )
|
||||||
{
|
{
|
||||||
VImage out;
|
VImage out;
|
||||||
|
|||||||
@@ -86,7 +86,13 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|||||||
baton->levels.push_back(std::pair<int, int>(width, height));
|
baton->levels.push_back(std::pair<int, int>(width, height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (image.get_typeof(VIPS_META_N_SUBIFDS) == G_TYPE_INT) {
|
||||||
|
baton->subifds = image.get_int(VIPS_META_N_SUBIFDS);
|
||||||
|
}
|
||||||
baton->hasProfile = sharp::HasProfile(image);
|
baton->hasProfile = sharp::HasProfile(image);
|
||||||
|
if (image.get_typeof("background") == VIPS_TYPE_ARRAY_DOUBLE) {
|
||||||
|
baton->background = image.get_array_double("background");
|
||||||
|
}
|
||||||
// Derived attributes
|
// Derived attributes
|
||||||
baton->hasAlpha = sharp::HasAlpha(image);
|
baton->hasAlpha = sharp::HasAlpha(image);
|
||||||
baton->orientation = sharp::ExifOrientation(image);
|
baton->orientation = sharp::ExifOrientation(image);
|
||||||
@@ -203,6 +209,20 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|||||||
}
|
}
|
||||||
info.Set("levels", levels);
|
info.Set("levels", levels);
|
||||||
}
|
}
|
||||||
|
if (baton->subifds > 0) {
|
||||||
|
info.Set("subifds", baton->subifds);
|
||||||
|
}
|
||||||
|
if (!baton->background.empty()) {
|
||||||
|
if (baton->background.size() == 3) {
|
||||||
|
Napi::Object background = Napi::Object::New(env);
|
||||||
|
background.Set("r", baton->background[0]);
|
||||||
|
background.Set("g", baton->background[1]);
|
||||||
|
background.Set("b", baton->background[2]);
|
||||||
|
info.Set("background", background);
|
||||||
|
} else {
|
||||||
|
info.Set("background", baton->background[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
info.Set("hasProfile", baton->hasProfile);
|
info.Set("hasProfile", baton->hasProfile);
|
||||||
info.Set("hasAlpha", baton->hasAlpha);
|
info.Set("hasAlpha", baton->hasAlpha);
|
||||||
if (baton->orientation > 0) {
|
if (baton->orientation > 0) {
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ struct MetadataBaton {
|
|||||||
int pagePrimary;
|
int pagePrimary;
|
||||||
std::string compression;
|
std::string compression;
|
||||||
std::vector<std::pair<int, int>> levels;
|
std::vector<std::pair<int, int>> levels;
|
||||||
|
int subifds;
|
||||||
|
std::vector<double> background;
|
||||||
bool hasProfile;
|
bool hasProfile;
|
||||||
bool hasAlpha;
|
bool hasAlpha;
|
||||||
int orientation;
|
int orientation;
|
||||||
@@ -68,6 +70,7 @@ struct MetadataBaton {
|
|||||||
pageHeight(0),
|
pageHeight(0),
|
||||||
loop(-1),
|
loop(-1),
|
||||||
pagePrimary(-1),
|
pagePrimary(-1),
|
||||||
|
subifds(0),
|
||||||
hasProfile(false),
|
hasProfile(false),
|
||||||
hasAlpha(false),
|
hasAlpha(false),
|
||||||
orientation(0),
|
orientation(0),
|
||||||
|
|||||||
@@ -92,6 +92,13 @@ namespace sharp {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Contrast limiting adapative histogram equalization (CLAHE)
|
||||||
|
*/
|
||||||
|
VImage Clahe(VImage image, int const width, int const height, int const maxSlope) {
|
||||||
|
return image.hist_local(width, height, VImage::option()->set("max_slope", maxSlope));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gamma encoding/decoding
|
* Gamma encoding/decoding
|
||||||
*/
|
*/
|
||||||
@@ -105,6 +112,19 @@ namespace sharp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce the "negative" of the image.
|
||||||
|
*/
|
||||||
|
VImage Negate(VImage image, bool const negateAlpha) {
|
||||||
|
if (HasAlpha(image) && !negateAlpha) {
|
||||||
|
// Separate alpha channel
|
||||||
|
VImage alpha = image[image.bands() - 1];
|
||||||
|
return RemoveAlpha(image).invert().bandjoin(alpha);
|
||||||
|
} else {
|
||||||
|
return image.invert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
||||||
*/
|
*/
|
||||||
@@ -275,4 +295,16 @@ namespace sharp {
|
|||||||
return image.linear(a, b);
|
return image.linear(a, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the image is in a given colourspace
|
||||||
|
*/
|
||||||
|
VImage EnsureColourspace(VImage image, VipsInterpretation colourspace) {
|
||||||
|
if (colourspace != VIPS_INTERPRETATION_LAST && image.interpretation() != colourspace) {
|
||||||
|
image = image.colourspace(colourspace,
|
||||||
|
VImage::option()->set("source_space", image.interpretation()));
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
|||||||
@@ -35,11 +35,21 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
VImage Normalise(VImage image);
|
VImage Normalise(VImage image);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Contrast limiting adapative histogram equalization (CLAHE)
|
||||||
|
*/
|
||||||
|
VImage Clahe(VImage image, int const width, int const height, int const maxSlope);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gamma encoding/decoding
|
* Gamma encoding/decoding
|
||||||
*/
|
*/
|
||||||
VImage Gamma(VImage image, double const exponent);
|
VImage Gamma(VImage image, double const exponent);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Produce the "negative" of the image.
|
||||||
|
*/
|
||||||
|
VImage Negate(VImage image, bool const negateAlpha);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
||||||
*/
|
*/
|
||||||
@@ -92,6 +102,11 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
VImage Modulate(VImage image, double const brightness, double const saturation, int const hue);
|
VImage Modulate(VImage image, double const brightness, double const saturation, int const hue);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the image is in a given colourspace
|
||||||
|
*/
|
||||||
|
VImage EnsureColourspace(VImage image, VipsInterpretation colourspace);
|
||||||
|
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
|
||||||
#endif // SRC_OPERATIONS_H_
|
#endif // SRC_OPERATIONS_H_
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
vips::VImage image;
|
vips::VImage image;
|
||||||
sharp::ImageType inputImageType;
|
sharp::ImageType inputImageType;
|
||||||
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
|
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
|
||||||
|
image = sharp::EnsureColourspace(image, baton->colourspaceInput);
|
||||||
|
|
||||||
// Calculate angle of rotation
|
// Calculate angle of rotation
|
||||||
VipsAngle rotation;
|
VipsAngle rotation;
|
||||||
@@ -214,7 +215,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
double yresidual = static_cast<double>(yshrink) / yfactor;
|
double yresidual = static_cast<double>(yshrink) / yfactor;
|
||||||
|
|
||||||
// If integral x and y shrink are equal, try to use shrink-on-load for JPEG and WebP,
|
// If integral x and y shrink are equal, try to use shrink-on-load for JPEG and WebP,
|
||||||
// but not when applying gamma correction, pre-resize extract or trim
|
// but not when applying gamma correction, pre-resize extract, trim or input colourspace
|
||||||
int shrink_on_load = 1;
|
int shrink_on_load = 1;
|
||||||
|
|
||||||
int shrink_on_load_factor = 1;
|
int shrink_on_load_factor = 1;
|
||||||
@@ -226,7 +227,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
if (
|
if (
|
||||||
xshrink == yshrink && xshrink >= 2 * shrink_on_load_factor &&
|
xshrink == yshrink && xshrink >= 2 * shrink_on_load_factor &&
|
||||||
(inputImageType == sharp::ImageType::JPEG || inputImageType == sharp::ImageType::WEBP) &&
|
(inputImageType == sharp::ImageType::JPEG || inputImageType == sharp::ImageType::WEBP) &&
|
||||||
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0
|
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0 &&
|
||||||
|
baton->colourspaceInput == VIPS_INTERPRETATION_LAST &&
|
||||||
|
image.width() > 3 && image.height() > 3 && baton->input->pages == 1
|
||||||
) {
|
) {
|
||||||
if (xshrink >= 8 * shrink_on_load_factor) {
|
if (xshrink >= 8 * shrink_on_load_factor) {
|
||||||
xfactor = xfactor / 8;
|
xfactor = xfactor / 8;
|
||||||
@@ -324,7 +327,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
|
|
||||||
// Negate the colours in the image
|
// Negate the colours in the image
|
||||||
if (baton->negate) {
|
if (baton->negate) {
|
||||||
image = image.invert();
|
image = sharp::Negate(image, baton->negateAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gamma encoding (darken)
|
// Gamma encoding (darken)
|
||||||
@@ -344,9 +347,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
bool const shouldApplyMedian = baton->medianSize > 0;
|
bool const shouldApplyMedian = baton->medianSize > 0;
|
||||||
bool const shouldComposite = !baton->composite.empty();
|
bool const shouldComposite = !baton->composite.empty();
|
||||||
bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0;
|
bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0;
|
||||||
|
bool const shouldApplyClahe = baton->claheWidth != 0 && baton->claheHeight != 0;
|
||||||
|
|
||||||
if (shouldComposite && !sharp::HasAlpha(image)) {
|
if (shouldComposite && !sharp::HasAlpha(image)) {
|
||||||
image = sharp::EnsureAlpha(image);
|
image = sharp::EnsureAlpha(image, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
|
bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
|
||||||
@@ -409,6 +413,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
|
|
||||||
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
|
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
|
||||||
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
|
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
|
||||||
|
joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
|
||||||
image = image.bandjoin(joinImage);
|
image = image.bandjoin(joinImage);
|
||||||
}
|
}
|
||||||
image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
|
image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
|
||||||
@@ -550,7 +555,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
for (Composite *composite : baton->composite) {
|
for (Composite *composite : baton->composite) {
|
||||||
VImage compositeImage;
|
VImage compositeImage;
|
||||||
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
||||||
std::tie(compositeImage, compositeImageType) = OpenInput(composite->input);
|
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
||||||
|
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspaceInput);
|
||||||
// Verify within current dimensions
|
// Verify within current dimensions
|
||||||
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
|
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
|
||||||
throw vips::VError("Image to composite must have same dimensions or smaller");
|
throw vips::VError("Image to composite must have same dimensions or smaller");
|
||||||
@@ -594,7 +600,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Ensure image to composite is sRGB with premultiplied alpha
|
// Ensure image to composite is sRGB with premultiplied alpha
|
||||||
compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
|
compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
|
||||||
if (!sharp::HasAlpha(compositeImage)) {
|
if (!sharp::HasAlpha(compositeImage)) {
|
||||||
compositeImage = sharp::EnsureAlpha(compositeImage);
|
compositeImage = sharp::EnsureAlpha(compositeImage, 1);
|
||||||
}
|
}
|
||||||
if (!composite->premultiplied) compositeImage = compositeImage.premultiply();
|
if (!composite->premultiplied) compositeImage = compositeImage.premultiply();
|
||||||
// Calculate position
|
// Calculate position
|
||||||
@@ -649,11 +655,17 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
image = sharp::Normalise(image);
|
image = sharp::Normalise(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply contrast limiting adaptive histogram equalization (CLAHE)
|
||||||
|
if (shouldApplyClahe) {
|
||||||
|
image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
|
||||||
|
}
|
||||||
|
|
||||||
// Apply bitwise boolean operation between images
|
// Apply bitwise boolean operation between images
|
||||||
if (baton->boolean != nullptr) {
|
if (baton->boolean != nullptr) {
|
||||||
VImage booleanImage;
|
VImage booleanImage;
|
||||||
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
||||||
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
||||||
|
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
|
||||||
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,8 +703,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure alpha channel, if missing
|
// Ensure alpha channel, if missing
|
||||||
if (baton->ensureAlpha) {
|
if (baton->ensureAlpha != -1) {
|
||||||
image = sharp::EnsureAlpha(image);
|
image = sharp::EnsureAlpha(image, baton->ensureAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert image to sRGB, if not already
|
// Convert image to sRGB, if not already
|
||||||
@@ -717,11 +729,21 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("input_profile", "srgb")
|
->set("input_profile", "srgb")
|
||||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override EXIF Orientation tag
|
// Override EXIF Orientation tag
|
||||||
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
||||||
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
||||||
}
|
}
|
||||||
|
// Override pixel density
|
||||||
|
if (baton->withMetadataDensity > 0) {
|
||||||
|
image = sharp::SetDensity(image, baton->withMetadataDensity);
|
||||||
|
}
|
||||||
|
// Metadata key/value pairs, e.g. EXIF
|
||||||
|
if (!baton->withMetadataStrs.empty()) {
|
||||||
|
image = image.copy();
|
||||||
|
for (const auto& s : baton->withMetadataStrs) {
|
||||||
|
image.set(s.first.data(), s.second.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Number of channels used in output image
|
// Number of channels used in output image
|
||||||
baton->channels = image.bands();
|
baton->channels = image.bands();
|
||||||
@@ -748,8 +770,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("Q", baton->jpegQuality)
|
->set("Q", baton->jpegQuality)
|
||||||
->set("interlace", baton->jpegProgressive)
|
->set("interlace", baton->jpegProgressive)
|
||||||
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
||||||
? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
|
? VIPS_FOREIGN_SUBSAMPLE_OFF
|
||||||
: VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
|
: VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||||
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
||||||
->set("quant_table", baton->jpegQuantisationTable)
|
->set("quant_table", baton->jpegQuantisationTable)
|
||||||
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
||||||
@@ -848,13 +870,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Write HEIF to buffer
|
// Write HEIF to buffer
|
||||||
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
->set("compression", baton->heifCompression)
|
|
||||||
->set("Q", baton->heifQuality)
|
->set("Q", baton->heifQuality)
|
||||||
|
->set("compression", baton->heifCompression)
|
||||||
->set("speed", baton->heifSpeed)
|
->set("speed", baton->heifSpeed)
|
||||||
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
||||||
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
||||||
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||||
#endif
|
|
||||||
->set("lossless", baton->heifLossless)));
|
->set("lossless", baton->heifLossless)));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
baton->bufferOutLength = area->length;
|
baton->bufferOutLength = area->length;
|
||||||
@@ -869,9 +889,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
image = image[0];
|
image = image[0];
|
||||||
baton->channels = 1;
|
baton->channels = 1;
|
||||||
}
|
}
|
||||||
if (image.format() != VIPS_FORMAT_UCHAR) {
|
if (image.format() != baton->rawDepth) {
|
||||||
// Cast pixels to uint8 (unsigned char)
|
// Cast pixels to requested format
|
||||||
image = image.cast(VIPS_FORMAT_UCHAR);
|
image = image.cast(baton->rawDepth);
|
||||||
}
|
}
|
||||||
// Get raw image data
|
// Get raw image data
|
||||||
baton->bufferOut = static_cast<char*>(image.write_to_memory(&baton->bufferOutLength));
|
baton->bufferOut = static_cast<char*>(image.write_to_memory(&baton->bufferOutLength));
|
||||||
@@ -914,8 +934,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("Q", baton->jpegQuality)
|
->set("Q", baton->jpegQuality)
|
||||||
->set("interlace", baton->jpegProgressive)
|
->set("interlace", baton->jpegProgressive)
|
||||||
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
||||||
? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
|
? VIPS_FOREIGN_SUBSAMPLE_OFF
|
||||||
: VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
|
: VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||||
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
||||||
->set("quant_table", baton->jpegQuantisationTable)
|
->set("quant_table", baton->jpegQuantisationTable)
|
||||||
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
||||||
@@ -993,10 +1013,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("Q", baton->heifQuality)
|
->set("Q", baton->heifQuality)
|
||||||
->set("compression", baton->heifCompression)
|
->set("compression", baton->heifCompression)
|
||||||
->set("speed", baton->heifSpeed)
|
->set("speed", baton->heifSpeed)
|
||||||
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
||||||
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
||||||
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||||
#endif
|
|
||||||
->set("lossless", baton->heifLossless));
|
->set("lossless", baton->heifLossless));
|
||||||
baton->formatOut = "heif";
|
baton->formatOut = "heif";
|
||||||
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
||||||
@@ -1113,6 +1131,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
info.Set("width", static_cast<uint32_t>(width));
|
info.Set("width", static_cast<uint32_t>(width));
|
||||||
info.Set("height", static_cast<uint32_t>(height));
|
info.Set("height", static_cast<uint32_t>(height));
|
||||||
info.Set("channels", static_cast<uint32_t>(baton->channels));
|
info.Set("channels", static_cast<uint32_t>(baton->channels));
|
||||||
|
if (baton->formatOut == "raw") {
|
||||||
|
info.Set("depth", vips_enum_nick(VIPS_TYPE_BAND_FORMAT, baton->rawDepth));
|
||||||
|
}
|
||||||
info.Set("premultiplied", baton->premultiplied);
|
info.Set("premultiplied", baton->premultiplied);
|
||||||
if (baton->hasCropOffset) {
|
if (baton->hasCropOffset) {
|
||||||
info.Set("cropOffsetLeft", static_cast<int32_t>(baton->cropOffsetLeft));
|
info.Set("cropOffsetLeft", static_cast<int32_t>(baton->cropOffsetLeft));
|
||||||
@@ -1302,6 +1323,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->flatten = sharp::AttrAsBool(options, "flatten");
|
baton->flatten = sharp::AttrAsBool(options, "flatten");
|
||||||
baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
|
baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
|
||||||
baton->negate = sharp::AttrAsBool(options, "negate");
|
baton->negate = sharp::AttrAsBool(options, "negate");
|
||||||
|
baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
|
||||||
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
|
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
|
||||||
baton->brightness = sharp::AttrAsDouble(options, "brightness");
|
baton->brightness = sharp::AttrAsDouble(options, "brightness");
|
||||||
baton->saturation = sharp::AttrAsDouble(options, "saturation");
|
baton->saturation = sharp::AttrAsDouble(options, "saturation");
|
||||||
@@ -1319,6 +1341,9 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->linearB = sharp::AttrAsDouble(options, "linearB");
|
baton->linearB = sharp::AttrAsDouble(options, "linearB");
|
||||||
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
|
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
|
||||||
baton->normalise = sharp::AttrAsBool(options, "normalise");
|
baton->normalise = sharp::AttrAsBool(options, "normalise");
|
||||||
|
baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
|
||||||
|
baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
|
||||||
|
baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
|
||||||
baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
|
baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
|
||||||
baton->angle = sharp::AttrAsInt32(options, "angle");
|
baton->angle = sharp::AttrAsInt32(options, "angle");
|
||||||
baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
|
baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
|
||||||
@@ -1341,7 +1366,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->affineInterpolator = vips::VInterpolate::new_from_name(sharp::AttrAsStr(options, "affineInterpolator").data());
|
baton->affineInterpolator = vips::VInterpolate::new_from_name(sharp::AttrAsStr(options, "affineInterpolator").data());
|
||||||
|
|
||||||
baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
|
baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
|
||||||
baton->ensureAlpha = sharp::AttrAsBool(options, "ensureAlpha");
|
baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
|
||||||
if (options.Has("boolean")) {
|
if (options.Has("boolean")) {
|
||||||
baton->boolean = sharp::CreateInputDescriptor(options.Get("boolean").As<Napi::Object>());
|
baton->boolean = sharp::CreateInputDescriptor(options.Get("boolean").As<Napi::Object>());
|
||||||
baton->booleanOp = sharp::GetBooleanOperation(sharp::AttrAsStr(options, "booleanOp"));
|
baton->booleanOp = sharp::GetBooleanOperation(sharp::AttrAsStr(options, "booleanOp"));
|
||||||
@@ -1369,6 +1394,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
|
baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
baton->colourspaceInput = sharp::GetInterpretation(sharp::AttrAsStr(options, "colourspaceInput"));
|
||||||
|
if (baton->colourspaceInput == VIPS_INTERPRETATION_ERROR) {
|
||||||
|
baton->colourspaceInput = VIPS_INTERPRETATION_LAST;
|
||||||
|
}
|
||||||
baton->colourspace = sharp::GetInterpretation(sharp::AttrAsStr(options, "colourspace"));
|
baton->colourspace = sharp::GetInterpretation(sharp::AttrAsStr(options, "colourspace"));
|
||||||
if (baton->colourspace == VIPS_INTERPRETATION_ERROR) {
|
if (baton->colourspace == VIPS_INTERPRETATION_ERROR) {
|
||||||
baton->colourspace = VIPS_INTERPRETATION_sRGB;
|
baton->colourspace = VIPS_INTERPRETATION_sRGB;
|
||||||
@@ -1378,7 +1407,14 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
||||||
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
||||||
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
||||||
|
baton->withMetadataDensity = sharp::AttrAsDouble(options, "withMetadataDensity");
|
||||||
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
|
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
|
||||||
|
Napi::Object mdStrs = options.Get("withMetadataStrs").As<Napi::Object>();
|
||||||
|
Napi::Array mdStrKeys = mdStrs.GetPropertyNames();
|
||||||
|
for (unsigned int i = 0; i < mdStrKeys.Length(); i++) {
|
||||||
|
std::string k = sharp::AttrAsStr(mdStrKeys, i);
|
||||||
|
baton->withMetadataStrs.insert(std::make_pair(k, sharp::AttrAsStr(mdStrs, k)));
|
||||||
|
}
|
||||||
// Format-specific
|
// Format-specific
|
||||||
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
||||||
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
||||||
@@ -1424,6 +1460,11 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
|
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
|
||||||
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
||||||
|
|
||||||
|
// Raw output
|
||||||
|
baton->rawDepth = static_cast<VipsBandFormat>(
|
||||||
|
vips_enum_from_nick(nullptr, VIPS_TYPE_BAND_FORMAT,
|
||||||
|
sharp::AttrAsStr(options, "rawDepth").data()));
|
||||||
|
|
||||||
// Animated output
|
// Animated output
|
||||||
if (sharp::HasAttr(options, "pageHeight")) {
|
if (sharp::HasAttr(options, "pageHeight")) {
|
||||||
baton->pageHeight = sharp::AttrAsUint32(options, "pageHeight");
|
baton->pageHeight = sharp::AttrAsUint32(options, "pageHeight");
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <napi.h>
|
#include <napi.h>
|
||||||
#include <vips/vips8>
|
#include <vips/vips8>
|
||||||
@@ -89,6 +90,7 @@ struct PipelineBaton {
|
|||||||
bool flatten;
|
bool flatten;
|
||||||
std::vector<double> flattenBackground;
|
std::vector<double> flattenBackground;
|
||||||
bool negate;
|
bool negate;
|
||||||
|
bool negateAlpha;
|
||||||
double blurSigma;
|
double blurSigma;
|
||||||
double brightness;
|
double brightness;
|
||||||
double saturation;
|
double saturation;
|
||||||
@@ -108,6 +110,9 @@ struct PipelineBaton {
|
|||||||
double gammaOut;
|
double gammaOut;
|
||||||
bool greyscale;
|
bool greyscale;
|
||||||
bool normalise;
|
bool normalise;
|
||||||
|
int claheWidth;
|
||||||
|
int claheHeight;
|
||||||
|
int claheMaxSlope;
|
||||||
bool useExifOrientation;
|
bool useExifOrientation;
|
||||||
int angle;
|
int angle;
|
||||||
double rotationAngle;
|
double rotationAngle;
|
||||||
@@ -164,10 +169,13 @@ struct PipelineBaton {
|
|||||||
int heifSpeed;
|
int heifSpeed;
|
||||||
std::string heifChromaSubsampling;
|
std::string heifChromaSubsampling;
|
||||||
bool heifLossless;
|
bool heifLossless;
|
||||||
|
VipsBandFormat rawDepth;
|
||||||
std::string err;
|
std::string err;
|
||||||
bool withMetadata;
|
bool withMetadata;
|
||||||
int withMetadataOrientation;
|
int withMetadataOrientation;
|
||||||
|
double withMetadataDensity;
|
||||||
std::string withMetadataIcc;
|
std::string withMetadataIcc;
|
||||||
|
std::unordered_map<std::string, std::string> withMetadataStrs;
|
||||||
std::unique_ptr<double[]> convKernel;
|
std::unique_ptr<double[]> convKernel;
|
||||||
int convKernelWidth;
|
int convKernelWidth;
|
||||||
int convKernelHeight;
|
int convKernelHeight;
|
||||||
@@ -178,7 +186,8 @@ struct PipelineBaton {
|
|||||||
VipsOperationBoolean bandBoolOp;
|
VipsOperationBoolean bandBoolOp;
|
||||||
int extractChannel;
|
int extractChannel;
|
||||||
bool removeAlpha;
|
bool removeAlpha;
|
||||||
bool ensureAlpha;
|
double ensureAlpha;
|
||||||
|
VipsInterpretation colourspaceInput;
|
||||||
VipsInterpretation colourspace;
|
VipsInterpretation colourspace;
|
||||||
int pageHeight;
|
int pageHeight;
|
||||||
std::vector<int> delay;
|
std::vector<int> delay;
|
||||||
@@ -213,6 +222,7 @@ struct PipelineBaton {
|
|||||||
flatten(false),
|
flatten(false),
|
||||||
flattenBackground{ 0.0, 0.0, 0.0 },
|
flattenBackground{ 0.0, 0.0, 0.0 },
|
||||||
negate(false),
|
negate(false),
|
||||||
|
negateAlpha(true),
|
||||||
blurSigma(0.0),
|
blurSigma(0.0),
|
||||||
brightness(1.0),
|
brightness(1.0),
|
||||||
saturation(1.0),
|
saturation(1.0),
|
||||||
@@ -231,6 +241,9 @@ struct PipelineBaton {
|
|||||||
gamma(0.0),
|
gamma(0.0),
|
||||||
greyscale(false),
|
greyscale(false),
|
||||||
normalise(false),
|
normalise(false),
|
||||||
|
claheWidth(0),
|
||||||
|
claheHeight(0),
|
||||||
|
claheMaxSlope(3),
|
||||||
useExifOrientation(false),
|
useExifOrientation(false),
|
||||||
angle(0),
|
angle(0),
|
||||||
rotationAngle(0.0),
|
rotationAngle(0.0),
|
||||||
@@ -284,10 +297,12 @@ struct PipelineBaton {
|
|||||||
heifQuality(50),
|
heifQuality(50),
|
||||||
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
|
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
|
||||||
heifSpeed(5),
|
heifSpeed(5),
|
||||||
heifChromaSubsampling("4:2:0"),
|
heifChromaSubsampling("4:4:4"),
|
||||||
heifLossless(false),
|
heifLossless(false),
|
||||||
|
rawDepth(VIPS_FORMAT_UCHAR),
|
||||||
withMetadata(false),
|
withMetadata(false),
|
||||||
withMetadataOrientation(-1),
|
withMetadataOrientation(-1),
|
||||||
|
withMetadataDensity(0.0),
|
||||||
convKernelWidth(0),
|
convKernelWidth(0),
|
||||||
convKernelHeight(0),
|
convKernelHeight(0),
|
||||||
convKernelScale(0.0),
|
convKernelScale(0.0),
|
||||||
@@ -297,7 +312,8 @@ struct PipelineBaton {
|
|||||||
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
||||||
extractChannel(-1),
|
extractChannel(-1),
|
||||||
removeAlpha(false),
|
removeAlpha(false),
|
||||||
ensureAlpha(false),
|
ensureAlpha(-1.0),
|
||||||
|
colourspaceInput(VIPS_INTERPRETATION_LAST),
|
||||||
colourspace(VIPS_INTERPRETATION_LAST),
|
colourspace(VIPS_INTERPRETATION_LAST),
|
||||||
pageHeight(0),
|
pageHeight(0),
|
||||||
delay{-1},
|
delay{-1},
|
||||||
|
|||||||
@@ -8,16 +8,18 @@
|
|||||||
"test": "node perf && node random && node parallel"
|
"test": "node perf && node random && node parallel"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"async": "3.2.0",
|
"@squoosh/cli": "0.7.2",
|
||||||
|
"@squoosh/lib": "0.4.0",
|
||||||
|
"async": "3.2.1",
|
||||||
"benchmark": "2.1.4",
|
"benchmark": "2.1.4",
|
||||||
"gm": "1.23.1",
|
"gm": "1.23.1",
|
||||||
"imagemagick": "0.1.3",
|
"imagemagick": "0.1.3",
|
||||||
"jimp": "0.16.1",
|
"jimp": "0.16.1",
|
||||||
"mapnik": "4.5.6",
|
"mapnik": "4.5.8",
|
||||||
"semver": "7.3.4"
|
"semver": "7.3.5"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "14"
|
"node": "16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
@@ -13,6 +14,7 @@ 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');
|
||||||
|
const squoosh = require('@squoosh/lib');
|
||||||
|
|
||||||
const fixtures = require('../fixtures');
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
@@ -75,6 +77,65 @@ async.series({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// squoosh-cli
|
||||||
|
jpegSuite.add('squoosh-cli-file-file', {
|
||||||
|
defer: true,
|
||||||
|
fn: function (deferred) {
|
||||||
|
exec(`./node_modules/.bin/squoosh-cli \
|
||||||
|
--output-dir ${os.tmpdir()} \
|
||||||
|
--resize '{"enabled":true,"width":${width},"height":${height},"method":"lanczos3","premultiply":false,"linearRGB":false}' \
|
||||||
|
--mozjpeg '{"quality":80,"progressive":false,"optimize_coding":true,"quant_table":0,"trellis_multipass":false,"chroma_subsample":2,"separate_chroma_quality":false}' \
|
||||||
|
"${fixtures.inputJpg}"`, function (err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// squoosh-lib (GPLv3)
|
||||||
|
jpegSuite.add('squoosh-lib-buffer-buffer', {
|
||||||
|
defer: true,
|
||||||
|
fn: function (deferred) {
|
||||||
|
const pool = new squoosh.ImagePool();
|
||||||
|
const image = pool.ingestImage(inputJpgBuffer);
|
||||||
|
image.decoded
|
||||||
|
.then(function () {
|
||||||
|
return image.preprocess({
|
||||||
|
resize: {
|
||||||
|
enabled: true,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
method: 'lanczos3',
|
||||||
|
premultiply: false,
|
||||||
|
linearRGB: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return image.encode({
|
||||||
|
mozjpeg: {
|
||||||
|
quality: 80,
|
||||||
|
progressive: false,
|
||||||
|
optimize_coding: true,
|
||||||
|
quant_table: 0,
|
||||||
|
trellis_multipass: false,
|
||||||
|
chroma_subsample: 2,
|
||||||
|
separate_chroma_quality: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return pool.close();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return image.encodedWith.mozjpeg;
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
// mapnik
|
// mapnik
|
||||||
jpegSuite.add('mapnik-file-file', {
|
jpegSuite.add('mapnik-file-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
|
|||||||
BIN
test/fixtures/concert.jpg
vendored
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
test/fixtures/expected/clahe-100-100-0.jpg
vendored
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
test/fixtures/expected/clahe-100-50-3.jpg
vendored
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
test/fixtures/expected/clahe-11-25-14.jpg
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
test/fixtures/expected/clahe-5-5-0.jpg
vendored
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
test/fixtures/expected/clahe-5-5-5.jpg
vendored
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
test/fixtures/expected/clahe-50-50-0.jpg
vendored
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
test/fixtures/expected/clahe-50-50-14.jpg
vendored
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
test/fixtures/expected/colourspace-gradients-gamma-resize.png
vendored
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
test/fixtures/expected/extract-alpha-16bit.jpg
vendored
|
Before Width: | Height: | Size: 685 B |
BIN
test/fixtures/expected/extract-alpha-16bit.png
vendored
Normal file
|
After Width: | Height: | Size: 262 B |
BIN
test/fixtures/expected/negate-preserve-alpha-grey.png
vendored
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
test/fixtures/expected/negate-preserve-alpha-trans.png
vendored
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
test/fixtures/expected/negate-preserve-alpha-trans.webp
vendored
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
test/fixtures/expected/negate-preserve-alpha.png
vendored
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
test/fixtures/expected/negate-preserve-alpha.webp
vendored
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
test/fixtures/gradients-rgb8.png
vendored
Normal file
|
After Width: | Height: | Size: 777 KiB |
6
test/fixtures/index.js
vendored
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const sharp = require('../../');
|
const sharp = require('../../');
|
||||||
const maxColourDistance = require('../../build/Release/sharp')._maxColourDistance;
|
const maxColourDistance = require('../../lib/sharp')._maxColourDistance;
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
const getPath = function (filename) {
|
const getPath = function (filename) {
|
||||||
@@ -74,6 +74,7 @@ module.exports = {
|
|||||||
inputJpgLossless: getPath('testimgl.jpg'), // Lossless JPEG from ftp://ftp.fu-berlin.de/unix/X11/graphics/ImageMagick/delegates/ljpeg-6b.tar.gz
|
inputJpgLossless: getPath('testimgl.jpg'), // Lossless JPEG from ftp://ftp.fu-berlin.de/unix/X11/graphics/ImageMagick/delegates/ljpeg-6b.tar.gz
|
||||||
|
|
||||||
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
||||||
|
inputPngGradients: getPath('gradients-rgb8.png'),
|
||||||
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
||||||
inputPngCompleteTransparency: getPath('full-transparent.png'),
|
inputPngCompleteTransparency: getPath('full-transparent.png'),
|
||||||
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
|
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
|
||||||
@@ -90,6 +91,7 @@ module.exports = {
|
|||||||
inputPngEmbed: getPath('embedgravitybird.png'), // Released to sharp under a CC BY 4.0
|
inputPngEmbed: getPath('embedgravitybird.png'), // Released to sharp under a CC BY 4.0
|
||||||
inputPngRGBWithAlpha: getPath('2569067123_aca715a2ee_o.png'), // http://www.flickr.com/photos/grizdave/2569067123/ (same as inputJpg)
|
inputPngRGBWithAlpha: getPath('2569067123_aca715a2ee_o.png'), // http://www.flickr.com/photos/grizdave/2569067123/ (same as inputJpg)
|
||||||
inputPngImageInAlpha: getPath('image-in-alpha.png'), // https://github.com/lovell/sharp/issues/1597
|
inputPngImageInAlpha: getPath('image-in-alpha.png'), // https://github.com/lovell/sharp/issues/1597
|
||||||
|
inputPngSolidAlpha: getPath('with-alpha.png'), // https://github.com/lovell/sharp/issues/1599
|
||||||
|
|
||||||
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
|
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
|
||||||
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
||||||
@@ -120,6 +122,8 @@ module.exports = {
|
|||||||
|
|
||||||
inputV: getPath('vfile.v'),
|
inputV: getPath('vfile.v'),
|
||||||
|
|
||||||
|
inputJpgClahe: getPath('concert.jpg'), // public domain - https://www.flickr.com/photos/mars_/14389236779/
|
||||||
|
|
||||||
testPattern: getPath('test-pattern.png'),
|
testPattern: getPath('test-pattern.png'),
|
||||||
|
|
||||||
// Path for tests requiring human inspection
|
// Path for tests requiring human inspection
|
||||||
|
|||||||
BIN
test/fixtures/with-alpha.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
@@ -444,6 +444,22 @@
|
|||||||
...
|
...
|
||||||
fun:vips__init
|
fun:vips__init
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
leak_libvips_thread_pool_new
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: possible
|
||||||
|
fun:calloc
|
||||||
|
...
|
||||||
|
fun:g_system_thread_new
|
||||||
|
}
|
||||||
|
{
|
||||||
|
leak_libvips_thread_pool_push
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: possible
|
||||||
|
fun:calloc
|
||||||
|
...
|
||||||
|
fun:g_thread_pool_push
|
||||||
|
}
|
||||||
{
|
{
|
||||||
leak_rsvg_static_data
|
leak_rsvg_static_data
|
||||||
Memcheck:Leak
|
Memcheck:Leak
|
||||||
@@ -480,6 +496,14 @@
|
|||||||
...
|
...
|
||||||
fun:rsvg_handle_new_from_gfile_sync
|
fun:rsvg_handle_new_from_gfile_sync
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
leak_rsvg_rust_280_bytes_static_regex
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: possible
|
||||||
|
fun:malloc
|
||||||
|
...
|
||||||
|
fun:rsvg_handle_get_dimensions_sub
|
||||||
|
}
|
||||||
|
|
||||||
# libuv warnings
|
# libuv warnings
|
||||||
{
|
{
|
||||||
@@ -871,3 +895,12 @@
|
|||||||
...
|
...
|
||||||
fun:_ZN2v88internal18ArrayBufferSweeper10ReleaseAllEv
|
fun:_ZN2v88internal18ArrayBufferSweeper10ReleaseAllEv
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
addr_v8_ZN2v88internal12_GLOBAL__N_119HandleApiCallHelperILb0EEENS0
|
||||||
|
Memcheck:Addr8
|
||||||
|
fun:strncmp
|
||||||
|
...
|
||||||
|
fun:_ZZN4node7binding6DLOpenERKN2v820FunctionCallbackInfoINS1_5ValueEEEENKUlPNS0_4DLibEE_clES8_
|
||||||
|
fun:_ZN4node7binding6DLOpenERKN2v820FunctionCallbackInfoINS1_5ValueEEE
|
||||||
|
fun:_ZN2v88internal12_GLOBAL__N_119HandleApiCallHelperILb0EEENS0_11MaybeHandleINS0_6ObjectEEEPNS0_7IsolateENS0_6HandleINS0_10HeapObjectEEESA_NS8_INS0_20FunctionTemplateInfoEEENS8_IS4_EENS0_16BuiltinArgumentsE
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,17 @@ describe('HTTP agent', function () {
|
|||||||
assert.strictEqual(443, proxy.defaultPort);
|
assert.strictEqual(443, proxy.defaultPort);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('HTTPS proxy with auth from HTTPS_PROXY using credentials containing special characters', function () {
|
||||||
|
process.env.HTTPS_PROXY = 'https://user,:pass=@secure:123';
|
||||||
|
const proxy = agent();
|
||||||
|
delete process.env.HTTPS_PROXY;
|
||||||
|
assert.strictEqual('object', typeof proxy);
|
||||||
|
assert.strictEqual('secure', proxy.options.proxy.host);
|
||||||
|
assert.strictEqual(123, proxy.options.proxy.port);
|
||||||
|
assert.strictEqual('user,:pass=', proxy.options.proxy.proxyAuth);
|
||||||
|
assert.strictEqual(443, proxy.defaultPort);
|
||||||
|
});
|
||||||
|
|
||||||
it('HTTP proxy without auth from npm_config_proxy', function () {
|
it('HTTP proxy without auth from npm_config_proxy', function () {
|
||||||
process.env.npm_config_proxy = 'http://plaintext:456';
|
process.env.npm_config_proxy = 'http://plaintext:456';
|
||||||
const proxy = agent();
|
const proxy = agent();
|
||||||
|
|||||||
@@ -155,4 +155,27 @@ describe('Alpha transparency', function () {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Valid ensureAlpha value used for alpha channel', async () => {
|
||||||
|
const background = { r: 255, g: 0, b: 0 };
|
||||||
|
const [r, g, b, alpha] = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
channels: 3,
|
||||||
|
background
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ensureAlpha(0.5)
|
||||||
|
.raw()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
assert.deepStrictEqual({ r, g, b, alpha }, { ...background, alpha: 127 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid ensureAlpha value throws', async () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().ensureAlpha('fail');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
139
test/unit/clahe.js
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../lib');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('Clahe', function () {
|
||||||
|
it('width 5 width 5 maxSlope 0', function (done) {
|
||||||
|
sharp(fixtures.inputJpgClahe)
|
||||||
|
.clahe({ width: 5, height: 5, maxSlope: 0 })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('clahe-5-5-0.jpg'), data, { threshold: 10 }, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('width 5 width 5 maxSlope 5', function (done) {
|
||||||
|
sharp(fixtures.inputJpgClahe)
|
||||||
|
.clahe({ width: 5, height: 5, maxSlope: 5 })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('clahe-5-5-5.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('width 11 width 25 maxSlope 14', function (done) {
|
||||||
|
sharp(fixtures.inputJpgClahe)
|
||||||
|
.clahe({ width: 11, height: 25, maxSlope: 14 })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('clahe-11-25-14.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('width 50 width 50 maxSlope 0', function (done) {
|
||||||
|
sharp(fixtures.inputJpgClahe)
|
||||||
|
.clahe({ width: 50, height: 50, maxSlope: 0 })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('clahe-50-50-0.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('width 50 width 50 maxSlope 14', function (done) {
|
||||||
|
sharp(fixtures.inputJpgClahe)
|
||||||
|
.clahe({ width: 50, height: 50, maxSlope: 14 })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('clahe-50-50-14.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('width 100 width 50 maxSlope 3', function (done) {
|
||||||
|
sharp(fixtures.inputJpgClahe)
|
||||||
|
.clahe({ width: 100, height: 50, maxSlope: 3 })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('width 100 width 100 maxSlope 0', function (done) {
|
||||||
|
sharp(fixtures.inputJpgClahe)
|
||||||
|
.clahe({ width: 100, height: 100, maxSlope: 0 })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('clahe-100-100-0.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid maxSlope', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: -5 });
|
||||||
|
});
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 110 });
|
||||||
|
});
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 5.5 });
|
||||||
|
});
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 'a string' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid width', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 100.5, height: 100 });
|
||||||
|
});
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: -5, height: 100 });
|
||||||
|
});
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: true, height: 100 });
|
||||||
|
});
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 'string test', height: 100 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid height', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100.5 });
|
||||||
|
});
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: -5 });
|
||||||
|
});
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: true });
|
||||||
|
});
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 'string test' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid options object', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpgClahe).clahe(100, 100, 5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses default maxSlope of 3', function (done) {
|
||||||
|
sharp(fixtures.inputJpgClahe)
|
||||||
|
.clahe({ width: 100, height: 50 })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -90,7 +90,29 @@ describe('Colour space conversion', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Invalid input', function () {
|
it('From sRGB with RGB16 pipeline, resize with gamma, to sRGB', function (done) {
|
||||||
|
sharp(fixtures.inputPngGradients)
|
||||||
|
.pipelineColourspace('rgb16')
|
||||||
|
.resize(320)
|
||||||
|
.gamma()
|
||||||
|
.toColourspace('srgb')
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('colourspace-gradients-gamma-resize.png'), data, {
|
||||||
|
threshold: 0
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid pipelineColourspace input', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.pipelineColorspace(null);
|
||||||
|
}, /Expected string for colourspace but received null of type object/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid toColourspace input', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.toColourspace(null);
|
.toColourspace(null);
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ describe('Image channel extraction', function () {
|
|||||||
it('With colorspace conversion', function (done) {
|
it('With colorspace conversion', function (done) {
|
||||||
const output = fixtures.path('output.extract-lch.jpg');
|
const output = fixtures.path('output.extract-lch.jpg');
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.toColourspace('lch')
|
|
||||||
.extractChannel(1)
|
.extractChannel(1)
|
||||||
|
.toColourspace('lch')
|
||||||
.resize(320, 240, { fastShrinkOnLoad: false })
|
.resize(320, 240, { fastShrinkOnLoad: false })
|
||||||
.toFile(output, function (err, info) {
|
.toFile(output, function (err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@@ -70,12 +70,13 @@ describe('Image channel extraction', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Alpha from 16-bit PNG', function (done) {
|
it('Alpha from 16-bit PNG', function (done) {
|
||||||
const output = fixtures.path('output.extract-alpha-16bit.jpg');
|
const output = fixtures.path('output.extract-alpha-16bit.png');
|
||||||
sharp(fixtures.inputPngWithTransparency16bit)
|
sharp(fixtures.inputPngWithTransparency16bit)
|
||||||
|
.resize(16)
|
||||||
.extractChannel(3)
|
.extractChannel(3)
|
||||||
.toFile(output, function (err, info) {
|
.toFile(output, function (err) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-16bit.jpg'));
|
fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-16bit.png'));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -335,17 +335,6 @@ describe('Input/output', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Fail when input is empty Buffer', function (done) {
|
|
||||||
sharp(Buffer.alloc(0)).toBuffer().then(function () {
|
|
||||||
assert(false);
|
|
||||||
done();
|
|
||||||
}).catch(function (err) {
|
|
||||||
assert(err instanceof Error);
|
|
||||||
assert.strictEqual('Input buffer contains unsupported image format', err.message);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Fail when input is invalid Buffer', function (done) {
|
it('Fail when input is invalid Buffer', function (done) {
|
||||||
sharp(Buffer.from([0x1, 0x2, 0x3, 0x4])).toBuffer().then(function () {
|
sharp(Buffer.from([0x1, 0x2, 0x3, 0x4])).toBuffer().then(function () {
|
||||||
assert(false);
|
assert(false);
|
||||||
@@ -718,6 +707,19 @@ describe('Input/output', function () {
|
|||||||
sharp({ level: -1 });
|
sharp({ level: -1 });
|
||||||
}, /Expected integer between 0 and 256 for level but received -1 of type number/);
|
}, /Expected integer between 0 and 256 for level but received -1 of type number/);
|
||||||
});
|
});
|
||||||
|
it('Valid subifd property', function () {
|
||||||
|
sharp({ subifd: 1 });
|
||||||
|
});
|
||||||
|
it('Invalid subifd property (string) throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ subifd: '1' });
|
||||||
|
}, /Expected integer between -1 and 100000 for subifd but received 1 of type string/);
|
||||||
|
});
|
||||||
|
it('Invalid subifd property (float) throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ subifd: 1.2 });
|
||||||
|
}, /Expected integer between -1 and 100000 for subifd but received 1.2 of type number/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create new image', function () {
|
describe('create new image', function () {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ describe('libvips binaries', function () {
|
|||||||
|
|
||||||
it('logs an error message', function (done) {
|
it('logs an error message', function (done) {
|
||||||
console.error = function (msg) {
|
console.error = function (msg) {
|
||||||
assert.strictEqual(msg, 'sharp: problem');
|
assert.strictEqual(msg, 'sharp: Installation error: problem');
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
libvips.log(new Error('problem'));
|
libvips.log(new Error('problem'));
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ describe('Image metadata', function () {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
it('GIF via giflib', function (done) {
|
it('GIF', function (done) {
|
||||||
sharp(fixtures.inputGif).metadata(function (err, metadata) {
|
sharp(fixtures.inputGif).metadata(function (err, metadata) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('gif', metadata.format);
|
assert.strictEqual('gif', metadata.format);
|
||||||
@@ -253,27 +253,26 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual('undefined', typeof metadata.chromaSubsampling);
|
assert.strictEqual('undefined', typeof metadata.chromaSubsampling);
|
||||||
assert.strictEqual(false, metadata.isProgressive);
|
assert.strictEqual(false, metadata.isProgressive);
|
||||||
assert.strictEqual(false, metadata.hasProfile);
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
|
||||||
assert.strictEqual('undefined', typeof metadata.orientation);
|
assert.strictEqual('undefined', typeof metadata.orientation);
|
||||||
assert.strictEqual('undefined', typeof metadata.exif);
|
assert.strictEqual('undefined', typeof metadata.exif);
|
||||||
assert.strictEqual('undefined', typeof metadata.icc);
|
assert.strictEqual('undefined', typeof metadata.icc);
|
||||||
|
assert.deepStrictEqual(metadata.background, { r: 138, g: 148, b: 102 });
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('GIF grey+alpha via giflib', function (done) {
|
it('GIF grey+alpha', function (done) {
|
||||||
sharp(fixtures.inputGifGreyPlusAlpha).metadata(function (err, metadata) {
|
sharp(fixtures.inputGifGreyPlusAlpha).metadata(function (err, metadata) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('gif', metadata.format);
|
assert.strictEqual('gif', metadata.format);
|
||||||
assert.strictEqual('undefined', typeof metadata.size);
|
assert.strictEqual('undefined', typeof metadata.size);
|
||||||
assert.strictEqual(2, metadata.width);
|
assert.strictEqual(2, metadata.width);
|
||||||
assert.strictEqual(1, metadata.height);
|
assert.strictEqual(1, metadata.height);
|
||||||
assert.strictEqual(2, metadata.channels);
|
assert.strictEqual(4, metadata.channels);
|
||||||
assert.strictEqual('uchar', metadata.depth);
|
assert.strictEqual('uchar', metadata.depth);
|
||||||
assert.strictEqual('undefined', typeof metadata.density);
|
assert.strictEqual('undefined', typeof metadata.density);
|
||||||
assert.strictEqual('undefined', typeof metadata.chromaSubsampling);
|
assert.strictEqual('undefined', typeof metadata.chromaSubsampling);
|
||||||
assert.strictEqual(false, metadata.isProgressive);
|
assert.strictEqual(false, metadata.isProgressive);
|
||||||
assert.strictEqual(false, metadata.hasProfile);
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
assert.strictEqual(true, metadata.hasAlpha);
|
|
||||||
assert.strictEqual('undefined', typeof metadata.orientation);
|
assert.strictEqual('undefined', typeof metadata.orientation);
|
||||||
assert.strictEqual('undefined', typeof metadata.exif);
|
assert.strictEqual('undefined', typeof metadata.exif);
|
||||||
assert.strictEqual('undefined', typeof metadata.icc);
|
assert.strictEqual('undefined', typeof metadata.icc);
|
||||||
@@ -287,7 +286,7 @@ describe('Image metadata', function () {
|
|||||||
.then(({
|
.then(({
|
||||||
format, width, height, space, channels, depth,
|
format, width, height, space, channels, depth,
|
||||||
isProgressive, pages, pageHeight, loop, delay,
|
isProgressive, pages, pageHeight, loop, delay,
|
||||||
hasProfile, hasAlpha
|
background, hasProfile, hasAlpha
|
||||||
}) => {
|
}) => {
|
||||||
assert.strictEqual(format, 'gif');
|
assert.strictEqual(format, 'gif');
|
||||||
assert.strictEqual(width, 80);
|
assert.strictEqual(width, 80);
|
||||||
@@ -300,6 +299,7 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual(pageHeight, 80);
|
assert.strictEqual(pageHeight, 80);
|
||||||
assert.strictEqual(loop, 0);
|
assert.strictEqual(loop, 0);
|
||||||
assert.deepStrictEqual(delay, Array(30).fill(30));
|
assert.deepStrictEqual(delay, Array(30).fill(30));
|
||||||
|
assert.deepStrictEqual(background, { r: 0, g: 0, b: 0 });
|
||||||
assert.strictEqual(hasProfile, false);
|
assert.strictEqual(hasProfile, false);
|
||||||
assert.strictEqual(hasAlpha, true);
|
assert.strictEqual(hasAlpha, true);
|
||||||
})
|
})
|
||||||
@@ -322,7 +322,7 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual(isProgressive, false);
|
assert.strictEqual(isProgressive, false);
|
||||||
assert.strictEqual(pages, 10);
|
assert.strictEqual(pages, 10);
|
||||||
assert.strictEqual(pageHeight, 285);
|
assert.strictEqual(pageHeight, 285);
|
||||||
assert.strictEqual(loop, 3);
|
assert.strictEqual(loop, 2);
|
||||||
assert.deepStrictEqual(delay, [...Array(9).fill(3000), 15000]);
|
assert.deepStrictEqual(delay, [...Array(9).fill(3000), 15000]);
|
||||||
assert.strictEqual(hasProfile, false);
|
assert.strictEqual(hasProfile, false);
|
||||||
assert.strictEqual(hasAlpha, true);
|
assert.strictEqual(hasAlpha, true);
|
||||||
@@ -599,6 +599,68 @@ describe('Image metadata', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Add EXIF metadata to JPEG', async () => {
|
||||||
|
const data = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
channels: 3,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.jpeg()
|
||||||
|
.withMetadata({
|
||||||
|
exif: {
|
||||||
|
IFD0: { Software: 'sharp' },
|
||||||
|
IFD2: { ExposureTime: '0.2' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { exif } = await sharp(data).metadata();
|
||||||
|
const parsedExif = exifReader(exif);
|
||||||
|
assert.strictEqual(parsedExif.image.Software, 'sharp');
|
||||||
|
assert.strictEqual(parsedExif.exif.ExposureTime, 0.2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Set density of JPEG', async () => {
|
||||||
|
const data = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
channels: 3,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.withMetadata({
|
||||||
|
density: 300
|
||||||
|
})
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { density } = await sharp(data).metadata();
|
||||||
|
assert.strictEqual(density, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Set density of PNG', async () => {
|
||||||
|
const data = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
channels: 3,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.withMetadata({
|
||||||
|
density: 96
|
||||||
|
})
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { density } = await sharp(data).metadata();
|
||||||
|
assert.strictEqual(density, 96);
|
||||||
|
});
|
||||||
|
|
||||||
it('chromaSubsampling 4:4:4:4 CMYK JPEG', function () {
|
it('chromaSubsampling 4:4:4:4 CMYK JPEG', function () {
|
||||||
return sharp(fixtures.inputJpgWithCmykProfile)
|
return sharp(fixtures.inputJpgWithCmykProfile)
|
||||||
.metadata()
|
.metadata()
|
||||||
@@ -664,6 +726,25 @@ describe('Image metadata', function () {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it('AVIF', async () => {
|
||||||
|
const metadata = await sharp(fixtures.inputAvif).metadata();
|
||||||
|
assert.deepStrictEqual(metadata, {
|
||||||
|
format: 'heif',
|
||||||
|
width: 2048,
|
||||||
|
height: 858,
|
||||||
|
space: 'srgb',
|
||||||
|
channels: 3,
|
||||||
|
depth: 'uchar',
|
||||||
|
isProgressive: false,
|
||||||
|
pages: 1,
|
||||||
|
pageHeight: 858,
|
||||||
|
pagePrimary: 0,
|
||||||
|
compression: 'av1',
|
||||||
|
hasProfile: false,
|
||||||
|
hasAlpha: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('File input with corrupt header fails gracefully', function (done) {
|
it('File input with corrupt header fails gracefully', function (done) {
|
||||||
sharp(fixtures.inputJpgWithCorruptHeader)
|
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||||
.metadata(function (err) {
|
.metadata(function (err) {
|
||||||
@@ -712,10 +793,35 @@ describe('Image metadata', function () {
|
|||||||
sharp().withMetadata({ orientation: 9 });
|
sharp().withMetadata({ orientation: 9 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('Non-numeric density', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().withMetadata({ density: '1' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('Negative density', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().withMetadata({ density: -1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
it('Non string icc', function () {
|
it('Non string icc', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().withMetadata({ icc: true });
|
sharp().withMetadata({ icc: true });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('Non object exif', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().withMetadata({ exif: false });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('Non string value in object exif', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().withMetadata({ exif: { ifd0: false } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('Non string value in nested object exif', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().withMetadata({ exif: { ifd0: { fail: false } } });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -107,4 +107,88 @@ describe('Negate', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('negate ({alpha: true})', function (done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.negate({ alpha: true })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('negate.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('negate non-alpha channels (png)', function (done) {
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.resize(320, 240)
|
||||||
|
.negate({ alpha: false })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('negate-preserve-alpha.png'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('negate non-alpha channels (png, trans)', function (done) {
|
||||||
|
sharp(fixtures.inputPngWithTransparency)
|
||||||
|
.resize(320, 240)
|
||||||
|
.negate({ alpha: false })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('negate-preserve-alpha-trans.png'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('negate non-alpha channels (png, alpha)', function (done) {
|
||||||
|
sharp(fixtures.inputPngWithGreyAlpha)
|
||||||
|
.resize(320, 240)
|
||||||
|
.negate({ alpha: false })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('negate-preserve-alpha-grey.png'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('negate non-alpha channels (webp)', function (done) {
|
||||||
|
sharp(fixtures.inputWebP)
|
||||||
|
.resize(320, 240)
|
||||||
|
.negate({ alpha: false })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('webp', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('negate-preserve-alpha.webp'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('negate non-alpha channels (webp, trans)', function (done) {
|
||||||
|
sharp(fixtures.inputWebPWithTransparency)
|
||||||
|
.resize(320, 240)
|
||||||
|
.negate({ alpha: false })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('webp', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('negate-preserve-alpha-trans.webp'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid alpha value', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputWebPWithTransparency).negate({ alpha: 'non-bool' });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,23 +32,21 @@ describe('Platform-detection', function () {
|
|||||||
delete process.env.npm_config_arch;
|
delete process.env.npm_config_arch;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Can detect ARM version via process.config', function () {
|
if (process.config.variables.arm_version) {
|
||||||
process.env.npm_config_arch = 'arm';
|
it('Can detect ARM version via process.config', function () {
|
||||||
const armVersion = process.config.variables.arm_version;
|
process.env.npm_config_arch = 'arm';
|
||||||
process.config.variables.arm_version = 'test';
|
assert.strictEqual(`armv${process.config.variables.arm_version}`, platform().split('-')[1]);
|
||||||
assert.strictEqual('armvtest', platform().split('-')[1]);
|
delete process.env.npm_config_arch;
|
||||||
process.config.variables.arm_version = armVersion;
|
});
|
||||||
delete process.env.npm_config_arch;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
it('Defaults to ARMv6 for 32-bit', function () {
|
if (!process.config.variables.arm_version) {
|
||||||
process.env.npm_config_arch = 'arm';
|
it('Defaults to ARMv6 for 32-bit', function () {
|
||||||
const armVersion = process.config.variables.arm_version;
|
process.env.npm_config_arch = 'arm';
|
||||||
delete process.config.variables.arm_version;
|
assert.strictEqual('armv6', platform().split('-')[1]);
|
||||||
assert.strictEqual('armv6', platform().split('-')[1]);
|
delete process.env.npm_config_arch;
|
||||||
process.config.variables.arm_version = armVersion;
|
});
|
||||||
delete process.env.npm_config_arch;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
it('Defaults to ARMv8 for 64-bit', function () {
|
it('Defaults to ARMv8 for 64-bit', function () {
|
||||||
process.env.npm_config_arch = 'arm64';
|
process.env.npm_config_arch = 'arm64';
|
||||||
|
|||||||
@@ -7,6 +7,18 @@ const fixtures = require('../fixtures');
|
|||||||
|
|
||||||
describe('Raw pixel data', function () {
|
describe('Raw pixel data', function () {
|
||||||
describe('Raw pixel input', function () {
|
describe('Raw pixel input', function () {
|
||||||
|
it('Empty data', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(Buffer.from(''));
|
||||||
|
}, /empty/);
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(new Uint8Array(0));
|
||||||
|
}, /empty/);
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(new Uint8ClampedArray(0));
|
||||||
|
}, /empty/);
|
||||||
|
});
|
||||||
|
|
||||||
it('Missing options', function () {
|
it('Missing options', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp({ raw: {} });
|
sharp({ raw: {} });
|
||||||
@@ -95,6 +107,52 @@ describe('Raw pixel data', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('RGBA premultiplied', function (done) {
|
||||||
|
// Convert to raw pixel data
|
||||||
|
sharp(fixtures.inputPngSolidAlpha)
|
||||||
|
.resize(256)
|
||||||
|
.raw()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(256, info.width);
|
||||||
|
assert.strictEqual(192, info.height);
|
||||||
|
assert.strictEqual(4, info.channels);
|
||||||
|
|
||||||
|
const originalData = Buffer.from(data);
|
||||||
|
|
||||||
|
// Premultiply image data
|
||||||
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
|
const alpha = data[i + 3];
|
||||||
|
const norm = alpha / 255;
|
||||||
|
|
||||||
|
if (alpha < 255) {
|
||||||
|
data[i] = Math.round(data[i] * norm);
|
||||||
|
data[i + 1] = Math.round(data[i + 1] * norm);
|
||||||
|
data[i + 2] = Math.round(data[i + 2] * norm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert back to PNG
|
||||||
|
sharp(data, {
|
||||||
|
raw: {
|
||||||
|
width: info.width,
|
||||||
|
height: info.height,
|
||||||
|
channels: info.channels,
|
||||||
|
premultiplied: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.raw()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(256, info.width);
|
||||||
|
assert.strictEqual(192, info.height);
|
||||||
|
assert.strictEqual(4, info.channels);
|
||||||
|
assert.equal(data.compare(originalData), 0, 'output buffer matches unpremultiplied input buffer');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('JPEG to raw Stream and back again', function (done) {
|
it('JPEG to raw Stream and back again', function (done) {
|
||||||
const width = 32;
|
const width = 32;
|
||||||
const height = 24;
|
const height = 24;
|
||||||
@@ -121,7 +179,7 @@ describe('Raw pixel data', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Ouput raw, uncompressed image data', function () {
|
describe('Output raw, uncompressed image data', function () {
|
||||||
it('1 channel greyscale image', function (done) {
|
it('1 channel greyscale image', function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.greyscale()
|
.greyscale()
|
||||||
@@ -169,7 +227,7 @@ describe('Raw pixel data', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('extract A from RGBA', () =>
|
it('Extract A from RGBA', () =>
|
||||||
sharp(fixtures.inputPngWithTransparency)
|
sharp(fixtures.inputPngWithTransparency)
|
||||||
.resize(32, 24)
|
.resize(32, 24)
|
||||||
.extractChannel(3)
|
.extractChannel(3)
|
||||||
@@ -183,4 +241,41 @@ describe('Raw pixel data', function () {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Raw pixel depths', function () {
|
||||||
|
it('Invalid depth', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(Buffer.alloc(3), { raw: { width: 1, height: 1, channels: 3 } })
|
||||||
|
.raw({ depth: 'zoinks' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const { constructor, depth, bits } of [
|
||||||
|
{ constructor: Uint8Array, depth: undefined, bits: 8 },
|
||||||
|
{ constructor: Uint8Array, depth: 'uchar', bits: 8 },
|
||||||
|
{ constructor: Uint8ClampedArray, depth: 'uchar', bits: 8 },
|
||||||
|
{ constructor: Int8Array, depth: 'char', bits: 8 },
|
||||||
|
{ constructor: Uint16Array, depth: 'ushort', bits: 16 },
|
||||||
|
{ constructor: Int16Array, depth: 'short', bits: 16 },
|
||||||
|
{ constructor: Uint32Array, depth: 'uint', bits: 32 },
|
||||||
|
{ constructor: Int32Array, depth: 'int', bits: 32 },
|
||||||
|
{ constructor: Float32Array, depth: 'float', bits: 32 },
|
||||||
|
{ constructor: Float64Array, depth: 'double', bits: 64 }
|
||||||
|
]) {
|
||||||
|
it(constructor.name, () =>
|
||||||
|
sharp(new constructor(3), { raw: { width: 1, height: 1, channels: 3 } })
|
||||||
|
.raw({ depth })
|
||||||
|
.toBuffer({ resolveWithObject: true })
|
||||||
|
.then(({ data, info }) => {
|
||||||
|
assert.strictEqual(1, info.width);
|
||||||
|
assert.strictEqual(1, info.height);
|
||||||
|
assert.strictEqual(3, info.channels);
|
||||||
|
if (depth !== undefined) {
|
||||||
|
assert.strictEqual(depth, info.depth);
|
||||||
|
}
|
||||||
|
assert.strictEqual(data.length / 3, bits / 8);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -605,6 +605,26 @@ describe('Resize dimensions', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Skip shrink-on-load where one dimension <4px', async () => {
|
||||||
|
const jpeg = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 100,
|
||||||
|
height: 3,
|
||||||
|
channels: 3,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { info } = await sharp(jpeg)
|
||||||
|
.resize(8)
|
||||||
|
.toBuffer({ resolveWithObject: true });
|
||||||
|
|
||||||
|
assert.strictEqual(info.width, 8);
|
||||||
|
assert.strictEqual(info.height, 1);
|
||||||
|
});
|
||||||
|
|
||||||
it('unknown kernel throws', function () {
|
it('unknown kernel throws', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().resize(null, null, { kernel: 'unknown' });
|
sharp().resize(null, null, { kernel: 'unknown' });
|
||||||
|
|||||||
@@ -423,20 +423,20 @@ describe('Image Stats', function () {
|
|||||||
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[3].min);
|
||||||
assert.strictEqual(255, stats.channels[1].max);
|
assert.strictEqual(255, stats.channels[3].max);
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 255));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].sum, 255));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 65025));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].squaresSum, 65025));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 127.5));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].mean, 127.5));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 180.31222920256963));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].stdev, 180.31222920256963));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
assert.strictEqual(true, isInteger(stats.channels[3].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 2));
|
assert.strictEqual(true, isInRange(stats.channels[3].minX, 0, 2));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
assert.strictEqual(true, isInteger(stats.channels[3].minY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1].minY, 0, 1));
|
assert.strictEqual(true, isInRange(stats.channels[3].minY, 0, 1));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1].maxX));
|
assert.strictEqual(true, isInteger(stats.channels[3].maxX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1].maxX, 0, 2));
|
assert.strictEqual(true, isInRange(stats.channels[3].maxX, 0, 2));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1].maxY));
|
assert.strictEqual(true, isInteger(stats.channels[3].maxY));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1].maxY, 0, 1));
|
assert.strictEqual(true, isInRange(stats.channels[3].maxY, 0, 1));
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ describe('WebP', function () {
|
|||||||
|
|
||||||
it('should work for webp alpha quality', function (done) {
|
it('should work for webp alpha quality', function (done) {
|
||||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
.webp({ alphaQuality: 80 })
|
.webp({ alphaQuality: 80, reductionEffort: 0 })
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
@@ -46,7 +46,7 @@ describe('WebP', function () {
|
|||||||
|
|
||||||
it('should work for webp lossless', function (done) {
|
it('should work for webp lossless', function (done) {
|
||||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
.webp({ lossless: true })
|
.webp({ lossless: true, reductionEffort: 0 })
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
@@ -57,7 +57,7 @@ describe('WebP', function () {
|
|||||||
|
|
||||||
it('should work for webp near-lossless', function (done) {
|
it('should work for webp near-lossless', function (done) {
|
||||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
.webp({ nearLossless: true, quality: 50 })
|
.webp({ nearLossless: true, quality: 50, reductionEffort: 0 })
|
||||||
.toBuffer(function (err50, data50, info50) {
|
.toBuffer(function (err50, data50, info50) {
|
||||||
if (err50) throw err50;
|
if (err50) throw err50;
|
||||||
assert.strictEqual(true, data50.length > 0);
|
assert.strictEqual(true, data50.length > 0);
|
||||||
@@ -68,7 +68,7 @@ describe('WebP', function () {
|
|||||||
|
|
||||||
it('should use near-lossless when both lossless and nearLossless are specified', function (done) {
|
it('should use near-lossless when both lossless and nearLossless are specified', function (done) {
|
||||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
.webp({ nearLossless: true, quality: 50, lossless: true })
|
.webp({ nearLossless: true, quality: 50, lossless: true, reductionEffort: 0 })
|
||||||
.toBuffer(function (err50, data50, info50) {
|
.toBuffer(function (err50, data50, info50) {
|
||||||
if (err50) throw err50;
|
if (err50) throw err50;
|
||||||
assert.strictEqual(true, data50.length > 0);
|
assert.strictEqual(true, data50.length > 0);
|
||||||
@@ -189,7 +189,7 @@ describe('WebP', function () {
|
|||||||
it('should work with streams when only animated is set', function (done) {
|
it('should work with streams when only animated is set', function (done) {
|
||||||
fs.createReadStream(fixtures.inputWebPAnimated)
|
fs.createReadStream(fixtures.inputWebPAnimated)
|
||||||
.pipe(sharp({ animated: true }))
|
.pipe(sharp({ animated: true }))
|
||||||
.webp({ lossless: true })
|
.webp({ lossless: true, reductionEffort: 0 })
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
@@ -201,7 +201,7 @@ describe('WebP', function () {
|
|||||||
it('should work with streams when only pages is set', function (done) {
|
it('should work with streams when only pages is set', function (done) {
|
||||||
fs.createReadStream(fixtures.inputWebPAnimated)
|
fs.createReadStream(fixtures.inputWebPAnimated)
|
||||||
.pipe(sharp({ pages: -1 }))
|
.pipe(sharp({ pages: -1 }))
|
||||||
.webp({ lossless: true })
|
.webp({ lossless: true, reductionEffort: 0 })
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
|||||||