Compare commits
53 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 |
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"
|
||||
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
|
||||
24
.github/workflows/ci.yml
vendored
@@ -1,3 +1,4 @@
|
||||
name: CI (GitHub)
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
@@ -11,43 +12,34 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
container: centos:7
|
||||
nodejs_version: 10
|
||||
nodejs_version: 12
|
||||
coverage: true
|
||||
prebuild: true
|
||||
- os: ubuntu-20.04
|
||||
container: centos:7
|
||||
nodejs_version: 12
|
||||
- os: ubuntu-20.04
|
||||
container: centos:7
|
||||
nodejs_version: 14
|
||||
- os: ubuntu-20.04
|
||||
container: centos:7
|
||||
nodejs_version: 16
|
||||
- os: ubuntu-20.04
|
||||
container: node:10-alpine3.11
|
||||
prebuild: true
|
||||
- os: ubuntu-20.04
|
||||
container: node:12-alpine3.11
|
||||
prebuild: true
|
||||
- os: ubuntu-20.04
|
||||
container: node:14-alpine3.11
|
||||
- os: ubuntu-20.04
|
||||
container: node:14-alpine3.13
|
||||
- os: ubuntu-20.04
|
||||
container: node:16-alpine3.11
|
||||
- os: macos-10.15
|
||||
nodejs_version: 10
|
||||
prebuild: true
|
||||
- os: macos-10.15
|
||||
nodejs_version: 12
|
||||
prebuild: true
|
||||
- os: macos-10.15
|
||||
nodejs_version: 14
|
||||
- os: macos-10.15
|
||||
nodejs_version: 16
|
||||
- os: windows-2019
|
||||
nodejs_version: 10
|
||||
prebuild: true
|
||||
- os: windows-2019
|
||||
nodejs_version: 12
|
||||
prebuild: true
|
||||
- os: windows-2019
|
||||
nodejs_version: 14
|
||||
- os: windows-2019
|
||||
@@ -57,7 +49,9 @@ jobs:
|
||||
if: contains(matrix.container, 'centos')
|
||||
run: |
|
||||
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)
|
||||
if: contains(matrix.container, 'alpine')
|
||||
run: apk add build-base git python3 --update-cache
|
||||
@@ -84,4 +78,4 @@ jobs:
|
||||
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
|
||||
env:
|
||||
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
|
||||
}
|
||||
|
||||
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 16"
|
||||
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_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"
|
||||
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 16"
|
||||
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:16-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
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
||||
[](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
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@ build: off
|
||||
platform: x86
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "10"
|
||||
prebuild: true
|
||||
- nodejs_version: "12"
|
||||
prebuild: true
|
||||
- nodejs_version: "14"
|
||||
- nodejs_version: "16"
|
||||
install:
|
||||
@@ -15,4 +14,4 @@ install:
|
||||
test_script:
|
||||
- npm test
|
||||
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': {
|
||||
'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': [{
|
||||
'target_name': 'libvips-cpp',
|
||||
@@ -37,6 +38,7 @@
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
'ExceptionHandling': 1,
|
||||
'Optimization': 1,
|
||||
'WholeProgramOptimization': 'true'
|
||||
},
|
||||
'VCLibrarianTool': {
|
||||
@@ -65,9 +67,9 @@
|
||||
}]
|
||||
]
|
||||
}, {
|
||||
'target_name': 'sharp',
|
||||
'target_name': 'sharp-<(platform_and_arch)',
|
||||
'defines': [
|
||||
'NAPI_VERSION=3'
|
||||
'NAPI_VERSION=5'
|
||||
],
|
||||
'dependencies': [
|
||||
'<!(node -p "require(\'node-addon-api\').gyp")',
|
||||
@@ -138,7 +140,7 @@
|
||||
}],
|
||||
['OS == "mac"', {
|
||||
'link_settings': {
|
||||
'library_dirs': ['<(sharp_vendor_dir)/lib'],
|
||||
'library_dirs': ['../<(sharp_vendor_dir)/lib'],
|
||||
'libraries': [
|
||||
'libvips-cpp.42.dylib'
|
||||
]
|
||||
@@ -146,7 +148,7 @@
|
||||
'xcode_settings': {
|
||||
'OTHER_LDFLAGS': [
|
||||
# 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'
|
||||
],
|
||||
'link_settings': {
|
||||
'library_dirs': ['<(sharp_vendor_dir)/lib'],
|
||||
'library_dirs': ['../<(sharp_vendor_dir)/lib'],
|
||||
'libraries': [
|
||||
'-l:libvips-cpp.so.42'
|
||||
],
|
||||
'ldflags': [
|
||||
# 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',
|
||||
'-fexceptions',
|
||||
'-Wall',
|
||||
'-O3'
|
||||
'-Os'
|
||||
],
|
||||
'xcode_settings': {
|
||||
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11',
|
||||
@@ -182,7 +184,7 @@
|
||||
'OTHER_CPLUSPLUSFLAGS': [
|
||||
'-fexceptions',
|
||||
'-Wall',
|
||||
'-O3'
|
||||
'-Oz'
|
||||
]
|
||||
},
|
||||
'configurations': {
|
||||
@@ -202,6 +204,7 @@
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
'ExceptionHandling': 1,
|
||||
'Optimization': 1,
|
||||
'WholeProgramOptimization': 'true'
|
||||
},
|
||||
'VCLibrarianTool': {
|
||||
|
||||
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
|
||||
As well as image resizing, operations such as
|
||||
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.
|
||||
|
||||
### Formats
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
|
||||
|
||||
See also [flatten][1].
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
@@ -25,7 +27,7 @@ This is a no-op if the image already has an alpha channel.
|
||||
|
||||
### Parameters
|
||||
|
||||
* `alpha` **[number][1]** alpha transparency level (0=fully-transparent, 1=fully-opaque) (optional, default `1`)
|
||||
* `alpha` **[number][2]** alpha transparency level (0=fully-transparent, 1=fully-opaque) (optional, default `1`)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -43,7 +45,7 @@ const rgba = await sharp(rgb)
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
* Throws **[Error][2]** Invalid alpha transparency level
|
||||
* Throws **[Error][3]** Invalid alpha transparency level
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
@@ -57,21 +59,26 @@ Extract a single channel from a multi-channel image.
|
||||
|
||||
### Parameters
|
||||
|
||||
* `channel` **([number][1] | [string][3])** 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
|
||||
|
||||
```javascript
|
||||
sharp(input)
|
||||
// green.jpg is a greyscale image containing the green channel of the input
|
||||
await sharp(input)
|
||||
.extractChannel('green')
|
||||
.toColourspace('b-w')
|
||||
.toFile('green.jpg', function(err, info) {
|
||||
// info.channels === 1
|
||||
// green.jpg is a greyscale image containing the green channel of the input
|
||||
});
|
||||
.toFile('green.jpg');
|
||||
```
|
||||
|
||||
* Throws **[Error][2]** 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**
|
||||
|
||||
@@ -90,12 +97,12 @@ For raw pixel input, the `options` object should contain a `raw` attribute, whic
|
||||
|
||||
### Parameters
|
||||
|
||||
* `images` **([Array][4]<([string][3] | [Buffer][5])> | [string][3] | [Buffer][5])** one or more images (file paths, Buffers).
|
||||
* `options` **[Object][6]** image options, see `sharp()` constructor.
|
||||
* `images` **([Array][5]<([string][4] | [Buffer][6])> | [string][4] | [Buffer][6])** one or more images (file paths, Buffers).
|
||||
* `options` **[Object][7]** image options, see `sharp()` constructor.
|
||||
|
||||
<!---->
|
||||
|
||||
* Throws **[Error][2]** Invalid parameters
|
||||
* Throws **[Error][3]** Invalid parameters
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
@@ -105,7 +112,7 @@ Perform a bitwise boolean operation on all input image channels (bands) to produ
|
||||
|
||||
### Parameters
|
||||
|
||||
* `boolOp` **[string][3]** 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
|
||||
|
||||
@@ -119,18 +126,20 @@ sharp('3-channel-rgb-input.png')
|
||||
});
|
||||
```
|
||||
|
||||
* Throws **[Error][2]** Invalid parameters
|
||||
* Throws **[Error][3]** Invalid parameters
|
||||
|
||||
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/Error
|
||||
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
|
||||
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||
[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
|
||||
|
||||
@@ -40,6 +40,51 @@ Alternative spelling of `greyscale`.
|
||||
|
||||
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**
|
||||
|
||||
## toColourspace
|
||||
|
||||
Set the output colourspace.
|
||||
@@ -47,7 +92,7 @@ By default output image will be web-friendly sRGB, with additional channels inte
|
||||
|
||||
### 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
|
||||
|
||||
@@ -86,4 +131,8 @@ Returns **Sharp**
|
||||
|
||||
[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
|
||||
|
||||
@@ -41,6 +41,8 @@ and [https://www.cairographics.org/operators/][2]
|
||||
* `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
|
||||
|
||||
@@ -61,7 +63,7 @@ sharp('input.png')
|
||||
});
|
||||
```
|
||||
|
||||
* Throws **[Error][10]** Invalid parameters
|
||||
* Throws **[Error][11]** Invalid parameters
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
@@ -87,4 +89,6 @@ Returns **Sharp**
|
||||
|
||||
[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,43 +13,44 @@ Implements the [stream.Duplex][1] class.
|
||||
|
||||
### Parameters
|
||||
|
||||
* `input` **([Buffer][2] | [Uint8Array][3] | [Uint8ClampedArray][4] | [string][5])?** if present, can be
|
||||
a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
||||
* `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 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.
|
||||
JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||
* `options` **[Object][6]?** if present, is an Object with optional attributes.
|
||||
* `options` **[Object][13]?** if present, is an Object with optional attributes.
|
||||
|
||||
* `options.failOnError` **[boolean][7]** by default halt processing and raise an error when loading invalid images.
|
||||
* `options.failOnError` **[boolean][14]** by default halt processing and raise an error when loading invalid images.
|
||||
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.limitInputPixels` **([number][15] | [boolean][14])** Do not process input images where the number of pixels
|
||||
(width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
||||
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.sequentialRead` **[boolean][7]** Set this to `true` to use sequential rather than random access where possible.
|
||||
* `options.sequentialRead` **[boolean][14]** Set this to `true` to use sequential rather than random access where possible.
|
||||
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
|
||||
* `options.density` **[number][8]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
|
||||
* `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`)
|
||||
* `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.subifd` **[number][8]** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default `-1`)
|
||||
* `options.level` **[number][8]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
||||
* `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.raw` **[Object][6]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||
* `options.density` **[number][15]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
|
||||
* `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.page` **[number][15]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
|
||||
* `options.subifd` **[number][15]** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default `-1`)
|
||||
* `options.level` **[number][15]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
||||
* `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` **[Object][13]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||
|
||||
* `options.raw.width` **[number][8]?** integral number of pixels wide.
|
||||
* `options.raw.height` **[number][8]?** integral number of pixels high.
|
||||
* `options.raw.channels` **[number][8]?** integral number of channels, between 1 and 4.
|
||||
* `options.raw.premultiplied` **[boolean][7]?** specifies that the raw input has already been premultiplied, set to `true`
|
||||
* `options.raw.width` **[number][15]?** integral number of pixels wide.
|
||||
* `options.raw.height` **[number][15]?** integral number of pixels high.
|
||||
* `options.raw.channels` **[number][15]?** integral number of channels, between 1 and 4.
|
||||
* `options.raw.premultiplied` **[boolean][14]?** specifies that the raw input has already been premultiplied, set to `true`
|
||||
to avoid sharp premultiplying the image. (optional, default `false`)
|
||||
* `options.create` **[Object][6]?** describes a new image to be created.
|
||||
* `options.create` **[Object][13]?** describes a new image to be created.
|
||||
|
||||
* `options.create.width` **[number][8]?** integral number of pixels wide.
|
||||
* `options.create.height` **[number][8]?** integral number of pixels high.
|
||||
* `options.create.channels` **[number][8]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
|
||||
* `options.create.background` **([string][5] | [Object][6])?** parsed by the [color][9] module to extract values for red, green, blue and alpha.
|
||||
* `options.create.noise` **[Object][6]?** describes a noise to be created.
|
||||
* `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][5]?** type of generated noise, currently only `gaussian` is supported.
|
||||
* `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.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
|
||||
|
||||
@@ -126,9 +127,9 @@ await sharp({
|
||||
}).toFile('noise.png');
|
||||
```
|
||||
|
||||
* Throws **[Error][10]** Invalid parameters
|
||||
* Throws **[Error][17]** Invalid parameters
|
||||
|
||||
Returns **[Sharp][11]**
|
||||
Returns **[Sharp][18]**
|
||||
|
||||
## clone
|
||||
|
||||
@@ -196,7 +197,7 @@ Promise.all(promises)
|
||||
});
|
||||
```
|
||||
|
||||
Returns **[Sharp][11]**
|
||||
Returns **[Sharp][18]**
|
||||
|
||||
[1]: http://nodejs.org/api/stream.html#stream_class_stream_duplex
|
||||
|
||||
@@ -206,16 +207,30 @@ Returns **[Sharp][11]**
|
||||
|
||||
[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
|
||||
|
||||
@@ -22,6 +22,8 @@ A `Promise` is returned when `callback` is not provided.
|
||||
* `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
|
||||
* `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
|
||||
* `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||
* `orientation`: Number value of the EXIF Orientation header, if present
|
||||
@@ -75,6 +77,9 @@ A `Promise` is returned when `callback` is not provided.
|
||||
* `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)
|
||||
|
||||
**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
|
||||
|
||||
* `callback` **[Function][4]?** called with the arguments `(err, stats)`
|
||||
@@ -95,6 +100,14 @@ const { entropy, sharpness, dominant } = await sharp(input).stats();
|
||||
const { r, g, b } = dominant;
|
||||
```
|
||||
|
||||
```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
|
||||
|
||||
@@ -176,6 +176,8 @@ Returns **Sharp**
|
||||
|
||||
Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
||||
|
||||
See also [removeAlpha][8].
|
||||
|
||||
### Parameters
|
||||
|
||||
* `options` **[Object][2]?**
|
||||
@@ -186,7 +188,7 @@ Merge alpha transparency channel, if any, with a background, then remove the alp
|
||||
|
||||
```javascript
|
||||
await sharp(rgbaInput)
|
||||
.flatten({background: '#F0A703' })
|
||||
.flatten({ background: '#F0A703' })
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
@@ -219,7 +221,9 @@ Produce the "negative" of the image.
|
||||
|
||||
### 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**
|
||||
|
||||
@@ -243,6 +247,33 @@ Alternative spelling of normalise.
|
||||
|
||||
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 the image with the specified kernel.
|
||||
@@ -252,7 +283,7 @@ Convolve the image with the specified kernel.
|
||||
* `kernel` **[Object][2]**
|
||||
|
||||
* `kernel.width` **[number][1]** width of the kernel in pixels.
|
||||
* `kernel.height` **[number][1]** width of the kernel in pixels.
|
||||
* `kernel.height` **[number][1]** height of the kernel in pixels.
|
||||
* `kernel.kernel` **[Array][7]<[number][1]>** Array of length `width*height` containing the kernel values.
|
||||
* `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`)
|
||||
@@ -304,7 +335,7 @@ the selected bitwise boolean `operation` between the corresponding pixels of the
|
||||
|
||||
### 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.
|
||||
* `options` **[Object][2]?**
|
||||
|
||||
@@ -421,4 +452,8 @@ Returns **Sharp**
|
||||
|
||||
[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
|
||||
|
||||
@@ -379,7 +379,7 @@ most web browsers do not display these properly.
|
||||
* `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.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
|
||||
|
||||
<!---->
|
||||
|
||||
@@ -406,7 +406,7 @@ globally-installed libvips compiled with support for libheif, libde265 and x265.
|
||||
* `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.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
|
||||
|
||||
<!---->
|
||||
|
||||
@@ -420,30 +420,36 @@ Returns **Sharp**
|
||||
|
||||
## 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.
|
||||
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
|
||||
|
||||
```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')
|
||||
.raw()
|
||||
.toBuffer({ resolveWithObject: true });
|
||||
```
|
||||
|
||||
```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')
|
||||
.ensureAlpha()
|
||||
.extractChannel(3)
|
||||
.toColourspace('b-w')
|
||||
.raw()
|
||||
.raw({ depth: 'ushort' })
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
Returns **Sharp**
|
||||
* Throws **[Error][4]** Invalid options
|
||||
|
||||
## tile
|
||||
|
||||
|
||||
@@ -1,9 +1,57 @@
|
||||
# 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*
|
||||
|
||||
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`.
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
".*",
|
||||
"build.js",
|
||||
"firebase.json",
|
||||
"*.md",
|
||||
"image/**",
|
||||
"search-index/**"
|
||||
],
|
||||
@@ -14,10 +13,9 @@
|
||||
{
|
||||
"source": "**",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Cache-Control",
|
||||
"value": "max-age=86400"
|
||||
}
|
||||
{ "key": "Cache-Control", "value": "max-age=86400" },
|
||||
{ "key": "X-Content-Type-Options", "value": "nosniff" },
|
||||
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -212,3 +212,9 @@ 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 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;
|
||||
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'
|
||||
https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.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" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
|
||||
<link rel="author" href="/humans.txt" type="text/plain">
|
||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.28.2/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://www.google-analytics.com">
|
||||
<script type="application/ld+json">
|
||||
@@ -105,7 +103,7 @@
|
||||
.then(function (entries) {
|
||||
const results = [];
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.k.includes(keyword)) {
|
||||
if (entry.k.includes(keyword.trim().toLowerCase())) {
|
||||
results.push({
|
||||
title: entry.t,
|
||||
link: entry.l,
|
||||
@@ -139,7 +137,6 @@
|
||||
docuteApiTitlePlugin,
|
||||
docuteApiSearchPlugin
|
||||
],
|
||||
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.28.2/docs',
|
||||
nav: [
|
||||
{
|
||||
title: 'Funding',
|
||||
|
||||
@@ -10,20 +10,20 @@ yarn add sharp
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Node.js v10+
|
||||
* Node.js >= 12.13.0
|
||||
|
||||
## Prebuilt binaries
|
||||
|
||||
Ready-compiled sharp and libvips binaries are provided for use with
|
||||
Node.js v10+ on the most common platforms:
|
||||
Ready-compiled sharp and libvips binaries are provided for use on the most common platforms:
|
||||
|
||||
* macOS x64 (>= 10.13)
|
||||
* macOS ARM64
|
||||
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
|
||||
* Linux ARM64 (glibc >= 2.29, musl >= 1.1.24)
|
||||
* Windows x64
|
||||
* 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`.
|
||||
|
||||
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:
|
||||
|
||||
* macOS ARM64
|
||||
* Linux ARMv6
|
||||
* Linux ARMv7 (glibc >= 2.28)
|
||||
* Windows ARM64
|
||||
@@ -61,15 +60,9 @@ Check the output of running `npm install --verbose sharp` for useful error messa
|
||||
|
||||
## 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,
|
||||
which requires Xcode and Python - see
|
||||
[building from source](#building-from-source).
|
||||
|
||||
When this new ARM64 CPU is made freely available
|
||||
to open source projects via a CI service
|
||||
then prebuilt sharp binaries can also be provided.
|
||||
Prebuilt libvips binaries were provided for macOS on ARM64 from sharp v0.28.0.
|
||||
|
||||
## Cross-platform
|
||||
|
||||
@@ -138,14 +131,10 @@ To install the prebuilt libvips binaries from a custom URL,
|
||||
set the `sharp_libvips_binary_host` npm config option
|
||||
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
|
||||
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+)
|
||||
|
||||
The version subpath and file name are appended to these.
|
||||
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
|
||||
`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.gz`.
|
||||
`https://hostname/path/v1.2.3/libvips-1.2.3-platform-arch.tar.br`.
|
||||
|
||||
See the Chinese mirror below for a further example.
|
||||
|
||||
@@ -210,32 +199,18 @@ to `false` when using the `yarn` package manager.
|
||||
|
||||
## AWS Lambda
|
||||
|
||||
The binaries in the `node_modules` directory of the
|
||||
The `node_modules` directory of the
|
||||
[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),
|
||||
run the following commands:
|
||||
run the following additional command after `npm install`:
|
||||
|
||||
macOS:
|
||||
```sh
|
||||
rm -rf node_modules/sharp
|
||||
npm install
|
||||
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.
|
||||
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
|
||||
|
||||
* [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*".
|
||||
* [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.
|
||||
|
||||
## The task
|
||||
@@ -19,21 +21,23 @@ then compress to JPEG at a "quality" setting of 80.
|
||||
## Test environment
|
||||
|
||||
* AWS EC2 eu-west-1 [c5ad.xlarge](https://aws.amazon.com/ec2/instance-types/c5/) (4x AMD EPYC 7R32)
|
||||
* Ubuntu 20.10 (ami-03f10415e8b0bfb86)
|
||||
* Node.js v14.16.0
|
||||
* Ubuntu 21.04 (ami-0d7626a9c2ceab1ac)
|
||||
* Node.js 16.6.2
|
||||
|
||||
## Results
|
||||
|
||||
| Module | Input | Output | Ops/sec | Speed-up |
|
||||
| :----------------- | :----- | :----- | ------: | -------: |
|
||||
| jimp | buffer | buffer | 0.78 | 1.0 |
|
||||
| mapnik | buffer | buffer | 3.39 | 4.3 |
|
||||
| gm | buffer | buffer | 7.84 | 10.1 |
|
||||
| gm | file | file | 9.24 | 11.8 |
|
||||
| imagemagick | file | file | 9.37 | 12.0 |
|
||||
| sharp | stream | stream | 26.84 | 34.4 |
|
||||
| sharp | file | file | 29.76 | 38.2 |
|
||||
| sharp | buffer | buffer | 31.60 | 40.5 |
|
||||
| jimp | buffer | buffer | 0.83 | 1.0 |
|
||||
| squoosh-cli | file | file | 1.09 | 1.3 |
|
||||
| squoosh-lib | buffer | buffer | 1.83 | 2.2 |
|
||||
| mapnik | buffer | buffer | 3.41 | 4.1 |
|
||||
| gm | buffer | buffer | 8.34 | 10.0 |
|
||||
| imagemagick | file | file | 8.67 | 10.4 |
|
||||
| gm | file | file | 8.82 | 10.6 |
|
||||
| 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)
|
||||
and using 8+ core machines, especially those with larger L1/L2 CPU caches.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { extractDescription, extractKeywords } = require('./extract');
|
||||
const { extractDescription, extractKeywords, extractParameters } = require('./extract');
|
||||
|
||||
const searchIndex = [];
|
||||
|
||||
@@ -37,18 +37,19 @@ for (const match of matches) {
|
||||
].forEach((section) => {
|
||||
const contents = fs.readFileSync(path.join(__dirname, '..', `api-${section}.md`), 'utf8');
|
||||
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) {
|
||||
const { title, firstparagraph } = match.groups;
|
||||
const { title, firstparagraph, parameters } = match.groups;
|
||||
const description = firstparagraph.startsWith('###')
|
||||
? 'Constructor'
|
||||
: extractDescription(firstparagraph);
|
||||
const parameterNames = parameters ? extractParameters(parameters) : '';
|
||||
|
||||
searchIndex.push({
|
||||
t: title,
|
||||
d: description,
|
||||
k: extractKeywords(`${title} ${description}`),
|
||||
k: extractKeywords(`${title} ${description} ${parameterNames}`),
|
||||
l: `/api-${section}#${title.toLowerCase()}`
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ const stopWords = require('./stop-words');
|
||||
|
||||
const extractDescription = (str) =>
|
||||
str
|
||||
.replace(/### Examples.*/sg, '')
|
||||
.replace(/\(http[^)]+/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/[^A-Za-z0-9_/\-,. ]/g, '')
|
||||
@@ -11,6 +12,11 @@ const extractDescription = (str) =>
|
||||
.substr(0, 180)
|
||||
.trim();
|
||||
|
||||
const extractParameters = (str) =>
|
||||
[...str.matchAll(/options\.(?<name>[^.`]+)/gs)]
|
||||
.map((match) => match.groups.name)
|
||||
.join(' ');
|
||||
|
||||
const extractKeywords = (str) =>
|
||||
[
|
||||
...new Set(
|
||||
@@ -21,4 +27,4 @@ const extractKeywords = (str) =>
|
||||
)
|
||||
].join(' ');
|
||||
|
||||
module.exports = { extractDescription, extractKeywords };
|
||||
module.exports = { extractDescription, extractKeywords, extractParameters };
|
||||
|
||||
@@ -17,6 +17,7 @@ module.exports = [
|
||||
'before',
|
||||
'both',
|
||||
'call',
|
||||
'callback',
|
||||
'can',
|
||||
'containing',
|
||||
'default',
|
||||
@@ -26,6 +27,7 @@ module.exports = [
|
||||
'ensure',
|
||||
'etc',
|
||||
'every',
|
||||
'except',
|
||||
'for',
|
||||
'from',
|
||||
'get',
|
||||
@@ -49,12 +51,16 @@ module.exports = [
|
||||
'occur',
|
||||
'occurs',
|
||||
'options',
|
||||
'other',
|
||||
'out',
|
||||
'over',
|
||||
'perform',
|
||||
'performs',
|
||||
'provide',
|
||||
'provided',
|
||||
'ready',
|
||||
'requires',
|
||||
'returned',
|
||||
'same',
|
||||
'see',
|
||||
'set',
|
||||
@@ -67,9 +73,11 @@ module.exports = [
|
||||
'supported',
|
||||
'sure',
|
||||
'take',
|
||||
'than',
|
||||
'that',
|
||||
'the',
|
||||
'their',
|
||||
'then',
|
||||
'there',
|
||||
'therefore',
|
||||
'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 libvips = require('../lib/libvips');
|
||||
const platform = require('../lib/platform');
|
||||
|
||||
const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
|
||||
|
||||
const platform = process.env.npm_config_platform || process.platform;
|
||||
if (platform === 'win32') {
|
||||
const buildDir = path.join(__dirname, '..', 'build');
|
||||
const buildReleaseDir = path.join(buildDir, 'Release');
|
||||
const platformAndArch = platform();
|
||||
|
||||
if (platformAndArch.startsWith('win32')) {
|
||||
const buildReleaseDir = path.join(__dirname, '..', 'build', 'Release');
|
||||
libvips.log(`Creating ${buildReleaseDir}`);
|
||||
try {
|
||||
libvips.mkdirSync(buildDir);
|
||||
libvips.mkdirSync(buildReleaseDir);
|
||||
} 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}`);
|
||||
try {
|
||||
fs
|
||||
|
||||
@@ -24,6 +24,7 @@ const minimumGlibcVersionByArch = {
|
||||
|
||||
const hasSharpPrebuild = [
|
||||
'darwin-x64',
|
||||
'darwin-arm64',
|
||||
'linux-arm64',
|
||||
'linux-x64',
|
||||
'linuxmusl-x64',
|
||||
@@ -35,7 +36,6 @@ const hasSharpPrebuild = [
|
||||
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
||||
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 supportsBrotli = ('BrotliDecompress' in zlib);
|
||||
const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
|
||||
|
||||
const fail = function (err) {
|
||||
@@ -43,7 +43,6 @@ const fail = function (err) {
|
||||
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('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');
|
||||
process.exit(1);
|
||||
};
|
||||
@@ -57,9 +56,7 @@ const handleError = function (err) {
|
||||
};
|
||||
|
||||
const extractTarball = function (tarPath, platformAndArch) {
|
||||
const vendorPath = path.join(__dirname, '..', 'vendor');
|
||||
libvips.mkdirSync(vendorPath);
|
||||
const versionedVendorPath = path.join(vendorPath, minimumLibvipsVersion);
|
||||
const versionedVendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch);
|
||||
libvips.mkdirSync(versionedVendorPath);
|
||||
|
||||
const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;
|
||||
@@ -69,7 +66,7 @@ const extractTarball = function (tarPath, platformAndArch) {
|
||||
|
||||
stream.pipeline(
|
||||
fs.createReadStream(tarPath),
|
||||
supportsBrotli ? new zlib.BrotliDecompress() : new zlib.Gunzip(),
|
||||
new zlib.BrotliDecompress(),
|
||||
tarFs.extract(versionedVendorPath, { ignore }),
|
||||
function (err) {
|
||||
if (err) {
|
||||
@@ -122,10 +119,8 @@ try {
|
||||
handleError(new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`));
|
||||
}
|
||||
|
||||
const extension = supportsBrotli ? 'br' : 'gz';
|
||||
|
||||
// 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);
|
||||
if (fs.existsSync(tarPathCache)) {
|
||||
libvips.log(`Using cached ${tarPathCache}`);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* See also {@link /api-operation#flatten|flatten}.
|
||||
*
|
||||
* @example
|
||||
* sharp('rgba.png')
|
||||
* .removeAlpha()
|
||||
@@ -70,13 +72,17 @@ function ensureAlpha (alpha) {
|
||||
* Extract a single channel from a multi-channel image.
|
||||
*
|
||||
* @example
|
||||
* sharp(input)
|
||||
* // green.jpg is a greyscale image containing the green channel of the input
|
||||
* await sharp(input)
|
||||
* .extractChannel('green')
|
||||
* .toColourspace('b-w')
|
||||
* .toFile('green.jpg', function(err, info) {
|
||||
* // info.channels === 1
|
||||
* // green.jpg is a greyscale image containing the green channel of the input
|
||||
* });
|
||||
* .toFile('green.jpg');
|
||||
*
|
||||
* @example
|
||||
* // 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`.
|
||||
* @returns {Sharp}
|
||||
@@ -92,7 +98,7 @@ function extractChannel (channel) {
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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')
|
||||
* .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}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
@@ -119,6 +158,8 @@ module.exports = function (Sharp) {
|
||||
tint,
|
||||
greyscale,
|
||||
grayscale,
|
||||
pipelineColourspace,
|
||||
pipelineColorspace,
|
||||
toColourspace,
|
||||
toColorspace,
|
||||
// Private
|
||||
|
||||
@@ -89,6 +89,8 @@ const blend = {
|
||||
* @param {Number} [images[].raw.width]
|
||||
* @param {Number} [images[].raw.height]
|
||||
* @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}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
|
||||
@@ -5,32 +5,7 @@ const stream = require('stream');
|
||||
const is = require('./is');
|
||||
|
||||
require('./libvips').hasVendoredLibvips();
|
||||
|
||||
/* 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);
|
||||
}
|
||||
require('./sharp');
|
||||
|
||||
// Use NODE_DEBUG=sharp to enable libvips warnings
|
||||
const debuglog = util.debuglog('sharp');
|
||||
@@ -117,8 +92,9 @@ const debuglog = util.debuglog('sharp');
|
||||
* }
|
||||
* }).toFile('noise.png');
|
||||
*
|
||||
* @param {(Buffer|Uint8Array|Uint8ClampedArray|string)} [input] - if present, can be
|
||||
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
||||
* @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 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.
|
||||
* 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.
|
||||
@@ -204,6 +180,7 @@ const Sharp = function (input, options) {
|
||||
flatten: false,
|
||||
flattenBackground: [0, 0, 0],
|
||||
negate: false,
|
||||
negateAlpha: true,
|
||||
medianSize: 0,
|
||||
blurSigma: 0,
|
||||
sharpenSigma: 0,
|
||||
@@ -216,6 +193,9 @@ const Sharp = function (input, options) {
|
||||
gammaOut: 0,
|
||||
greyscale: false,
|
||||
normalise: false,
|
||||
claheWidth: 0,
|
||||
claheHeight: 0,
|
||||
claheMaxSlope: 3,
|
||||
brightness: 1,
|
||||
saturation: 1,
|
||||
hue: 0,
|
||||
@@ -226,6 +206,7 @@ const Sharp = function (input, options) {
|
||||
removeAlpha: false,
|
||||
ensureAlpha: -1,
|
||||
colourspace: 'srgb',
|
||||
colourspaceInput: 'last',
|
||||
composite: [],
|
||||
// output
|
||||
fileOut: '',
|
||||
@@ -273,7 +254,8 @@ const Sharp = function (input, options) {
|
||||
heifLossless: false,
|
||||
heifCompression: 'av1',
|
||||
heifSpeed: 5,
|
||||
heifChromaSubsampling: '4:2:0',
|
||||
heifChromaSubsampling: '4:4:4',
|
||||
rawDepth: 'uchar',
|
||||
tileSize: 256,
|
||||
tileOverlap: 0,
|
||||
tileContainer: 'fs',
|
||||
|
||||
48
lib/input.js
@@ -2,7 +2,7 @@
|
||||
|
||||
const color = require('color');
|
||||
const is = require('./is');
|
||||
const sharp = require('../build/Release/sharp.node');
|
||||
const sharp = require('./sharp');
|
||||
|
||||
/**
|
||||
* Extract input options, if any, from an object.
|
||||
@@ -34,8 +34,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
throw Error('Input Buffer is empty');
|
||||
}
|
||||
inputDescriptor.buffer = input;
|
||||
} else if (is.uint8Array(input)) {
|
||||
// Uint8Array or Uint8ClampedArray
|
||||
} else if (is.typedArray(input)) {
|
||||
if (input.length === 0) {
|
||||
throw Error('Input Bit Array is empty');
|
||||
}
|
||||
@@ -104,6 +103,37 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
inputDescriptor.rawHeight = inputOptions.raw.height;
|
||||
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 {
|
||||
throw new Error('Expected width, height and channels for raw pixel input');
|
||||
}
|
||||
@@ -271,6 +301,8 @@ function _isStreamInput () {
|
||||
* - `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
|
||||
* - `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
|
||||
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||
* - `orientation`: Number value of the EXIF Orientation header, if present
|
||||
@@ -356,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)
|
||||
* - `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
|
||||
* const image = sharp(inputJpg);
|
||||
* image
|
||||
@@ -368,6 +403,13 @@ function metadata (callback) {
|
||||
* const { entropy, sharpness, dominant } = await sharp(input).stats();
|
||||
* 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)`
|
||||
* @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
|
||||
*/
|
||||
const uint8Array = function (val) {
|
||||
// allow both since Uint8ClampedArray simply clamps the values between 0-255
|
||||
return val instanceof Uint8Array || val instanceof Uint8ClampedArray;
|
||||
const typedArray = function (val) {
|
||||
if (defined(val)) {
|
||||
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,
|
||||
bool: bool,
|
||||
buffer: buffer,
|
||||
uint8Array: uint8Array,
|
||||
typedArray: typedArray,
|
||||
string: string,
|
||||
number: number,
|
||||
integer: integer,
|
||||
|
||||
@@ -21,9 +21,9 @@ const spawnSyncOptions = {
|
||||
|
||||
const mkdirSync = function (dirPath) {
|
||||
try {
|
||||
fs.mkdirSync(dirPath);
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
} catch (err) {
|
||||
/* istanbul ignore if */
|
||||
/* istanbul ignore next */
|
||||
if (err.code !== 'EEXIST') {
|
||||
throw err;
|
||||
}
|
||||
@@ -67,23 +67,8 @@ const globalLibvipsVersion = function () {
|
||||
};
|
||||
|
||||
const hasVendoredLibvips = function () {
|
||||
const currentPlatformId = platform();
|
||||
const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion);
|
||||
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 vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
|
||||
return fs.existsSync(vendorPath);
|
||||
};
|
||||
|
||||
const pkgConfigPath = function () {
|
||||
|
||||
@@ -270,9 +270,11 @@ function blur (sigma) {
|
||||
/**
|
||||
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
||||
*
|
||||
* See also {@link /api-channel#removealpha|removeAlpha}.
|
||||
*
|
||||
* @example
|
||||
* await sharp(rgbaInput)
|
||||
* .flatten({background: '#F0A703' })
|
||||
* .flatten({ background: '#F0A703' })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object} [options]
|
||||
@@ -323,11 +325,19 @@ function gamma (gamma, gammaOut) {
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
function negate (negate) {
|
||||
this.options.negate = is.bool(negate) ? negate : true;
|
||||
function negate (options) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -350,6 +360,47 @@ function normalize (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.
|
||||
*
|
||||
@@ -368,7 +419,7 @@ function normalize (normalize) {
|
||||
*
|
||||
* @param {Object} kernel
|
||||
* @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 {number} [kernel.scale=sum] - the scale 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,
|
||||
normalise,
|
||||
normalize,
|
||||
clahe,
|
||||
convolve,
|
||||
threshold,
|
||||
boolean,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const is = require('./is');
|
||||
const sharp = require('../build/Release/sharp.node');
|
||||
const sharp = require('./sharp');
|
||||
|
||||
const formats = new Map([
|
||||
['heic', 'heif'],
|
||||
@@ -660,7 +660,7 @@ function tiff (options) {
|
||||
* @param {number} [options.quality=50] - quality, integer 1-100
|
||||
* @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 {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}
|
||||
* @throws {Error} Invalid options
|
||||
*/
|
||||
@@ -681,7 +681,7 @@ function avif (options) {
|
||||
* @param {string} [options.compression='av1'] - compression format: av1, hevc
|
||||
* @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 {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}
|
||||
* @throws {Error} Invalid options
|
||||
*/
|
||||
@@ -727,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.
|
||||
* Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
|
||||
*
|
||||
* @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')
|
||||
* .raw()
|
||||
* .toBuffer({ resolveWithObject: true });
|
||||
*
|
||||
* @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')
|
||||
* .ensureAlpha()
|
||||
* .extractChannel(3)
|
||||
* .toColourspace('b-w')
|
||||
* .raw()
|
||||
* .raw({ depth: 'ushort' })
|
||||
* .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');
|
||||
}
|
||||
|
||||
|
||||
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 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.
|
||||
|
||||
30
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images",
|
||||
"version": "0.28.2",
|
||||
"version": "0.29.0",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://github.com/lovell/sharp",
|
||||
"contributors": [
|
||||
@@ -76,10 +76,12 @@
|
||||
"Leon Radley <leon@radley.se>",
|
||||
"alza54 <alza54@thiocod.in>",
|
||||
"Jacob Smith <jacob@frende.me>",
|
||||
"Michael Nutt <michael@nutt.im>"
|
||||
"Michael Nutt <michael@nutt.im>",
|
||||
"Brad Parham <baparham@gmail.com>",
|
||||
"Taneli Vatanen <taneli.vatanen@gmail.com>"
|
||||
],
|
||||
"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.*",
|
||||
"test": "npm run test-lint && npm run test-unit && npm run test-licensing",
|
||||
"test-lint": "semistandard && cpplint",
|
||||
@@ -120,45 +122,45 @@
|
||||
"vips"
|
||||
],
|
||||
"dependencies": {
|
||||
"color": "^3.1.3",
|
||||
"color": "^4.0.1",
|
||||
"detect-libc": "^1.0.3",
|
||||
"node-addon-api": "^3.1.0",
|
||||
"prebuild-install": "^6.1.2",
|
||||
"node-addon-api": "^4.0.0",
|
||||
"prebuild-install": "^6.1.4",
|
||||
"semver": "^7.3.5",
|
||||
"simple-get": "^3.1.0",
|
||||
"tar-fs": "^2.1.1",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"async": "^3.2.0",
|
||||
"async": "^3.2.1",
|
||||
"cc": "^3.0.1",
|
||||
"decompress-zip": "^0.3.3",
|
||||
"documentation": "^13.2.5",
|
||||
"exif-reader": "^1.0.3",
|
||||
"icc": "^2.0.0",
|
||||
"license-checker": "^25.0.1",
|
||||
"mocha": "^8.4.0",
|
||||
"mock-fs": "^4.14.0",
|
||||
"mocha": "^9.0.3",
|
||||
"mock-fs": "^5.0.0",
|
||||
"nyc": "^15.1.0",
|
||||
"prebuild": "^10.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"semistandard": "^16.0.0"
|
||||
"semistandard": "^16.0.1"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"config": {
|
||||
"libvips": "8.10.6",
|
||||
"libvips": "8.11.3",
|
||||
"runtime": "napi",
|
||||
"target": 3
|
||||
"target": 5
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=12.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"binary": {
|
||||
"napi_versions": [
|
||||
3
|
||||
5
|
||||
]
|
||||
},
|
||||
"semistandard": {
|
||||
|
||||
@@ -92,6 +92,9 @@ namespace sharp {
|
||||
}
|
||||
// Raw pixel input
|
||||
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->rawWidth = AttrAsUint32(input, "rawWidth");
|
||||
descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
|
||||
@@ -297,7 +300,7 @@ namespace sharp {
|
||||
if (descriptor->rawChannels > 0) {
|
||||
// Raw, uncompressed pixel data
|
||||
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) {
|
||||
image.get_image()->Type = VIPS_INTERPRETATION_B_W;
|
||||
} else {
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
// Verify platform and compiler compatibility
|
||||
|
||||
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 10 && VIPS_MICRO_VERSION < 6)
|
||||
#error "libvips version 8.10.6+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 11) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 11 && VIPS_MICRO_VERSION < 3)
|
||||
#error "libvips version 8.11.3+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
#endif
|
||||
|
||||
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
||||
@@ -54,6 +54,7 @@ namespace sharp {
|
||||
size_t bufferLength;
|
||||
bool isBuffer;
|
||||
double density;
|
||||
VipsBandFormat rawDepth;
|
||||
int rawChannels;
|
||||
int rawWidth;
|
||||
int rawHeight;
|
||||
@@ -78,6 +79,7 @@ namespace sharp {
|
||||
bufferLength(0),
|
||||
isBuffer(FALSE),
|
||||
density(72.0),
|
||||
rawDepth(VIPS_FORMAT_UCHAR),
|
||||
rawChannels(0),
|
||||
rawWidth(0),
|
||||
rawHeight(0),
|
||||
|
||||
@@ -110,19 +110,6 @@ VSource::new_from_options( const char *options )
|
||||
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::new_to_descriptor( int descriptor )
|
||||
{
|
||||
@@ -162,17 +149,4 @@ VTarget::new_to_memory()
|
||||
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
|
||||
|
||||
@@ -51,6 +51,12 @@
|
||||
|
||||
VIPS_NAMESPACE_START
|
||||
|
||||
/**
|
||||
* \namespace vips
|
||||
*
|
||||
* General docs for the vips namespace.
|
||||
*/
|
||||
|
||||
std::vector<double>
|
||||
to_vectorv( int n, ... )
|
||||
{
|
||||
@@ -140,6 +146,20 @@ VOption::set( const char *name, int value )
|
||||
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
|
||||
VOption *
|
||||
VOption::set( const char *name, double value )
|
||||
@@ -167,39 +187,17 @@ VOption::set( const char *name, const char *value )
|
||||
return( this );
|
||||
}
|
||||
|
||||
// input image
|
||||
// input vips object (image, source, target, etc. etc.)
|
||||
VOption *
|
||||
VOption::set( const char *name, const VImage value )
|
||||
VOption::set( const char *name, const VObject value )
|
||||
{
|
||||
Pair *pair = new Pair( name );
|
||||
VipsObject *object = value.get_object();
|
||||
GType type = G_OBJECT_TYPE( object );
|
||||
|
||||
pair->input = true;
|
||||
g_value_init( &pair->value, VIPS_TYPE_IMAGE );
|
||||
g_value_set_object( &pair->value, value.get_image() );
|
||||
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];
|
||||
|
||||
g_value_init( &pair->value, type );
|
||||
g_value_set_object( &pair->value, object );
|
||||
options.push_back( pair );
|
||||
|
||||
return( this );
|
||||
@@ -229,6 +227,30 @@ VOption::set( const char *name, std::vector<int> value )
|
||||
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
|
||||
VOption *
|
||||
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 );
|
||||
}
|
||||
|
||||
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::new_matrix( int width, int height )
|
||||
{
|
||||
|
||||
@@ -60,17 +60,4 @@ VInterpolate::new_from_name( const char *name, VOption *options )
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 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!
|
||||
|
||||
VImage VImage::CMC2LCh( VOption *options ) const
|
||||
@@ -1065,6 +1065,18 @@ VImage VImage::fitsload( const char *filename, VOption *options )
|
||||
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
|
||||
{
|
||||
call( "fitssave",
|
||||
@@ -1656,6 +1668,70 @@ VImage VImage::join( VImage in2, VipsDirection direction, VOption *options ) con
|
||||
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 out;
|
||||
@@ -1727,6 +1803,70 @@ void VImage::jpegsave_target( VTarget target, VOption *options ) const
|
||||
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 mask;
|
||||
@@ -2284,6 +2424,18 @@ VImage VImage::niftiload( const char *filename, VOption *options )
|
||||
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
|
||||
{
|
||||
call( "niftisave",
|
||||
@@ -2316,6 +2468,18 @@ VImage VImage::openslideload( const char *filename, VOption *options )
|
||||
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 out;
|
||||
@@ -3388,6 +3552,18 @@ VImage VImage::vipsload( const char *filename, VOption *options )
|
||||
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
|
||||
{
|
||||
call( "vipssave",
|
||||
@@ -3396,6 +3572,14 @@ void VImage::vipssave( const char *filename, VOption *options ) const
|
||||
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 out;
|
||||
|
||||
@@ -90,6 +90,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
baton->subifds = image.get_int(VIPS_META_N_SUBIFDS);
|
||||
}
|
||||
baton->hasProfile = sharp::HasProfile(image);
|
||||
if (image.get_typeof("background") == VIPS_TYPE_ARRAY_DOUBLE) {
|
||||
baton->background = image.get_array_double("background");
|
||||
}
|
||||
// Derived attributes
|
||||
baton->hasAlpha = sharp::HasAlpha(image);
|
||||
baton->orientation = sharp::ExifOrientation(image);
|
||||
@@ -209,6 +212,17 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
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("hasAlpha", baton->hasAlpha);
|
||||
if (baton->orientation > 0) {
|
||||
|
||||
@@ -42,6 +42,7 @@ struct MetadataBaton {
|
||||
std::string compression;
|
||||
std::vector<std::pair<int, int>> levels;
|
||||
int subifds;
|
||||
std::vector<double> background;
|
||||
bool hasProfile;
|
||||
bool hasAlpha;
|
||||
int orientation;
|
||||
|
||||
@@ -92,6 +92,13 @@ namespace sharp {
|
||||
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
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -275,4 +295,16 @@ namespace sharp {
|
||||
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
|
||||
|
||||
@@ -35,11 +35,21 @@ namespace sharp {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@@ -92,6 +102,11 @@ namespace sharp {
|
||||
*/
|
||||
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
|
||||
|
||||
#endif // SRC_OPERATIONS_H_
|
||||
|
||||
@@ -67,6 +67,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
vips::VImage image;
|
||||
sharp::ImageType inputImageType;
|
||||
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
|
||||
image = sharp::EnsureColourspace(image, baton->colourspaceInput);
|
||||
|
||||
// Calculate angle of rotation
|
||||
VipsAngle rotation;
|
||||
@@ -214,7 +215,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
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,
|
||||
// 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_factor = 1;
|
||||
@@ -227,7 +228,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
xshrink == yshrink && xshrink >= 2 * shrink_on_load_factor &&
|
||||
(inputImageType == sharp::ImageType::JPEG || inputImageType == sharp::ImageType::WEBP) &&
|
||||
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0 &&
|
||||
image.width() > 3 && image.height() > 3
|
||||
baton->colourspaceInput == VIPS_INTERPRETATION_LAST &&
|
||||
image.width() > 3 && image.height() > 3 && baton->input->pages == 1
|
||||
) {
|
||||
if (xshrink >= 8 * shrink_on_load_factor) {
|
||||
xfactor = xfactor / 8;
|
||||
@@ -325,7 +327,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
// Negate the colours in the image
|
||||
if (baton->negate) {
|
||||
image = image.invert();
|
||||
image = sharp::Negate(image, baton->negateAlpha);
|
||||
}
|
||||
|
||||
// Gamma encoding (darken)
|
||||
@@ -345,6 +347,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
bool const shouldApplyMedian = baton->medianSize > 0;
|
||||
bool const shouldComposite = !baton->composite.empty();
|
||||
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)) {
|
||||
image = sharp::EnsureAlpha(image, 1);
|
||||
@@ -410,6 +413,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
|
||||
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
|
||||
joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
|
||||
image = image.bandjoin(joinImage);
|
||||
}
|
||||
image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
|
||||
@@ -551,7 +555,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
for (Composite *composite : baton->composite) {
|
||||
VImage compositeImage;
|
||||
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
|
||||
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
|
||||
throw vips::VError("Image to composite must have same dimensions or smaller");
|
||||
@@ -650,11 +655,17 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
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
|
||||
if (baton->boolean != nullptr) {
|
||||
VImage booleanImage;
|
||||
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
||||
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
||||
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
|
||||
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
||||
}
|
||||
|
||||
@@ -759,8 +770,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("Q", baton->jpegQuality)
|
||||
->set("interlace", baton->jpegProgressive)
|
||||
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
||||
? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
|
||||
: VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
|
||||
? VIPS_FOREIGN_SUBSAMPLE_OFF
|
||||
: VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
||||
->set("quant_table", baton->jpegQuantisationTable)
|
||||
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
||||
@@ -859,13 +870,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Write HEIF to buffer
|
||||
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
||||
->set("strip", !baton->withMetadata)
|
||||
->set("compression", baton->heifCompression)
|
||||
->set("Q", baton->heifQuality)
|
||||
->set("compression", baton->heifCompression)
|
||||
->set("speed", baton->heifSpeed)
|
||||
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
||||
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
||||
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||
#endif
|
||||
->set("lossless", baton->heifLossless)));
|
||||
baton->bufferOut = static_cast<char*>(area->data);
|
||||
baton->bufferOutLength = area->length;
|
||||
@@ -880,9 +889,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
image = image[0];
|
||||
baton->channels = 1;
|
||||
}
|
||||
if (image.format() != VIPS_FORMAT_UCHAR) {
|
||||
// Cast pixels to uint8 (unsigned char)
|
||||
image = image.cast(VIPS_FORMAT_UCHAR);
|
||||
if (image.format() != baton->rawDepth) {
|
||||
// Cast pixels to requested format
|
||||
image = image.cast(baton->rawDepth);
|
||||
}
|
||||
// Get raw image data
|
||||
baton->bufferOut = static_cast<char*>(image.write_to_memory(&baton->bufferOutLength));
|
||||
@@ -925,8 +934,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("Q", baton->jpegQuality)
|
||||
->set("interlace", baton->jpegProgressive)
|
||||
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
||||
? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
|
||||
: VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
|
||||
? VIPS_FOREIGN_SUBSAMPLE_OFF
|
||||
: VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
||||
->set("quant_table", baton->jpegQuantisationTable)
|
||||
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
||||
@@ -1004,10 +1013,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("Q", baton->heifQuality)
|
||||
->set("compression", baton->heifCompression)
|
||||
->set("speed", baton->heifSpeed)
|
||||
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
||||
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
||||
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||
#endif
|
||||
->set("lossless", baton->heifLossless));
|
||||
baton->formatOut = "heif";
|
||||
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
||||
@@ -1124,6 +1131,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
info.Set("width", static_cast<uint32_t>(width));
|
||||
info.Set("height", static_cast<uint32_t>(height));
|
||||
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);
|
||||
if (baton->hasCropOffset) {
|
||||
info.Set("cropOffsetLeft", static_cast<int32_t>(baton->cropOffsetLeft));
|
||||
@@ -1313,6 +1323,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->flatten = sharp::AttrAsBool(options, "flatten");
|
||||
baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
|
||||
baton->negate = sharp::AttrAsBool(options, "negate");
|
||||
baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
|
||||
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
|
||||
baton->brightness = sharp::AttrAsDouble(options, "brightness");
|
||||
baton->saturation = sharp::AttrAsDouble(options, "saturation");
|
||||
@@ -1330,6 +1341,9 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->linearB = sharp::AttrAsDouble(options, "linearB");
|
||||
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
|
||||
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->angle = sharp::AttrAsInt32(options, "angle");
|
||||
baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
|
||||
@@ -1380,6 +1394,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
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"));
|
||||
if (baton->colourspace == VIPS_INTERPRETATION_ERROR) {
|
||||
baton->colourspace = VIPS_INTERPRETATION_sRGB;
|
||||
@@ -1442,6 +1460,11 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
|
||||
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
|
||||
if (sharp::HasAttr(options, "pageHeight")) {
|
||||
baton->pageHeight = sharp::AttrAsUint32(options, "pageHeight");
|
||||
|
||||
@@ -90,6 +90,7 @@ struct PipelineBaton {
|
||||
bool flatten;
|
||||
std::vector<double> flattenBackground;
|
||||
bool negate;
|
||||
bool negateAlpha;
|
||||
double blurSigma;
|
||||
double brightness;
|
||||
double saturation;
|
||||
@@ -109,6 +110,9 @@ struct PipelineBaton {
|
||||
double gammaOut;
|
||||
bool greyscale;
|
||||
bool normalise;
|
||||
int claheWidth;
|
||||
int claheHeight;
|
||||
int claheMaxSlope;
|
||||
bool useExifOrientation;
|
||||
int angle;
|
||||
double rotationAngle;
|
||||
@@ -165,6 +169,7 @@ struct PipelineBaton {
|
||||
int heifSpeed;
|
||||
std::string heifChromaSubsampling;
|
||||
bool heifLossless;
|
||||
VipsBandFormat rawDepth;
|
||||
std::string err;
|
||||
bool withMetadata;
|
||||
int withMetadataOrientation;
|
||||
@@ -182,6 +187,7 @@ struct PipelineBaton {
|
||||
int extractChannel;
|
||||
bool removeAlpha;
|
||||
double ensureAlpha;
|
||||
VipsInterpretation colourspaceInput;
|
||||
VipsInterpretation colourspace;
|
||||
int pageHeight;
|
||||
std::vector<int> delay;
|
||||
@@ -216,6 +222,7 @@ struct PipelineBaton {
|
||||
flatten(false),
|
||||
flattenBackground{ 0.0, 0.0, 0.0 },
|
||||
negate(false),
|
||||
negateAlpha(true),
|
||||
blurSigma(0.0),
|
||||
brightness(1.0),
|
||||
saturation(1.0),
|
||||
@@ -234,6 +241,9 @@ struct PipelineBaton {
|
||||
gamma(0.0),
|
||||
greyscale(false),
|
||||
normalise(false),
|
||||
claheWidth(0),
|
||||
claheHeight(0),
|
||||
claheMaxSlope(3),
|
||||
useExifOrientation(false),
|
||||
angle(0),
|
||||
rotationAngle(0.0),
|
||||
@@ -287,8 +297,9 @@ struct PipelineBaton {
|
||||
heifQuality(50),
|
||||
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
|
||||
heifSpeed(5),
|
||||
heifChromaSubsampling("4:2:0"),
|
||||
heifChromaSubsampling("4:4:4"),
|
||||
heifLossless(false),
|
||||
rawDepth(VIPS_FORMAT_UCHAR),
|
||||
withMetadata(false),
|
||||
withMetadataOrientation(-1),
|
||||
withMetadataDensity(0.0),
|
||||
@@ -302,6 +313,7 @@ struct PipelineBaton {
|
||||
extractChannel(-1),
|
||||
removeAlpha(false),
|
||||
ensureAlpha(-1.0),
|
||||
colourspaceInput(VIPS_INTERPRETATION_LAST),
|
||||
colourspace(VIPS_INTERPRETATION_LAST),
|
||||
pageHeight(0),
|
||||
delay{-1},
|
||||
|
||||
@@ -8,16 +8,18 @@
|
||||
"test": "node perf && node random && node parallel"
|
||||
},
|
||||
"devDependencies": {
|
||||
"async": "3.2.0",
|
||||
"@squoosh/cli": "0.7.2",
|
||||
"@squoosh/lib": "0.4.0",
|
||||
"async": "3.2.1",
|
||||
"benchmark": "2.1.4",
|
||||
"gm": "1.23.1",
|
||||
"imagemagick": "0.1.3",
|
||||
"jimp": "0.16.1",
|
||||
"mapnik": "4.5.6",
|
||||
"semver": "7.3.4"
|
||||
"mapnik": "4.5.8",
|
||||
"semver": "7.3.5"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "14"
|
||||
"node": "16"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const { exec } = require('child_process');
|
||||
|
||||
const async = require('async');
|
||||
const assert = require('assert');
|
||||
@@ -13,6 +14,7 @@ const gm = require('gm');
|
||||
const imagemagick = require('imagemagick');
|
||||
const mapnik = require('mapnik');
|
||||
const jimp = require('jimp');
|
||||
const squoosh = require('@squoosh/lib');
|
||||
|
||||
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
|
||||
jpegSuite.add('mapnik-file-file', {
|
||||
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 |
5
test/fixtures/index.js
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
const path = require('path');
|
||||
const sharp = require('../../');
|
||||
const maxColourDistance = require('../../build/Release/sharp')._maxColourDistance;
|
||||
const maxColourDistance = require('../../lib/sharp')._maxColourDistance;
|
||||
|
||||
// Helpers
|
||||
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
|
||||
|
||||
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
|
||||
inputPngCompleteTransparency: getPath('full-transparent.png'),
|
||||
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
|
||||
@@ -121,6 +122,8 @@ module.exports = {
|
||||
|
||||
inputV: getPath('vfile.v'),
|
||||
|
||||
inputJpgClahe: getPath('concert.jpg'), // public domain - https://www.flickr.com/photos/mars_/14389236779/
|
||||
|
||||
testPattern: getPath('test-pattern.png'),
|
||||
|
||||
// Path for tests requiring human inspection
|
||||
|
||||
@@ -444,6 +444,22 @@
|
||||
...
|
||||
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
|
||||
Memcheck:Leak
|
||||
@@ -480,6 +496,14 @@
|
||||
...
|
||||
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
|
||||
{
|
||||
@@ -871,3 +895,12 @@
|
||||
...
|
||||
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
|
||||
}
|
||||
|
||||
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 () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.toColourspace(null);
|
||||
|
||||
@@ -57,8 +57,8 @@ describe('Image channel extraction', function () {
|
||||
it('With colorspace conversion', function (done) {
|
||||
const output = fixtures.path('output.extract-lch.jpg');
|
||||
sharp(fixtures.inputJpg)
|
||||
.toColourspace('lch')
|
||||
.extractChannel(1)
|
||||
.toColourspace('lch')
|
||||
.resize(320, 240, { fastShrinkOnLoad: false })
|
||||
.toFile(output, function (err, info) {
|
||||
if (err) throw err;
|
||||
@@ -70,12 +70,13 @@ describe('Image channel extraction', function () {
|
||||
});
|
||||
|
||||
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)
|
||||
.resize(16)
|
||||
.extractChannel(3)
|
||||
.toFile(output, function (err, info) {
|
||||
.toFile(output, function (err) {
|
||||
if (err) throw err;
|
||||
fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-16bit.jpg'));
|
||||
fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-16bit.png'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -247,7 +247,7 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual('undefined', typeof metadata.size);
|
||||
assert.strictEqual(800, metadata.width);
|
||||
assert.strictEqual(533, metadata.height);
|
||||
assert.strictEqual(true, [3, 4].includes(metadata.channels)); // libvips 8.11.0 = 4
|
||||
assert.strictEqual(3, metadata.channels);
|
||||
assert.strictEqual('uchar', metadata.depth);
|
||||
assert.strictEqual('undefined', typeof metadata.density);
|
||||
assert.strictEqual('undefined', typeof metadata.chromaSubsampling);
|
||||
@@ -256,6 +256,7 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual('undefined', typeof metadata.orientation);
|
||||
assert.strictEqual('undefined', typeof metadata.exif);
|
||||
assert.strictEqual('undefined', typeof metadata.icc);
|
||||
assert.deepStrictEqual(metadata.background, { r: 138, g: 148, b: 102 });
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -266,7 +267,7 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual('undefined', typeof metadata.size);
|
||||
assert.strictEqual(2, metadata.width);
|
||||
assert.strictEqual(1, metadata.height);
|
||||
assert.strictEqual(true, [2, 4].includes(metadata.channels)); // libvips 8.11.0 = 4
|
||||
assert.strictEqual(4, metadata.channels);
|
||||
assert.strictEqual('uchar', metadata.depth);
|
||||
assert.strictEqual('undefined', typeof metadata.density);
|
||||
assert.strictEqual('undefined', typeof metadata.chromaSubsampling);
|
||||
@@ -285,7 +286,7 @@ describe('Image metadata', function () {
|
||||
.then(({
|
||||
format, width, height, space, channels, depth,
|
||||
isProgressive, pages, pageHeight, loop, delay,
|
||||
hasProfile, hasAlpha
|
||||
background, hasProfile, hasAlpha
|
||||
}) => {
|
||||
assert.strictEqual(format, 'gif');
|
||||
assert.strictEqual(width, 80);
|
||||
@@ -298,6 +299,7 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual(pageHeight, 80);
|
||||
assert.strictEqual(loop, 0);
|
||||
assert.deepStrictEqual(delay, Array(30).fill(30));
|
||||
assert.deepStrictEqual(background, { r: 0, g: 0, b: 0 });
|
||||
assert.strictEqual(hasProfile, false);
|
||||
assert.strictEqual(hasAlpha, true);
|
||||
})
|
||||
@@ -320,7 +322,7 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual(isProgressive, false);
|
||||
assert.strictEqual(pages, 10);
|
||||
assert.strictEqual(pageHeight, 285);
|
||||
assert.strictEqual(true, [2, 3].includes(loop)); // libvips 8.11.0 = 2
|
||||
assert.strictEqual(loop, 2);
|
||||
assert.deepStrictEqual(delay, [...Array(9).fill(3000), 15000]);
|
||||
assert.strictEqual(hasProfile, false);
|
||||
assert.strictEqual(hasAlpha, true);
|
||||
@@ -724,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) {
|
||||
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||
.metadata(function (err) {
|
||||
|
||||
@@ -107,4 +107,88 @@ describe('Negate', function () {
|
||||
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' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -179,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) {
|
||||
sharp(fixtures.inputJpg)
|
||||
.greyscale()
|
||||
@@ -227,7 +227,7 @@ describe('Raw pixel data', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('extract A from RGBA', () =>
|
||||
it('Extract A from RGBA', () =>
|
||||
sharp(fixtures.inputPngWithTransparency)
|
||||
.resize(32, 24)
|
||||
.extractChannel(3)
|
||||
@@ -241,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);
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -423,20 +423,20 @@ describe('Image Stats', function () {
|
||||
assert.strictEqual(true, isInRange(stats.channels[0].maxY, 0, 1));
|
||||
|
||||
// alpha channel
|
||||
assert.strictEqual(0, stats.channels[1].min);
|
||||
assert.strictEqual(255, stats.channels[1].max);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 255));
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 65025));
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 127.5));
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 180.31222920256963));
|
||||
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
||||
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 2));
|
||||
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
||||
assert.strictEqual(true, isInRange(stats.channels[1].minY, 0, 1));
|
||||
assert.strictEqual(true, isInteger(stats.channels[1].maxX));
|
||||
assert.strictEqual(true, isInRange(stats.channels[1].maxX, 0, 2));
|
||||
assert.strictEqual(true, isInteger(stats.channels[1].maxY));
|
||||
assert.strictEqual(true, isInRange(stats.channels[1].maxY, 0, 1));
|
||||
assert.strictEqual(0, stats.channels[3].min);
|
||||
assert.strictEqual(255, stats.channels[3].max);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].sum, 255));
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].squaresSum, 65025));
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].mean, 127.5));
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[3].stdev, 180.31222920256963));
|
||||
assert.strictEqual(true, isInteger(stats.channels[3].minX));
|
||||
assert.strictEqual(true, isInRange(stats.channels[3].minX, 0, 2));
|
||||
assert.strictEqual(true, isInteger(stats.channels[3].minY));
|
||||
assert.strictEqual(true, isInRange(stats.channels[3].minY, 0, 1));
|
||||
assert.strictEqual(true, isInteger(stats.channels[3].maxX));
|
||||
assert.strictEqual(true, isInRange(stats.channels[3].maxX, 0, 2));
|
||||
assert.strictEqual(true, isInteger(stats.channels[3].maxY));
|
||||
assert.strictEqual(true, isInRange(stats.channels[3].maxY, 0, 1));
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('WebP', function () {
|
||||
|
||||
it('should work for webp alpha quality', function (done) {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.webp({ alphaQuality: 80 })
|
||||
.webp({ alphaQuality: 80, reductionEffort: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
@@ -46,7 +46,7 @@ describe('WebP', function () {
|
||||
|
||||
it('should work for webp lossless', function (done) {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.webp({ lossless: true })
|
||||
.webp({ lossless: true, reductionEffort: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
@@ -57,7 +57,7 @@ describe('WebP', function () {
|
||||
|
||||
it('should work for webp near-lossless', function (done) {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.webp({ nearLossless: true, quality: 50 })
|
||||
.webp({ nearLossless: true, quality: 50, reductionEffort: 0 })
|
||||
.toBuffer(function (err50, data50, info50) {
|
||||
if (err50) throw err50;
|
||||
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) {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.webp({ nearLossless: true, quality: 50, lossless: true })
|
||||
.webp({ nearLossless: true, quality: 50, lossless: true, reductionEffort: 0 })
|
||||
.toBuffer(function (err50, data50, info50) {
|
||||
if (err50) throw err50;
|
||||
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) {
|
||||
fs.createReadStream(fixtures.inputWebPAnimated)
|
||||
.pipe(sharp({ animated: true }))
|
||||
.webp({ lossless: true })
|
||||
.webp({ lossless: true, reductionEffort: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
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) {
|
||||
fs.createReadStream(fixtures.inputWebPAnimated)
|
||||
.pipe(sharp({ pages: -1 }))
|
||||
.webp({ lossless: true })
|
||||
.webp({ lossless: true, reductionEffort: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
|
||||