mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
92f21451c1 | ||
|
2cd2f8430a | ||
|
8ee8d273ee | ||
|
4e3f3792ad | ||
|
df5454e7dc | ||
|
32cf6be1b8 | ||
|
96dfd400b2 | ||
|
1422798c1d | ||
|
e26d4e9d5b | ||
|
852c7f8663 | ||
|
e286e2bff9 | ||
|
76995deefa | ||
|
e688c53659 | ||
|
c4b1d80c35 | ||
|
f92540f134 | ||
|
8c53d499f7 | ||
|
9392b8702b | ||
|
8669fbc936 | ||
|
cab02463ec | ||
|
5374b036f3 | ||
|
327a6d2083 | ||
|
751f9992c4 | ||
|
01f6cbbaee | ||
|
99be893dd4 | ||
|
4d1f7e051d | ||
|
91f1b58f31 | ||
|
6d04b7c1fa | ||
|
d4b30b7392 | ||
|
7f03502003 | ||
|
63b0a11b5b | ||
|
c4d6aec48c | ||
|
e75ae970ed | ||
|
956d72ddc0 | ||
|
00e66efbee | ||
|
db3a4528eb | ||
|
d36fd5064d | ||
|
8e17c6f518 | ||
|
94481a967e | ||
|
32872ef840 | ||
|
7c7f960b60 | ||
|
0b5f131df8 | ||
|
e922ef7450 | ||
|
73bec629cf | ||
|
758d7e63cc | ||
|
eba3e9aeb2 | ||
|
701143afb3 | ||
|
38b6f44611 | ||
|
5b5dfbad77 | ||
|
a642767329 |
@ -1,105 +0,0 @@
|
|||||||
version: 2.1
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
build:
|
|
||||||
jobs:
|
|
||||||
- linux-arm64-glibc-node-18:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^v.*/
|
|
||||||
- linux-arm64-musl-node-18:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^v.*/
|
|
||||||
- linux-arm64-glibc-node-20:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^v.*/
|
|
||||||
- linux-arm64-musl-node-20:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^v.*/
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
linux-arm64-glibc-node-18:
|
|
||||||
resource_class: arm.medium
|
|
||||||
machine:
|
|
||||||
image: ubuntu-2204:current
|
|
||||||
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 fonts-noto-core"
|
|
||||||
sudo docker exec sharp sh -c "mkdir -p /etc/apt/keyrings"
|
|
||||||
sudo docker exec sharp sh -c "curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg"
|
|
||||||
sudo docker exec sharp sh -c "echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main' | tee /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"
|
|
||||||
- run: sudo docker exec sharp sh -c "npm test"
|
|
||||||
- run: |
|
|
||||||
sudo docker exec sharp sh -c "npm run package-from-local-build"
|
|
||||||
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@img/sharp-linux-arm64=file:./npm/linux-arm64\""
|
|
||||||
sudo docker exec sharp sh -c "npm run clean"
|
|
||||||
sudo docker exec sharp sh -c "npm install --ignore-scripts"
|
|
||||||
sudo docker exec sharp sh -c "npm test"
|
|
||||||
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"cd src && ln -s ../package.json && npx prebuild --upload=$prebuild_upload\" || true"
|
|
||||||
linux-arm64-glibc-node-20:
|
|
||||||
resource_class: arm.medium
|
|
||||||
machine:
|
|
||||||
image: ubuntu-2204:current
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: |
|
|
||||||
sudo docker run -dit --name 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 fonts-noto-core"
|
|
||||||
sudo docker exec sharp sh -c "mkdir -p /etc/apt/keyrings"
|
|
||||||
sudo docker exec sharp sh -c "curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg"
|
|
||||||
sudo docker exec sharp sh -c "echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' | tee /etc/apt/sources.list.d/nodesource.list"
|
|
||||||
sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
|
||||||
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
|
|
||||||
sudo docker cp . sharp:/mnt/sharp/.
|
|
||||||
- run: sudo docker exec sharp sh -c "npm install --build-from-source"
|
|
||||||
- run: sudo docker exec sharp sh -c "npm test"
|
|
||||||
- run: |
|
|
||||||
sudo docker exec sharp sh -c "npm run package-from-local-build"
|
|
||||||
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@img/sharp-linux-arm64=file:./npm/linux-arm64\""
|
|
||||||
sudo docker exec sharp sh -c "npm run clean"
|
|
||||||
sudo docker exec sharp sh -c "npm install --ignore-scripts"
|
|
||||||
sudo docker exec sharp sh -c "npm test"
|
|
||||||
linux-arm64-musl-node-18:
|
|
||||||
resource_class: arm.medium
|
|
||||||
machine:
|
|
||||||
image: ubuntu-2204:current
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: |
|
|
||||||
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:18-alpine3.17
|
|
||||||
sudo docker exec sharp sh -c "apk add build-base git python3 font-noto --update-cache"
|
|
||||||
- run: sudo docker exec sharp sh -c "npm install --build-from-source"
|
|
||||||
- run: sudo docker exec sharp sh -c "npm test"
|
|
||||||
- run: |
|
|
||||||
sudo docker exec sharp sh -c "npm run package-from-local-build"
|
|
||||||
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@img/sharp-linuxmusl-arm64=file:./npm/linuxmusl-arm64\""
|
|
||||||
sudo docker exec sharp sh -c "npm run clean"
|
|
||||||
sudo docker exec sharp sh -c "npm install --ignore-scripts"
|
|
||||||
sudo docker exec sharp sh -c "npm test"
|
|
||||||
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"cd src && ln -s ../package.json && npx prebuild --upload=$prebuild_upload\" || true"
|
|
||||||
linux-arm64-musl-node-20:
|
|
||||||
resource_class: arm.medium
|
|
||||||
machine:
|
|
||||||
image: ubuntu-2204:current
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: |
|
|
||||||
sudo docker run -dit --name sharp --workdir /mnt/sharp node:20-alpine3.18
|
|
||||||
sudo docker exec sharp sh -c "apk add build-base git python3 font-noto --update-cache"
|
|
||||||
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
|
|
||||||
sudo docker cp . sharp:/mnt/sharp/.
|
|
||||||
- run: sudo docker exec sharp sh -c "npm install --build-from-source"
|
|
||||||
- run: sudo docker exec sharp sh -c "npm test"
|
|
||||||
- run: |
|
|
||||||
sudo docker exec sharp sh -c "npm run package-from-local-build"
|
|
||||||
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@img/sharp-linuxmusl-arm64=file:./npm/linuxmusl-arm64\""
|
|
||||||
sudo docker exec sharp sh -c "npm run clean"
|
|
||||||
sudo docker exec sharp sh -c "npm install --ignore-scripts"
|
|
||||||
sudo docker exec sharp sh -c "npm test"
|
|
@ -1,5 +1,5 @@
|
|||||||
freebsd_instance:
|
freebsd_instance:
|
||||||
image_family: freebsd-14-0-snap
|
image_family: freebsd-15-0-snap
|
||||||
|
|
||||||
task:
|
task:
|
||||||
name: FreeBSD
|
name: FreeBSD
|
||||||
|
207
.github/workflows/ci.yml
vendored
207
.github/workflows/ci.yml
vendored
@ -4,10 +4,10 @@ on:
|
|||||||
- pull_request
|
- pull_request
|
||||||
permissions: {}
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
github-runner:
|
build-native:
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} ${{ matrix.prebuild && '- prebuild' }}
|
name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
container: ${{ matrix.container }}
|
container: ${{ matrix.container }}
|
||||||
strategy:
|
strategy:
|
||||||
@ -20,7 +20,7 @@ jobs:
|
|||||||
nodejs_version: "^18.17.0"
|
nodejs_version: "^18.17.0"
|
||||||
nodejs_version_major: 18
|
nodejs_version_major: 18
|
||||||
platform: linux-x64
|
platform: linux-x64
|
||||||
prebuild: true
|
package: true
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
container: rockylinux:8
|
container: rockylinux:8
|
||||||
nodejs_arch: x64
|
nodejs_arch: x64
|
||||||
@ -37,7 +37,7 @@ jobs:
|
|||||||
container: node:18-alpine3.17
|
container: node:18-alpine3.17
|
||||||
nodejs_version_major: 18
|
nodejs_version_major: 18
|
||||||
platform: linuxmusl-x64
|
platform: linuxmusl-x64
|
||||||
prebuild: true
|
package: true
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
container: node:20-alpine3.18
|
container: node:20-alpine3.18
|
||||||
nodejs_version_major: 20
|
nodejs_version_major: 20
|
||||||
@ -46,12 +46,25 @@ jobs:
|
|||||||
container: node:22-alpine3.20
|
container: node:22-alpine3.20
|
||||||
nodejs_version_major: 22
|
nodejs_version_major: 22
|
||||||
platform: linuxmusl-x64
|
platform: linuxmusl-x64
|
||||||
|
- os: ubuntu-24.04-arm
|
||||||
|
container: arm64v8/rockylinux:8
|
||||||
|
nodejs_arch: arm64
|
||||||
|
nodejs_version: "^18.17.0"
|
||||||
|
nodejs_version_major: 18
|
||||||
|
platform: linux-arm64
|
||||||
|
package: true
|
||||||
|
- os: ubuntu-24.04-arm
|
||||||
|
container: arm64v8/rockylinux:8
|
||||||
|
nodejs_arch: arm64
|
||||||
|
nodejs_version: "^20.3.0"
|
||||||
|
nodejs_version_major: 20
|
||||||
|
platform: linux-arm64
|
||||||
- os: macos-13
|
- os: macos-13
|
||||||
nodejs_arch: x64
|
nodejs_arch: x64
|
||||||
nodejs_version: "^18.17.0"
|
nodejs_version: "^18.17.0"
|
||||||
nodejs_version_major: 18
|
nodejs_version_major: 18
|
||||||
platform: darwin-x64
|
platform: darwin-x64
|
||||||
prebuild: true
|
package: true
|
||||||
- os: macos-13
|
- os: macos-13
|
||||||
nodejs_arch: x64
|
nodejs_arch: x64
|
||||||
nodejs_version: "^20.3.0"
|
nodejs_version: "^20.3.0"
|
||||||
@ -67,7 +80,7 @@ jobs:
|
|||||||
nodejs_version: "^18.17.0"
|
nodejs_version: "^18.17.0"
|
||||||
nodejs_version_major: 18
|
nodejs_version_major: 18
|
||||||
platform: darwin-arm64
|
platform: darwin-arm64
|
||||||
prebuild: true
|
package: true
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
nodejs_arch: arm64
|
nodejs_arch: arm64
|
||||||
nodejs_version: "^20.3.0"
|
nodejs_version: "^20.3.0"
|
||||||
@ -78,44 +91,55 @@ jobs:
|
|||||||
nodejs_version: "^22.9.0"
|
nodejs_version: "^22.9.0"
|
||||||
nodejs_version_major: 22
|
nodejs_version_major: 22
|
||||||
platform: darwin-arm64
|
platform: darwin-arm64
|
||||||
- os: windows-2019
|
- os: windows-2022
|
||||||
nodejs_arch: x86
|
nodejs_arch: x86
|
||||||
nodejs_version: "18.18.2" # pinned to avoid 18.19.0 and npm 10
|
nodejs_version: "18.18.2" # pinned to avoid 18.19.0 and npm 10
|
||||||
nodejs_version_major: 18
|
nodejs_version_major: 18
|
||||||
platform: win32-ia32
|
platform: win32-ia32
|
||||||
prebuild: true
|
package: true
|
||||||
- os: windows-2019
|
- os: windows-2022
|
||||||
nodejs_arch: x86
|
nodejs_arch: x86
|
||||||
nodejs_version: "^20.3.0"
|
nodejs_version: "^20.3.0"
|
||||||
nodejs_version_major: 20
|
nodejs_version_major: 20
|
||||||
platform: win32-ia32
|
platform: win32-ia32
|
||||||
- os: windows-2019
|
- os: windows-2022
|
||||||
nodejs_arch: x86
|
nodejs_arch: x86
|
||||||
nodejs_version: "^22.9.0"
|
nodejs_version: "^22.9.0"
|
||||||
nodejs_version_major: 22
|
nodejs_version_major: 22
|
||||||
platform: win32-ia32
|
platform: win32-ia32
|
||||||
- os: windows-2019
|
- os: windows-2022
|
||||||
nodejs_arch: x64
|
nodejs_arch: x64
|
||||||
nodejs_version: "^18.17.0"
|
nodejs_version: "^18.17.0"
|
||||||
nodejs_version_major: 18
|
nodejs_version_major: 18
|
||||||
platform: win32-x64
|
platform: win32-x64
|
||||||
prebuild: true
|
package: true
|
||||||
- os: windows-2019
|
- os: windows-2022
|
||||||
nodejs_arch: x64
|
nodejs_arch: x64
|
||||||
nodejs_version: "^20.3.0"
|
nodejs_version: "^20.3.0"
|
||||||
nodejs_version_major: 20
|
nodejs_version_major: 20
|
||||||
platform: win32-x64
|
platform: win32-x64
|
||||||
- os: windows-2019
|
- os: windows-2022
|
||||||
nodejs_arch: x64
|
nodejs_arch: x64
|
||||||
nodejs_version: "^22.9.0"
|
nodejs_version: "^22.9.0"
|
||||||
nodejs_version_major: 22
|
nodejs_version_major: 22
|
||||||
platform: win32-x64
|
platform: win32-x64
|
||||||
|
- os: windows-11-arm
|
||||||
|
nodejs_arch: arm64
|
||||||
|
nodejs_version: "^20.3.0"
|
||||||
|
nodejs_version_major: 20
|
||||||
|
platform: win32-arm64
|
||||||
|
package: true
|
||||||
|
- os: windows-11-arm
|
||||||
|
nodejs_arch: arm64
|
||||||
|
nodejs_version: "^22.9.0"
|
||||||
|
nodejs_version_major: 22
|
||||||
|
platform: win32-arm64
|
||||||
steps:
|
steps:
|
||||||
- name: Dependencies (Rocky Linux glibc)
|
- name: Dependencies (Rocky Linux glibc)
|
||||||
if: contains(matrix.container, 'rockylinux')
|
if: contains(matrix.container, 'rockylinux')
|
||||||
run: |
|
run: |
|
||||||
dnf install -y gcc-toolset-11-gcc-c++ make git python3.12 fontconfig google-noto-sans-fonts
|
dnf install -y gcc-toolset-14-gcc-c++ make git python3.12 fontconfig google-noto-sans-fonts
|
||||||
echo "/opt/rh/gcc-toolset-11/root/usr/bin" >> $GITHUB_PATH
|
echo "/opt/rh/gcc-toolset-14/root/usr/bin" >> $GITHUB_PATH
|
||||||
- name: Dependencies (Linux musl)
|
- name: Dependencies (Linux musl)
|
||||||
if: contains(matrix.container, 'alpine')
|
if: contains(matrix.container, 'alpine')
|
||||||
run: apk add build-base git python3 font-noto --update-cache
|
run: apk add build-base git python3 font-noto --update-cache
|
||||||
@ -130,31 +154,70 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.nodejs_version }}
|
node-version: ${{ matrix.nodejs_version }}
|
||||||
architecture: ${{ matrix.nodejs_arch }}
|
architecture: ${{ matrix.nodejs_arch }}
|
||||||
- name: Checkout
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: npm install --build-from-source
|
run: npm install --build-from-source
|
||||||
- name: Test
|
- name: Test
|
||||||
run: npm test
|
run: npm test
|
||||||
- name: Test packaging
|
- name: Populate npm package
|
||||||
run: |
|
if: matrix.package
|
||||||
npm run package-from-local-build
|
run: npm run package-from-local-build
|
||||||
npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}"
|
- uses: actions/upload-artifact@v4
|
||||||
npm run clean
|
if: matrix.package
|
||||||
npm install --ignore-scripts
|
with:
|
||||||
npm test
|
name: ${{ matrix.platform }}
|
||||||
- name: Prebuild
|
path: npm/${{ matrix.platform }}
|
||||||
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
|
retention-days: 1
|
||||||
env:
|
if-no-files-found: error
|
||||||
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
|
build-linuxmusl-arm-64:
|
||||||
run: |
|
|
||||||
node -e "require('fs').cpSync('package.json', 'src/package.json')"
|
|
||||||
cd src
|
|
||||||
npx prebuild
|
|
||||||
github-runner-qemu:
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} - prebuild
|
name: "build-linuxmusl-arm64 [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
container:
|
||||||
|
image: ${{ matrix.container }}
|
||||||
|
volumes:
|
||||||
|
- /:/host
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- container: node:18-alpine3.17
|
||||||
|
nodejs_version_major: 18
|
||||||
|
package: true
|
||||||
|
- container: node:20-alpine3.18
|
||||||
|
nodejs_version_major: 20
|
||||||
|
steps:
|
||||||
|
- name: Allow Linux musl containers on ARM64 runners # https://github.com/actions/runner/issues/801#issuecomment-2394425757
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
apk add nodejs
|
||||||
|
sed -i "s:ID=alpine:ID=NotpineForGHA:" /etc/os-release
|
||||||
|
cd /host/home/runner/runners/*/externals/
|
||||||
|
rm -rf node20/*
|
||||||
|
mkdir node20/bin
|
||||||
|
ln -s /usr/bin/node node20/bin/node
|
||||||
|
- name: Dependencies
|
||||||
|
run: apk add build-base git python3 font-noto --update-cache
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install
|
||||||
|
run: npm install --build-from-source
|
||||||
|
- name: Test
|
||||||
|
run: npm test
|
||||||
|
- name: Populate npm package
|
||||||
|
if: matrix.package
|
||||||
|
run: npm run package-from-local-build
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: matrix.package
|
||||||
|
with:
|
||||||
|
name: linuxmusl-arm64
|
||||||
|
path: npm/linuxmusl-arm64
|
||||||
|
retention-days: 1
|
||||||
|
if-no-files-found: error
|
||||||
|
build-qemu:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] [package]"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@ -187,8 +250,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
arch: ${{ matrix.run_on_arch }}
|
arch: ${{ matrix.run_on_arch }}
|
||||||
distro: ${{ matrix.distro }}
|
distro: ${{ matrix.distro }}
|
||||||
env: |
|
|
||||||
prebuild_upload: "${{ startsWith(github.ref, 'refs/tags/') && secrets.GITHUB_TOKEN || '' }}"
|
|
||||||
run: |
|
run: |
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y curl g++ git libatomic1 make python3 xz-utils
|
apt-get install -y curl g++ git libatomic1 make python3 xz-utils
|
||||||
@ -198,20 +259,20 @@ jobs:
|
|||||||
npm install --build-from-source
|
npm install --build-from-source
|
||||||
npx mocha --no-config --spec=test/unit/io.js --timeout=30000
|
npx mocha --no-config --spec=test/unit/io.js --timeout=30000
|
||||||
npm run package-from-local-build
|
npm run package-from-local-build
|
||||||
npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}"
|
- uses: actions/upload-artifact@v4
|
||||||
npm run clean
|
with:
|
||||||
npm install --ignore-scripts
|
name: ${{ matrix.platform }}
|
||||||
npx mocha --no-config --spec=test/unit/io.js --timeout=30000
|
path: npm/${{ matrix.platform }}
|
||||||
[[ -n $prebuild_upload ]] && cd src && ln -s ../package.json && npx prebuild || true
|
retention-days: 1
|
||||||
github-runner-emscripten:
|
if-no-files-found: error
|
||||||
|
build-emscripten:
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
name: wasm32 - prebuild
|
name: "build-wasm32 [package]"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
container: "emscripten/emsdk:4.0.6"
|
container: "emscripten/emsdk:4.0.10"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Dependencies
|
- name: Dependencies
|
||||||
run: apt-get update && apt-get install -y pkg-config
|
run: apt-get update && apt-get install -y pkg-config
|
||||||
- name: Dependencies (Node.js)
|
- name: Dependencies (Node.js)
|
||||||
@ -229,17 +290,35 @@ jobs:
|
|||||||
test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP"
|
test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP"
|
||||||
- name: Test
|
- name: Test
|
||||||
run: emmake npm test
|
run: emmake npm test
|
||||||
- name: Test packaging
|
- name: Populate npm package
|
||||||
run: |
|
run: emmake npm run package-from-local-build
|
||||||
emmake npm run package-from-local-build
|
- uses: actions/upload-artifact@v4
|
||||||
npm pkg set "optionalDependencies.@img/sharp-wasm32=file:./npm/wasm32"
|
with:
|
||||||
npm run clean
|
name: wasm32
|
||||||
rm -rf node_modules/@img/sharp-linux-x64
|
path: npm/wasm32
|
||||||
npm install --cpu=wasm32
|
retention-days: 1
|
||||||
npm test
|
if-no-files-found: error
|
||||||
- name: Prebuild
|
release:
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
permissions:
|
||||||
env:
|
contents: write
|
||||||
npm_config_nodedir: emscripten
|
runs-on: ubuntu-24.04
|
||||||
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
|
needs:
|
||||||
run: cd src && ln -s ../package.json && emmake npx prebuild --platform=emscripten --arch=wasm32 --strip=0
|
- build-native
|
||||||
|
- build-linuxmusl-arm-64
|
||||||
|
- build-qemu
|
||||||
|
- build-emscripten
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: npm
|
||||||
|
- name: Create npm workspace tarball
|
||||||
|
run: tar -vcaf npm-workspace.tar.xz --directory npm --exclude=from-local-build.js .
|
||||||
|
- name: Create GitHub release for tag
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
artifacts: npm-workspace.tar.xz
|
||||||
|
artifactContentType: application/x-xz
|
||||||
|
prerelease: ${{ contains(github.ref, '-rc') }}
|
||||||
|
makeLatest: ${{ !contains(github.ref, '-rc') }}
|
||||||
|
12
.github/workflows/npm.yml
vendored
12
.github/workflows/npm.yml
vendored
@ -70,27 +70,27 @@ jobs:
|
|||||||
runtime: bun
|
runtime: bun
|
||||||
|
|
||||||
- name: win32-x64-node-npm
|
- name: win32-x64-node-npm
|
||||||
runs-on: windows-2019
|
runs-on: windows-2022
|
||||||
runtime: node
|
runtime: node
|
||||||
package-manager: npm
|
package-manager: npm
|
||||||
- name: win32-x64-node-pnpm
|
- name: win32-x64-node-pnpm
|
||||||
runs-on: windows-2019
|
runs-on: windows-2022
|
||||||
runtime: node
|
runtime: node
|
||||||
package-manager: pnpm
|
package-manager: pnpm
|
||||||
- name: win32-x64-node-yarn
|
- name: win32-x64-node-yarn
|
||||||
runs-on: windows-2019
|
runs-on: windows-2022
|
||||||
runtime: node
|
runtime: node
|
||||||
package-manager: yarn
|
package-manager: yarn
|
||||||
- name: win32-x64-node-yarn-pnp
|
- name: win32-x64-node-yarn-pnp
|
||||||
runs-on: windows-2019
|
runs-on: windows-2022
|
||||||
runtime: node
|
runtime: node
|
||||||
package-manager: yarn-pnp
|
package-manager: yarn-pnp
|
||||||
- name: win32-x64-node-yarn-v1
|
- name: win32-x64-node-yarn-v1
|
||||||
runs-on: windows-2019
|
runs-on: windows-2022
|
||||||
runtime: node
|
runtime: node
|
||||||
package-manager: yarn-v1
|
package-manager: yarn-v1
|
||||||
- name: win32-x64-deno
|
- name: win32-x64-deno
|
||||||
runs-on: windows-2019
|
runs-on: windows-2022
|
||||||
runtime: deno
|
runtime: deno
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"runtime": "napi",
|
|
||||||
"include-regex": "(sharp-.+\\.node|libvips-.+\\.dll)",
|
|
||||||
"prerelease": true,
|
|
||||||
"strip": true
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
# sharp
|
# sharp
|
||||||
|
|
||||||
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/public/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
|
<img src="https://sharp.pixelplumbing.com/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
|
||||||
|
|
||||||
The typical use case for this high speed Node-API module
|
The typical use case for this high speed Node-API module
|
||||||
is to convert large images in common formats to
|
is to convert large images in common formats to
|
||||||
|
@ -18,7 +18,7 @@ export default defineConfig({
|
|||||||
tag: 'meta',
|
tag: 'meta',
|
||||||
attrs: {
|
attrs: {
|
||||||
'http-equiv': 'Content-Security-Policy',
|
'http-equiv': 'Content-Security-Policy',
|
||||||
content: "default-src 'self'; connect-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://cdn.jsdelivr.net/gh/lovell/; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com/beacon.min.js/;"
|
content: "default-src 'self'; connect-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com/beacon.min.js/;"
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tag: 'link',
|
tag: 'link',
|
||||||
@ -70,10 +70,10 @@ export default defineConfig({
|
|||||||
{ label: 'Performance', slug: 'performance' },
|
{ label: 'Performance', slug: 'performance' },
|
||||||
{ label: 'Changelog', slug: 'changelog' }
|
{ label: 'Changelog', slug: 'changelog' }
|
||||||
],
|
],
|
||||||
social: {
|
social: [
|
||||||
openCollective: 'https://opencollective.com/libvips',
|
{ icon: 'openCollective', label: 'Open Collective', href: 'https://opencollective.com/libvips' },
|
||||||
github: 'https://github.com/lovell/sharp'
|
{ icon: 'github', label: 'GitHub', href: 'https://github.com/lovell/sharp' }
|
||||||
}
|
]
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/starlight": "^0.32.3",
|
"@astrojs/starlight": "^0.34.3",
|
||||||
"astro": "^5.5.3"
|
"astro": "^5.7.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,3 +317,9 @@ GitHub: https://github.com/florentzabera
|
|||||||
|
|
||||||
Name: Quentin Pinçon
|
Name: Quentin Pinçon
|
||||||
GitHub: https://github.com/qpincon
|
GitHub: https://github.com/qpincon
|
||||||
|
|
||||||
|
Name: Hans Chen
|
||||||
|
GitHub: https://github.com/hans00
|
||||||
|
|
||||||
|
Name: Thibaut Patel
|
||||||
|
GitHub: https://github.com/tpatel
|
||||||
|
@ -44,24 +44,23 @@ where the overall height is the `pageHeight` multiplied by the number of `pages`
|
|||||||
| [options.ignoreIcc] | <code>number</code> | <code>false</code> | should the embedded ICC profile, if any, be ignored. |
|
| [options.ignoreIcc] | <code>number</code> | <code>false</code> | should the embedded ICC profile, if any, be ignored. |
|
||||||
| [options.pages] | <code>number</code> | <code>1</code> | Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages. |
|
| [options.pages] | <code>number</code> | <code>1</code> | Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages. |
|
||||||
| [options.page] | <code>number</code> | <code>0</code> | Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based. |
|
| [options.page] | <code>number</code> | <code>0</code> | Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based. |
|
||||||
| [options.subifd] | <code>number</code> | <code>-1</code> | subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. |
|
|
||||||
| [options.level] | <code>number</code> | <code>0</code> | level to extract from a multi-level input (OpenSlide), zero based. |
|
|
||||||
| [options.pdfBackground] | <code>string</code> \| <code>Object</code> | | Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. |
|
|
||||||
| [options.animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. |
|
| [options.animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. |
|
||||||
| [options.raw] | <code>Object</code> | | describes raw pixel input image data. See `raw()` for pixel ordering. |
|
| [options.raw] | <code>Object</code> | | describes raw pixel input image data. See `raw()` for pixel ordering. |
|
||||||
| [options.raw.width] | <code>number</code> | | integral number of pixels wide. |
|
| [options.raw.width] | <code>number</code> | | integral number of pixels wide. |
|
||||||
| [options.raw.height] | <code>number</code> | | integral number of pixels high. |
|
| [options.raw.height] | <code>number</code> | | integral number of pixels high. |
|
||||||
| [options.raw.channels] | <code>number</code> | | integral number of channels, between 1 and 4. |
|
| [options.raw.channels] | <code>number</code> | | integral number of channels, between 1 and 4. |
|
||||||
| [options.raw.premultiplied] | <code>boolean</code> | | specifies that the raw input has already been premultiplied, set to `true` to avoid sharp premultiplying the image. (optional, default `false`) |
|
| [options.raw.premultiplied] | <code>boolean</code> | | specifies that the raw input has already been premultiplied, set to `true` to avoid sharp premultiplying the image. (optional, default `false`) |
|
||||||
|
| [options.raw.pageHeight] | <code>number</code> | | The pixel height of each page/frame for animated images, must be an integral factor of `raw.height`. |
|
||||||
| [options.create] | <code>Object</code> | | describes a new image to be created. |
|
| [options.create] | <code>Object</code> | | describes a new image to be created. |
|
||||||
| [options.create.width] | <code>number</code> | | integral number of pixels wide. |
|
| [options.create.width] | <code>number</code> | | integral number of pixels wide. |
|
||||||
| [options.create.height] | <code>number</code> | | integral number of pixels high. |
|
| [options.create.height] | <code>number</code> | | integral number of pixels high. |
|
||||||
| [options.create.channels] | <code>number</code> | | integral number of channels, either 3 (RGB) or 4 (RGBA). |
|
| [options.create.channels] | <code>number</code> | | integral number of channels, either 3 (RGB) or 4 (RGBA). |
|
||||||
| [options.create.background] | <code>string</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
| [options.create.background] | <code>string</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
||||||
|
| [options.create.pageHeight] | <code>number</code> | | The pixel height of each page/frame for animated images, must be an integral factor of `create.height`. |
|
||||||
| [options.create.noise] | <code>Object</code> | | describes a noise to be created. |
|
| [options.create.noise] | <code>Object</code> | | describes a noise to be created. |
|
||||||
| [options.create.noise.type] | <code>string</code> | | type of generated noise, currently only `gaussian` is supported. |
|
| [options.create.noise.type] | <code>string</code> | | type of generated noise, currently only `gaussian` is supported. |
|
||||||
| [options.create.noise.mean] | <code>number</code> | | mean of pixels in generated noise. |
|
| [options.create.noise.mean] | <code>number</code> | <code>128</code> | Mean value of pixels in the generated noise. |
|
||||||
| [options.create.noise.sigma] | <code>number</code> | | standard deviation of pixels in generated noise. |
|
| [options.create.noise.sigma] | <code>number</code> | <code>30</code> | Standard deviation of pixel values in the generated noise. |
|
||||||
| [options.text] | <code>Object</code> | | describes a new text image to be created. |
|
| [options.text] | <code>Object</code> | | describes a new text image to be created. |
|
||||||
| [options.text.text] | <code>string</code> | | text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`. |
|
| [options.text.text] | <code>string</code> | | text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`. |
|
||||||
| [options.text.font] | <code>string</code> | | font name to render with. |
|
| [options.text.font] | <code>string</code> | | font name to render with. |
|
||||||
@ -81,6 +80,17 @@ where the overall height is the `pageHeight` multiplied by the number of `pages`
|
|||||||
| [options.join.background] | <code>string</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
| [options.join.background] | <code>string</code> \| <code>Object</code> | | parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
||||||
| [options.join.halign] | <code>string</code> | <code>"'left'"</code> | horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`). |
|
| [options.join.halign] | <code>string</code> | <code>"'left'"</code> | horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`). |
|
||||||
| [options.join.valign] | <code>string</code> | <code>"'top'"</code> | vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`). |
|
| [options.join.valign] | <code>string</code> | <code>"'top'"</code> | vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`). |
|
||||||
|
| [options.tiff] | <code>Object</code> | | Describes TIFF specific options. |
|
||||||
|
| [options.tiff.subifd] | <code>number</code> | <code>-1</code> | Sub Image File Directory to extract for OME-TIFF, defaults to main image. |
|
||||||
|
| [options.svg] | <code>Object</code> | | Describes SVG specific options. |
|
||||||
|
| [options.svg.stylesheet] | <code>string</code> | | Custom CSS for SVG input, applied with a User Origin during the CSS cascade. |
|
||||||
|
| [options.svg.highBitdepth] | <code>boolean</code> | <code>false</code> | Set to `true` to render SVG input at 32-bits per channel (128-bit) instead of 8-bits per channel (32-bit) RGBA. |
|
||||||
|
| [options.pdf] | <code>Object</code> | | Describes PDF specific options. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. |
|
||||||
|
| [options.pdf.background] | <code>string</code> \| <code>Object</code> | | Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. |
|
||||||
|
| [options.openSlide] | <code>Object</code> | | Describes OpenSlide specific options. Requires the use of a globally-installed libvips compiled with support for OpenSlide. |
|
||||||
|
| [options.openSlide.level] | <code>number</code> | <code>0</code> | Level to extract from a multi-level input, zero based. |
|
||||||
|
| [options.jp2] | <code>Object</code> | | Describes JPEG 2000 specific options. Requires the use of a globally-installed libvips compiled with support for OpenJPEG. |
|
||||||
|
| [options.jp2.oneshot] | <code>boolean</code> | <code>false</code> | Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility. |
|
||||||
|
|
||||||
**Example**
|
**Example**
|
||||||
```js
|
```js
|
||||||
|
@ -242,6 +242,57 @@ const outputWithP3 = await sharp(input)
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## keepXmp
|
||||||
|
> keepXmp() ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
|
Keep XMP metadata from the input image in the output image.
|
||||||
|
|
||||||
|
|
||||||
|
**Since**: 0.34.3
|
||||||
|
**Example**
|
||||||
|
```js
|
||||||
|
const outputWithXmp = await sharp(inputWithXmp)
|
||||||
|
.keepXmp()
|
||||||
|
.toBuffer();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## withXmp
|
||||||
|
> withXmp(xmp) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
|
Set XMP metadata in the output image.
|
||||||
|
|
||||||
|
Supported by PNG, JPEG, WebP, and TIFF output.
|
||||||
|
|
||||||
|
|
||||||
|
**Throws**:
|
||||||
|
|
||||||
|
- <code>Error</code> Invalid parameters
|
||||||
|
|
||||||
|
**Since**: 0.34.3
|
||||||
|
|
||||||
|
| Param | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| xmp | <code>string</code> | String containing XMP metadata to be embedded in the output image. |
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
```js
|
||||||
|
const xmpString = `
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<x:xmpmeta xmlns:x="adobe:ns:meta/">
|
||||||
|
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||||
|
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<dc:creator><rdf:Seq><rdf:li>John Doe</rdf:li></rdf:Seq></dc:creator>
|
||||||
|
</rdf:Description>
|
||||||
|
</rdf:RDF>
|
||||||
|
</x:xmpmeta>`;
|
||||||
|
|
||||||
|
const data = await sharp(input)
|
||||||
|
.withXmp(xmpString)
|
||||||
|
.toBuffer();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## keepMetadata
|
## keepMetadata
|
||||||
> keepMetadata() ⇒ <code>Sharp</code>
|
> keepMetadata() ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
@ -496,6 +547,7 @@ The palette of the input image will be re-used if possible.
|
|||||||
| [options.dither] | <code>number</code> | <code>1.0</code> | level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) |
|
| [options.dither] | <code>number</code> | <code>1.0</code> | level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) |
|
||||||
| [options.interFrameMaxError] | <code>number</code> | <code>0</code> | maximum inter-frame error for transparency, between 0 (lossless) and 32 |
|
| [options.interFrameMaxError] | <code>number</code> | <code>0</code> | maximum inter-frame error for transparency, between 0 (lossless) and 32 |
|
||||||
| [options.interPaletteMaxError] | <code>number</code> | <code>3</code> | maximum inter-palette error for palette reuse, between 0 and 256 |
|
| [options.interPaletteMaxError] | <code>number</code> | <code>3</code> | maximum inter-palette error for palette reuse, between 0 and 256 |
|
||||||
|
| [options.keepDuplicateFrames] | <code>boolean</code> | <code>false</code> | keep duplicate frames in the output instead of combining them |
|
||||||
| [options.loop] | <code>number</code> | <code>0</code> | number of animation iterations, use 0 for infinite animation |
|
| [options.loop] | <code>number</code> | <code>0</code> | number of animation iterations, use 0 for infinite animation |
|
||||||
| [options.delay] | <code>number</code> \| <code>Array.<number></code> | | delay(s) between animation frames (in milliseconds) |
|
| [options.delay] | <code>number</code> \| <code>Array.<number></code> | | delay(s) between animation frames (in milliseconds) |
|
||||||
| [options.force] | <code>boolean</code> | <code>true</code> | force GIF output, otherwise attempt to use input format |
|
| [options.force] | <code>boolean</code> | <code>true</code> | force GIF output, otherwise attempt to use input format |
|
||||||
@ -626,6 +678,9 @@ Use these AVIF options for output image.
|
|||||||
AVIF image sequences are not supported.
|
AVIF image sequences are not supported.
|
||||||
Prebuilt binaries support a bitdepth of 8 only.
|
Prebuilt binaries support a bitdepth of 8 only.
|
||||||
|
|
||||||
|
This feature is experimental on the Windows ARM64 platform
|
||||||
|
and requires a CPU with ARM64v8.4 or later.
|
||||||
|
|
||||||
|
|
||||||
**Throws**:
|
**Throws**:
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ When both a `width` and `height` are provided, the possible methods by which the
|
|||||||
|
|
||||||
Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
|
Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
|
||||||
|
|
||||||
<img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.svg">
|
<img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="/api-resize-fit.svg">
|
||||||
|
|
||||||
When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
||||||
- `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
- `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
||||||
@ -38,6 +38,8 @@ Possible downsizing kernels are:
|
|||||||
- `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
|
- `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
|
||||||
- `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
|
- `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
|
||||||
- `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
- `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
||||||
|
- `mks2013`: Use a [Magic Kernel Sharp](https://johncostella.com/magic/mks.pdf) 2013 kernel, as adopted by Facebook.
|
||||||
|
- `mks2021`: Use a Magic Kernel Sharp 2021 kernel, with more accurate (reduced) sharpening than the 2013 version.
|
||||||
|
|
||||||
When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
|
When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
|
||||||
Downsampling kernels without a matching upsampling interpolator map to `cubic`.
|
Downsampling kernels without a matching upsampling interpolator map to `cubic`.
|
||||||
|
@ -113,15 +113,9 @@ Some image format libraries spawn additional threads,
|
|||||||
e.g. libaom manages its own 4 threads when encoding AVIF images,
|
e.g. libaom manages its own 4 threads when encoding AVIF images,
|
||||||
and these are independent of the value set here.
|
and these are independent of the value set here.
|
||||||
|
|
||||||
The maximum number of images that sharp can process in parallel
|
:::note
|
||||||
is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
|
Further [control over performance](/performance) is available.
|
||||||
which defaults to 4.
|
:::
|
||||||
|
|
||||||
https://nodejs.org/api/cli.html#uv_threadpool_sizesize
|
|
||||||
|
|
||||||
For example, by default, a machine with 8 CPU cores will process
|
|
||||||
4 images in parallel and use up to 8 threads per image,
|
|
||||||
so there will be up to 32 concurrent threads.
|
|
||||||
|
|
||||||
|
|
||||||
**Returns**: <code>number</code> - concurrency
|
**Returns**: <code>number</code> - concurrency
|
||||||
|
@ -4,7 +4,64 @@ title: Changelog
|
|||||||
|
|
||||||
## v0.34 - *hat*
|
## v0.34 - *hat*
|
||||||
|
|
||||||
Requires libvips v8.16.1
|
Requires libvips v8.17.1
|
||||||
|
|
||||||
|
### v0.34.3 - TBD
|
||||||
|
|
||||||
|
* Upgrade to libvips v8.17.1 for upstream bug fixes.
|
||||||
|
|
||||||
|
* Add "Magic Kernel Sharp" (no relation) to resizing kernels.
|
||||||
|
|
||||||
|
* Deprecate top-level, format-specific constructor parameters, e.g. `subifd` becomes `tiff.subifd`.
|
||||||
|
|
||||||
|
* Expose `stylesheet` and `highBitdepth` SVG input parameters.
|
||||||
|
|
||||||
|
* Expose `keepDuplicateFrames` GIF output parameter.
|
||||||
|
|
||||||
|
* Add support for RAW digital camera image input. Requires libvips compiled with libraw support.
|
||||||
|
|
||||||
|
* Provide XMP metadata as a string, as well as a Buffer, where possible.
|
||||||
|
|
||||||
|
* Add `pageHeight` option to `create` and `raw` input for animated images.
|
||||||
|
[#3236](https://github.com/lovell/sharp/issues/3236)
|
||||||
|
|
||||||
|
* Expose JPEG 2000 `oneshot` decoder option.
|
||||||
|
[#4262](https://github.com/lovell/sharp/pull/4262)
|
||||||
|
[@mbklein](https://github.com/mbklein)
|
||||||
|
|
||||||
|
* Support composite operation with non-sRGB pipeline colourspace.
|
||||||
|
[#4412](https://github.com/lovell/sharp/pull/4412)
|
||||||
|
[@kleisauke](https://github.com/kleisauke)
|
||||||
|
|
||||||
|
* Add `keepXmp` and `withXmp` for control over output XMP metadata.
|
||||||
|
[#4416](https://github.com/lovell/sharp/pull/4416)
|
||||||
|
[@tpatel](https://github.com/tpatel)
|
||||||
|
|
||||||
|
### v0.34.2 - 20th May 2025
|
||||||
|
|
||||||
|
* Ensure animated GIF to WebP conversion retains loop (regression in 0.34.0).
|
||||||
|
[#3394](https://github.com/lovell/sharp/issues/3394)
|
||||||
|
|
||||||
|
* Ensure `pdfBackground` constructor property is used.
|
||||||
|
[#4207](https://github.com/lovell/sharp/pull/4207)
|
||||||
|
[#4398](https://github.com/lovell/sharp/issues/4398)
|
||||||
|
|
||||||
|
* Add experimental support for prebuilt Windows ARM64 binaries.
|
||||||
|
[#4375](https://github.com/lovell/sharp/pull/4375)
|
||||||
|
[@hans00](https://github.com/hans00)
|
||||||
|
|
||||||
|
* Ensure resizing with a `fit` of `contain` supports multiple alpha channels.
|
||||||
|
[#4382](https://github.com/lovell/sharp/issues/4382)
|
||||||
|
|
||||||
|
* TypeScript: Ensure `metadata` response more closely matches reality.
|
||||||
|
[#4383](https://github.com/lovell/sharp/issues/4383)
|
||||||
|
|
||||||
|
* TypeScript: Ensure `smartDeblock` property is included in WebP definition.
|
||||||
|
[#4387](https://github.com/lovell/sharp/pull/4387)
|
||||||
|
[@Stephen-X](https://github.com/Stephen-X)
|
||||||
|
|
||||||
|
* Ensure support for wide-character filenames on Windows (regression in 0.34.0).
|
||||||
|
[#4391](https://github.com/lovell/sharp/issues/4391)
|
||||||
|
|
||||||
### v0.34.1 - 7th April 2025
|
### v0.34.1 - 7th April 2025
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: "High performance Node.js image processing"
|
title: "High performance Node.js image processing"
|
||||||
---
|
---
|
||||||
|
|
||||||
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/public/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
|
<img src="/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
|
||||||
|
|
||||||
The typical use case for this high speed Node-API module
|
The typical use case for this high speed Node-API module
|
||||||
is to convert large images in common formats to
|
is to convert large images in common formats to
|
||||||
|
@ -21,7 +21,7 @@ pnpm add sharp
|
|||||||
```
|
```
|
||||||
|
|
||||||
When using `pnpm`, you may need to add `sharp` to
|
When using `pnpm`, you may need to add `sharp` to
|
||||||
[ignoredBuiltDependencies](https://pnpm.io/package_json#pnpmignoredbuiltdependencies)
|
[ignoredBuiltDependencies](https://pnpm.io/settings#ignoredbuiltdependencies)
|
||||||
to silence warnings.
|
to silence warnings.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -53,6 +53,7 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
|
|||||||
* Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
|
* Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
|
||||||
* Windows x64
|
* Windows x64
|
||||||
* Windows x86
|
* Windows x86
|
||||||
|
* Windows ARM64 (experimental, CPU with ARMv8.4 required for all features)
|
||||||
|
|
||||||
This provides support for the
|
This provides support for the
|
||||||
JPEG, PNG, WebP, AVIF (limited to 8-bit depth), TIFF, GIF and SVG (input) image formats.
|
JPEG, PNG, WebP, AVIF (limited to 8-bit depth), TIFF, GIF and SVG (input) image formats.
|
||||||
@ -93,7 +94,7 @@ Use the [supportedArchitectures](https://yarnpkg.com/configuration/yarnrc#suppor
|
|||||||
|
|
||||||
### pnpm v8+
|
### pnpm v8+
|
||||||
|
|
||||||
Use the [supportedArchitectures](https://pnpm.io/package_json#pnpmsupportedarchitectures) configuration.
|
Use the [supportedArchitectures](https://pnpm.io/settings#supportedarchitectures) configuration.
|
||||||
|
|
||||||
## Custom libvips
|
## Custom libvips
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ npm install --save node-addon-api node-gyp
|
|||||||
```
|
```
|
||||||
|
|
||||||
When using `pnpm`, you may need to add `sharp` to
|
When using `pnpm`, you may need to add `sharp` to
|
||||||
[onlyBuiltDependencies](https://pnpm.io/package_json#pnpmonlybuiltdependencies)
|
[onlyBuiltDependencies](https://pnpm.io/settings#onlybuiltdependencies)
|
||||||
to ensure the installation script can be run.
|
to ensure the installation script can be run.
|
||||||
|
|
||||||
For cross-compiling, the `--platform`, `--arch` and `--libc` npm flags
|
For cross-compiling, the `--platform`, `--arch` and `--libc` npm flags
|
||||||
@ -174,7 +175,7 @@ The default memory allocator on most glibc-based Linux systems
|
|||||||
processes that involve lots of small memory allocations.
|
processes that involve lots of small memory allocations.
|
||||||
|
|
||||||
For this reason, by default, sharp will limit the use of thread-based
|
For this reason, by default, sharp will limit the use of thread-based
|
||||||
[concurrency](api-utility#concurrency) when the glibc allocator is
|
[concurrency](/api-utility#concurrency) when the glibc allocator is
|
||||||
detected at runtime.
|
detected at runtime.
|
||||||
|
|
||||||
To help avoid fragmentation and improve performance on these systems,
|
To help avoid fragmentation and improve performance on these systems,
|
||||||
|
@ -2,6 +2,38 @@
|
|||||||
title: Performance
|
title: Performance
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Parallelism and concurrency
|
||||||
|
|
||||||
|
Node.js uses a libuv-managed thread pool when processing asynchronous calls to native modules such as sharp.
|
||||||
|
|
||||||
|
The maximum number of images that sharp can process in parallel is controlled by libuv's
|
||||||
|
[`UV_THREADPOOL_SIZE`](https://nodejs.org/api/cli.html#uv_threadpool_sizesize)
|
||||||
|
environment variable, which defaults to 4.
|
||||||
|
|
||||||
|
When using more than 4 physical CPU cores, set this environment variable
|
||||||
|
before the Node.js process starts to increase the thread pool size.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export UV_THREADPOOL_SIZE="$(lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l)"
|
||||||
|
```
|
||||||
|
|
||||||
|
libvips uses a glib-managed thread pool to avoid the overhead of spawning new threads.
|
||||||
|
|
||||||
|
The default number of threads used to concurrently process each image is the same as the number of CPU cores,
|
||||||
|
except when using glibc-based Linux without jemalloc, where the default is `1` to help reduce memory fragmentation.
|
||||||
|
|
||||||
|
Use [`sharp.concurrency()`](/api-utility/#concurrency) to manage the number of threads per image.
|
||||||
|
|
||||||
|
To reduce memory fragmentation when using the default Linux glibc memory allocator, set the
|
||||||
|
[`MALLOC_ARENA_MAX`](https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html)
|
||||||
|
environment variable before the Node.js process starts to reduce the number of memory pools.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export MALLOC_ARENA_MAX="2"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benchmark
|
||||||
|
|
||||||
A test to benchmark the performance of this module relative to alternatives.
|
A test to benchmark the performance of this module relative to alternatives.
|
||||||
|
|
||||||
Greater libvips performance can be expected with caching enabled (default)
|
Greater libvips performance can be expected with caching enabled (default)
|
||||||
@ -9,28 +41,28 @@ and using 8+ core machines, especially those with larger L1/L2 CPU caches.
|
|||||||
|
|
||||||
The I/O limits of the relevant (de)compression library will generally determine maximum throughput.
|
The I/O limits of the relevant (de)compression library will generally determine maximum throughput.
|
||||||
|
|
||||||
## Contenders
|
### Contenders
|
||||||
|
|
||||||
* [jimp](https://www.npmjs.com/package/jimp) v1.6.0 - Image processing in pure JavaScript.
|
- [jimp](https://www.npmjs.com/package/jimp) v1.6.0 - Image processing in pure JavaScript.
|
||||||
* [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
|
- [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "_has been unmaintained for a long time_".
|
||||||
* [gm](https://www.npmjs.com/package/gm) v1.25.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility, but "*has been sunset*".
|
- [gm](https://www.npmjs.com/package/gm) v1.25.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility, but "_has been sunset_".
|
||||||
* sharp v0.34.0 / libvips v8.16.1 - Caching within libvips disabled to ensure a fair comparison.
|
- sharp v0.34.3 / libvips v8.17.0 - Caching within libvips disabled to ensure a fair comparison.
|
||||||
|
|
||||||
## Environment
|
### Environment
|
||||||
|
|
||||||
### AMD64
|
#### AMD64
|
||||||
|
|
||||||
* AWS EC2 us-west-2 [c7a.xlarge](https://aws.amazon.com/ec2/instance-types/c7a/) (4x AMD EPYC 9R14)
|
- AWS EC2 us-west-2 [c7a.xlarge](https://aws.amazon.com/ec2/instance-types/c7a/) (4x AMD EPYC 9R14)
|
||||||
* Ubuntu 24.10 [fad5ba7223f8](https://hub.docker.com/layers/library/ubuntu/24.10/images/sha256-fad5ba7223f8d87179dfa23211d31845d47e07a474ac31ad5258afb606523c0d)
|
- Ubuntu 25.04
|
||||||
* Node.js 22.14.0
|
- Node.js 24.3.0
|
||||||
|
|
||||||
### ARM64
|
#### ARM64
|
||||||
|
|
||||||
* AWS EC2 us-west-2 [c8g.xlarge](https://aws.amazon.com/ec2/instance-types/c8g/) (4x ARM Graviton4)
|
- AWS EC2 us-west-2 [c8g.xlarge](https://aws.amazon.com/ec2/instance-types/c8g/) (4x ARM Graviton4)
|
||||||
* Ubuntu 24.10 [133f2e05cb69](https://hub.docker.com/layers/library/ubuntu/24.10/images/sha256-133f2e05cb6958c3ce7ec870fd5a864558ba780fb7062315b51a23670bff7e76)
|
- Ubuntu 25.04
|
||||||
* Node.js 22.14.0
|
- Node.js 24.3.0
|
||||||
|
|
||||||
## Task: JPEG
|
### Task: JPEG
|
||||||
|
|
||||||
Decompress a 2725x2225 JPEG image,
|
Decompress a 2725x2225 JPEG image,
|
||||||
resize to 720x588 using Lanczos 3 resampling (where available),
|
resize to 720x588 using Lanczos 3 resampling (where available),
|
||||||
@ -40,29 +72,31 @@ Note: jimp does not support Lanczos 3, bicubic resampling used instead.
|
|||||||
|
|
||||||
#### Results: JPEG (AMD64)
|
#### Results: JPEG (AMD64)
|
||||||
|
|
||||||
| Module | Input | Output | Ops/sec | Speed-up |
|
| Package | I/O | Ops/sec | Speed-up |
|
||||||
| :----------------- | :----- | :----- | ------: | -------: |
|
| :---------- | :----- | ------: | -------: |
|
||||||
| jimp | buffer | buffer | 2.35 | 1.0 |
|
| jimp | buffer | 2.40 | 1.0 |
|
||||||
| imagemagick | file | file | 10.51 | 4.5 |
|
| jimp | file | 2.60 | 1.1 |
|
||||||
| gm | buffer | buffer | 11.67 | 5.0 |
|
| imagemagick | file | 9.70 | 4.0 |
|
||||||
| gm | file | file | 11.75 | 5.1 |
|
| gm | buffer | 11.60 | 4.8 |
|
||||||
| sharp | stream | stream | 60.72 | 25.8 |
|
| gm | file | 11.72 | 4.9 |
|
||||||
| sharp | file | file | 62.37 | 26.5 |
|
| sharp | stream | 59.40 | 24.8 |
|
||||||
| sharp | buffer | buffer | 65.15 | 27.7 |
|
| sharp | file | 62.67 | 26.1 |
|
||||||
|
| sharp | buffer | 64.42 | 26.8 |
|
||||||
|
|
||||||
#### Results: JPEG (ARM64)
|
#### Results: JPEG (ARM64)
|
||||||
|
|
||||||
| Module | Input | Output | Ops/sec | Speed-up |
|
| Package | I/O | Ops/sec | Speed-up |
|
||||||
| :----------------- | :----- | :----- | ------: | -------: |
|
| :---------- | :----- | ------: | -------: |
|
||||||
| jimp | buffer | buffer | 2.13 | 1.0 |
|
| jimp | buffer | 2.24 | 1.0 |
|
||||||
| imagemagick | file | file | 12.95 | 6.1 |
|
| jimp | file | 2.47 | 1.1 |
|
||||||
| gm | buffer | buffer | 13.53 | 6.4 |
|
| imagemagick | file | 10.42 | 4.7 |
|
||||||
| gm | file | file | 13.52 | 6.4 |
|
| gm | buffer | 12.80 | 5.7 |
|
||||||
| sharp | stream | stream | 46.58 | 21.9 |
|
| gm | file | 12.88 | 5.7 |
|
||||||
| sharp | file | file | 48.42 | 22.7 |
|
| sharp | stream | 45.58 | 20.3 |
|
||||||
| sharp | buffer | buffer | 50.16 | 23.6 |
|
| sharp | file | 47.99 | 21.4 |
|
||||||
|
| sharp | buffer | 49.20 | 22.0 |
|
||||||
|
|
||||||
## Task: PNG
|
### Task: PNG
|
||||||
|
|
||||||
Decompress a 2048x1536 RGBA PNG image,
|
Decompress a 2048x1536 RGBA PNG image,
|
||||||
premultiply the alpha channel,
|
premultiply the alpha channel,
|
||||||
@ -72,25 +106,25 @@ and without adaptive filtering.
|
|||||||
|
|
||||||
Note: jimp does not support premultiply/unpremultiply.
|
Note: jimp does not support premultiply/unpremultiply.
|
||||||
|
|
||||||
### Results: PNG (AMD64)
|
#### Results: PNG (AMD64)
|
||||||
|
|
||||||
| Module | Input | Output | Ops/sec | Speed-up |
|
| Package | I/O | Ops/sec | Speed-up |
|
||||||
| :----------------- | :----- | :----- | ------: | -------: |
|
| :---------- | :----- | ------: | -------: |
|
||||||
| gm | file | file | 8.66 | 1.0 |
|
| imagemagick | file | 6.06 | 1.0 |
|
||||||
| imagemagick | file | file | 8.79 | 1.0 |
|
| gm | file | 8.44 | 1.4 |
|
||||||
| jimp | buffer | buffer | 11.26 | 1.3 |
|
| jimp | buffer | 10.98 | 1.8 |
|
||||||
| sharp | file | file | 27.93 | 3.2 |
|
| sharp | file | 28.26 | 4.7 |
|
||||||
| sharp | buffer | buffer | 28.69 | 3.3 |
|
| sharp | buffer | 28.70 | 4.7 |
|
||||||
|
|
||||||
### Results: PNG (ARM64)
|
#### Results: PNG (ARM64)
|
||||||
|
|
||||||
| Module | Input | Output | Ops/sec | Speed-up |
|
| Package | I/O | Ops/sec | Speed-up |
|
||||||
| :----------------- | :----- | :----- | ------: | -------: |
|
| :---------- | :----- | ------: | -------: |
|
||||||
| gm | file | file | 9.65 | 1.0 |
|
| imagemagick | file | 7.09 | 1.0 |
|
||||||
| imagemagick | file | file | 9.72 | 1.0 |
|
| gm | file | 8.93 | 1.3 |
|
||||||
| jimp | buffer | buffer | 10.68 | 1.1 |
|
| jimp | buffer | 10.28 | 1.5 |
|
||||||
| sharp | file | file | 23.90 | 2.5 |
|
| sharp | file | 23.81 | 3.4 |
|
||||||
| sharp | buffer | buffer | 24.48 | 2.5 |
|
| sharp | buffer | 24.19 | 3.4 |
|
||||||
|
|
||||||
## Running the benchmark test
|
## Running the benchmark test
|
||||||
|
|
||||||
|
@ -135,17 +135,15 @@ function toColorspace (colorspace) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a colour attribute of the this.options Object.
|
* Create a RGBA colour array from a given value.
|
||||||
* @private
|
* @private
|
||||||
* @param {string} key
|
|
||||||
* @param {string|Object} value
|
* @param {string|Object} value
|
||||||
* @throws {Error} Invalid value
|
* @throws {Error} Invalid value
|
||||||
*/
|
*/
|
||||||
function _setBackgroundColourOption (key, value) {
|
function _getBackgroundColourOption (value) {
|
||||||
if (is.defined(value)) {
|
|
||||||
if (is.object(value) || is.string(value)) {
|
if (is.object(value) || is.string(value)) {
|
||||||
const colour = color(value);
|
const colour = color(value);
|
||||||
this.options[key] = [
|
return [
|
||||||
colour.red(),
|
colour.red(),
|
||||||
colour.green(),
|
colour.green(),
|
||||||
colour.blue(),
|
colour.blue(),
|
||||||
@ -155,6 +153,18 @@ function _setBackgroundColourOption (key, value) {
|
|||||||
throw is.invalidParameterError('background', 'object or string', value);
|
throw is.invalidParameterError('background', 'object or string', value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a colour attribute of the this.options Object.
|
||||||
|
* @private
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string|Object} value
|
||||||
|
* @throws {Error} Invalid value
|
||||||
|
*/
|
||||||
|
function _setBackgroundColourOption (key, value) {
|
||||||
|
if (is.defined(value)) {
|
||||||
|
this.options[key] = _getBackgroundColourOption(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,6 +183,7 @@ module.exports = function (Sharp) {
|
|||||||
toColourspace,
|
toColourspace,
|
||||||
toColorspace,
|
toColorspace,
|
||||||
// Private
|
// Private
|
||||||
|
_getBackgroundColourOption,
|
||||||
_setBackgroundColourOption
|
_setBackgroundColourOption
|
||||||
});
|
});
|
||||||
// Class attributes
|
// Class attributes
|
||||||
|
@ -153,9 +153,6 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored.
|
* @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored.
|
||||||
* @param {number} [options.pages=1] - Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages.
|
* @param {number} [options.pages=1] - Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages.
|
||||||
* @param {number} [options.page=0] - Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based.
|
* @param {number} [options.page=0] - Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based.
|
||||||
* @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
|
|
||||||
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
|
||||||
* @param {string|Object} [options.pdfBackground] - Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick.
|
|
||||||
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`.
|
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`.
|
||||||
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||||
* @param {number} [options.raw.width] - integral number of pixels wide.
|
* @param {number} [options.raw.width] - integral number of pixels wide.
|
||||||
@ -163,15 +160,17 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
|
* @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
|
||||||
* @param {boolean} [options.raw.premultiplied] - specifies that the raw input has already been premultiplied, set to `true`
|
* @param {boolean} [options.raw.premultiplied] - specifies that the raw input has already been premultiplied, set to `true`
|
||||||
* to avoid sharp premultiplying the image. (optional, default `false`)
|
* to avoid sharp premultiplying the image. (optional, default `false`)
|
||||||
|
* @param {number} [options.raw.pageHeight] - The pixel height of each page/frame for animated images, must be an integral factor of `raw.height`.
|
||||||
* @param {Object} [options.create] - describes a new image to be created.
|
* @param {Object} [options.create] - describes a new image to be created.
|
||||||
* @param {number} [options.create.width] - integral number of pixels wide.
|
* @param {number} [options.create.width] - integral number of pixels wide.
|
||||||
* @param {number} [options.create.height] - integral number of pixels high.
|
* @param {number} [options.create.height] - integral number of pixels high.
|
||||||
* @param {number} [options.create.channels] - integral number of channels, either 3 (RGB) or 4 (RGBA).
|
* @param {number} [options.create.channels] - integral number of channels, either 3 (RGB) or 4 (RGBA).
|
||||||
* @param {string|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
* @param {string|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||||
|
* @param {number} [options.create.pageHeight] - The pixel height of each page/frame for animated images, must be an integral factor of `create.height`.
|
||||||
* @param {Object} [options.create.noise] - describes a noise to be created.
|
* @param {Object} [options.create.noise] - describes a noise to be created.
|
||||||
* @param {string} [options.create.noise.type] - type of generated noise, currently only `gaussian` is supported.
|
* @param {string} [options.create.noise.type] - type of generated noise, currently only `gaussian` is supported.
|
||||||
* @param {number} [options.create.noise.mean] - mean of pixels in generated noise.
|
* @param {number} [options.create.noise.mean=128] - Mean value of pixels in the generated noise.
|
||||||
* @param {number} [options.create.noise.sigma] - standard deviation of pixels in generated noise.
|
* @param {number} [options.create.noise.sigma=30] - Standard deviation of pixel values in the generated noise.
|
||||||
* @param {Object} [options.text] - describes a new text image to be created.
|
* @param {Object} [options.text] - describes a new text image to be created.
|
||||||
* @param {string} [options.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
|
* @param {string} [options.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
|
||||||
* @param {string} [options.text.font] - font name to render with.
|
* @param {string} [options.text.font] - font name to render with.
|
||||||
@ -191,7 +190,17 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* @param {string|Object} [options.join.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
* @param {string|Object} [options.join.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||||
* @param {string} [options.join.halign='left'] - horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`).
|
* @param {string} [options.join.halign='left'] - horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`).
|
||||||
* @param {string} [options.join.valign='top'] - vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`).
|
* @param {string} [options.join.valign='top'] - vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`).
|
||||||
*
|
* @param {Object} [options.tiff] - Describes TIFF specific options.
|
||||||
|
* @param {number} [options.tiff.subifd=-1] - Sub Image File Directory to extract for OME-TIFF, defaults to main image.
|
||||||
|
* @param {Object} [options.svg] - Describes SVG specific options.
|
||||||
|
* @param {string} [options.svg.stylesheet] - Custom CSS for SVG input, applied with a User Origin during the CSS cascade.
|
||||||
|
* @param {boolean} [options.svg.highBitdepth=false] - Set to `true` to render SVG input at 32-bits per channel (128-bit) instead of 8-bits per channel (32-bit) RGBA.
|
||||||
|
* @param {Object} [options.pdf] - Describes PDF specific options. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick.
|
||||||
|
* @param {string|Object} [options.pdf.background] - Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||||
|
* @param {Object} [options.openSlide] - Describes OpenSlide specific options. Requires the use of a globally-installed libvips compiled with support for OpenSlide.
|
||||||
|
* @param {number} [options.openSlide.level=0] - Level to extract from a multi-level input, zero based.
|
||||||
|
* @param {Object} [options.jp2] - Describes JPEG 2000 specific options. Requires the use of a globally-installed libvips compiled with support for OpenJPEG.
|
||||||
|
* @param {boolean} [options.jp2.oneshot=false] - Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility.
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
@ -297,8 +306,9 @@ const Sharp = function (input, options) {
|
|||||||
withIccProfile: '',
|
withIccProfile: '',
|
||||||
withExif: {},
|
withExif: {},
|
||||||
withExifMerge: true,
|
withExifMerge: true,
|
||||||
|
withXmp: '',
|
||||||
resolveWithObject: false,
|
resolveWithObject: false,
|
||||||
loop: 1,
|
loop: -1,
|
||||||
delay: [],
|
delay: [],
|
||||||
// output format
|
// output format
|
||||||
jpegQuality: 80,
|
jpegQuality: 80,
|
||||||
@ -337,6 +347,7 @@ const Sharp = function (input, options) {
|
|||||||
gifDither: 1,
|
gifDither: 1,
|
||||||
gifInterFrameMaxError: 0,
|
gifInterFrameMaxError: 0,
|
||||||
gifInterPaletteMaxError: 3,
|
gifInterPaletteMaxError: 3,
|
||||||
|
gifKeepDuplicateFrames: false,
|
||||||
gifReuse: true,
|
gifReuse: true,
|
||||||
gifProgressive: false,
|
gifProgressive: false,
|
||||||
tiffQuality: 80,
|
tiffQuality: 80,
|
||||||
|
136
lib/index.d.ts
vendored
136
lib/index.d.ts
vendored
@ -730,6 +730,20 @@ declare namespace sharp {
|
|||||||
*/
|
*/
|
||||||
withIccProfile(icc: string, options?: WithIccProfileOptions): Sharp;
|
withIccProfile(icc: string, options?: WithIccProfileOptions): Sharp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep all XMP metadata from the input image in the output image.
|
||||||
|
* @returns A sharp instance that can be used to chain operations
|
||||||
|
*/
|
||||||
|
keepXmp(): Sharp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set XMP metadata in the output image.
|
||||||
|
* @param {string} xmp - String containing XMP metadata to be embedded in the output image.
|
||||||
|
* @returns A sharp instance that can be used to chain operations
|
||||||
|
* @throws {Error} Invalid parameters
|
||||||
|
*/
|
||||||
|
withXmp(xmp: string): Sharp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
||||||
* The default behaviour, when withMetadata is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
|
* The default behaviour, when withMetadata is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
|
||||||
@ -1003,12 +1017,22 @@ declare namespace sharp {
|
|||||||
pages?: number | undefined;
|
pages?: number | undefined;
|
||||||
/** Page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default 0) */
|
/** Page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default 0) */
|
||||||
page?: number | undefined;
|
page?: number | undefined;
|
||||||
/** subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. (optional, default -1) */
|
/** TIFF specific input options */
|
||||||
|
tiff?: TiffInputOptions | undefined;
|
||||||
|
/** SVG specific input options */
|
||||||
|
svg?: SvgInputOptions | undefined;
|
||||||
|
/** PDF specific input options */
|
||||||
|
pdf?: PdfInputOptions | undefined;
|
||||||
|
/** OpenSlide specific input options */
|
||||||
|
openSlide?: OpenSlideInputOptions | undefined;
|
||||||
|
/** JPEG 2000 specific input options */
|
||||||
|
jp2?: Jp2InputOptions | undefined;
|
||||||
|
/** Deprecated: use tiff.subifd instead */
|
||||||
subifd?: number | undefined;
|
subifd?: number | undefined;
|
||||||
/** Level to extract from a multi-level input (OpenSlide), zero based. (optional, default 0) */
|
/** Deprecated: use pdf.background instead */
|
||||||
level?: number | undefined;
|
|
||||||
/** Background colour to use when PDF is partially transparent. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. */
|
|
||||||
pdfBackground?: Colour | Color | undefined;
|
pdfBackground?: Colour | Color | undefined;
|
||||||
|
/** Deprecated: use openSlide.level instead */
|
||||||
|
level?: number | undefined;
|
||||||
/** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */
|
/** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */
|
||||||
animated?: boolean | undefined;
|
animated?: boolean | undefined;
|
||||||
/** Describes raw pixel input image data. See raw() for pixel ordering. */
|
/** Describes raw pixel input image data. See raw() for pixel ordering. */
|
||||||
@ -1051,6 +1075,8 @@ declare namespace sharp {
|
|||||||
interface CreateRaw extends Raw {
|
interface CreateRaw extends Raw {
|
||||||
/** Specifies that the raw input has already been premultiplied, set to true to avoid sharp premultiplying the image. (optional, default false) */
|
/** Specifies that the raw input has already been premultiplied, set to true to avoid sharp premultiplying the image. (optional, default false) */
|
||||||
premultiplied?: boolean | undefined;
|
premultiplied?: boolean | undefined;
|
||||||
|
/** The height of each page/frame for animated images, must be an integral factor of the overall image height. */
|
||||||
|
pageHeight?: number | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateChannels = 3 | 4;
|
type CreateChannels = 3 | 4;
|
||||||
@ -1066,6 +1092,9 @@ declare namespace sharp {
|
|||||||
background: Colour | Color;
|
background: Colour | Color;
|
||||||
/** Describes a noise to be created. */
|
/** Describes a noise to be created. */
|
||||||
noise?: Noise | undefined;
|
noise?: Noise | undefined;
|
||||||
|
/** The height of each page/frame for animated images, must be an integral factor of the overall image height. */
|
||||||
|
pageHeight?: number | undefined;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateText {
|
interface CreateText {
|
||||||
@ -1114,6 +1143,33 @@ declare namespace sharp {
|
|||||||
valign?: VerticalAlignment | undefined;
|
valign?: VerticalAlignment | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TiffInputOptions {
|
||||||
|
/** Sub Image File Directory to extract, defaults to main image. Use -1 for all subifds. */
|
||||||
|
subifd?: number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SvgInputOptions {
|
||||||
|
/** Custom CSS for SVG input, applied with a User Origin during the CSS cascade. */
|
||||||
|
stylesheet?: string | undefined;
|
||||||
|
/** Set to `true` to render SVG input at 32-bits per channel (128-bit) instead of 8-bits per channel (32-bit) RGBA. */
|
||||||
|
highBitdepth?: boolean | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PdfInputOptions {
|
||||||
|
/** Background colour to use when PDF is partially transparent. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. */
|
||||||
|
background?: Colour | Color | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OpenSlideInputOptions {
|
||||||
|
/** Level to extract from a multi-level input, zero based. (optional, default 0) */
|
||||||
|
level?: number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Jp2InputOptions {
|
||||||
|
/** Set to `true` to load JPEG 2000 images using [oneshot mode](https://github.com/libvips/libvips/issues/4205) */
|
||||||
|
oneshot?: boolean | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
interface ExifDir {
|
interface ExifDir {
|
||||||
[k: string]: string;
|
[k: string]: string;
|
||||||
}
|
}
|
||||||
@ -1146,13 +1202,13 @@ declare namespace sharp {
|
|||||||
/** Number value of the EXIF Orientation header, if present */
|
/** Number value of the EXIF Orientation header, if present */
|
||||||
orientation?: number | undefined;
|
orientation?: number | undefined;
|
||||||
/** Name of decoder used to decompress image data e.g. jpeg, png, webp, gif, svg */
|
/** Name of decoder used to decompress image data e.g. jpeg, png, webp, gif, svg */
|
||||||
format?: keyof FormatEnum | undefined;
|
format: keyof FormatEnum;
|
||||||
/** Total size of image in bytes, for Stream and Buffer input only */
|
/** Total size of image in bytes, for Stream and Buffer input only */
|
||||||
size?: number | undefined;
|
size?: number | undefined;
|
||||||
/** Number of pixels wide (EXIF orientation is not taken into consideration) */
|
/** Number of pixels wide (EXIF orientation is not taken into consideration) */
|
||||||
width?: number | undefined;
|
width: number;
|
||||||
/** Number of pixels high (EXIF orientation is not taken into consideration) */
|
/** Number of pixels high (EXIF orientation is not taken into consideration) */
|
||||||
height?: number | undefined;
|
height: number;
|
||||||
/** Any changed metadata after the image orientation is applied. */
|
/** Any changed metadata after the image orientation is applied. */
|
||||||
autoOrient: {
|
autoOrient: {
|
||||||
/** Number of pixels wide (EXIF orientation is taken into consideration) */
|
/** Number of pixels wide (EXIF orientation is taken into consideration) */
|
||||||
@ -1161,19 +1217,19 @@ declare namespace sharp {
|
|||||||
height: number;
|
height: number;
|
||||||
};
|
};
|
||||||
/** Name of colour space interpretation */
|
/** Name of colour space interpretation */
|
||||||
space?: keyof ColourspaceEnum | undefined;
|
space: keyof ColourspaceEnum;
|
||||||
/** Number of bands e.g. 3 for sRGB, 4 for CMYK */
|
/** Number of bands e.g. 3 for sRGB, 4 for CMYK */
|
||||||
channels?: Channels | undefined;
|
channels: Channels;
|
||||||
/** Name of pixel depth format e.g. uchar, char, ushort, float ... */
|
/** Name of pixel depth format e.g. uchar, char, ushort, float ... */
|
||||||
depth?: string | undefined;
|
depth: keyof DepthEnum;
|
||||||
/** Number of pixels per inch (DPI), if present */
|
/** Number of pixels per inch (DPI), if present */
|
||||||
density?: number | undefined;
|
density?: number | undefined;
|
||||||
/** String containing JPEG chroma subsampling, 4:2:0 or 4:4:4 for RGB, 4:2:0:4 or 4:4:4:4 for CMYK */
|
/** String containing JPEG chroma subsampling, 4:2:0 or 4:4:4 for RGB, 4:2:0:4 or 4:4:4:4 for CMYK */
|
||||||
chromaSubsampling?: string | undefined;
|
chromaSubsampling?: string | undefined;
|
||||||
/** Boolean indicating whether the image is interlaced using a progressive scan */
|
/** Boolean indicating whether the image is interlaced using a progressive scan */
|
||||||
isProgressive?: boolean | undefined;
|
isProgressive: boolean;
|
||||||
/** Boolean indicating whether the image is palette-based (GIF, PNG). */
|
/** Boolean indicating whether the image is palette-based (GIF, PNG). */
|
||||||
isPalette?: boolean | undefined;
|
isPalette: boolean;
|
||||||
/** Number of bits per sample for each channel (GIF, PNG). */
|
/** Number of bits per sample for each channel (GIF, PNG). */
|
||||||
bitsPerSample?: number | undefined;
|
bitsPerSample?: number | undefined;
|
||||||
/** Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP */
|
/** Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP */
|
||||||
@ -1187,9 +1243,9 @@ declare namespace sharp {
|
|||||||
/** Number of the primary page in a HEIF image */
|
/** Number of the primary page in a HEIF image */
|
||||||
pagePrimary?: number | undefined;
|
pagePrimary?: number | undefined;
|
||||||
/** Boolean indicating the presence of an embedded ICC profile */
|
/** Boolean indicating the presence of an embedded ICC profile */
|
||||||
hasProfile?: boolean | undefined;
|
hasProfile: boolean;
|
||||||
/** Boolean indicating the presence of an alpha transparency channel */
|
/** Boolean indicating the presence of an alpha transparency channel */
|
||||||
hasAlpha?: boolean | undefined;
|
hasAlpha: boolean;
|
||||||
/** Buffer containing raw EXIF data, if present */
|
/** Buffer containing raw EXIF data, if present */
|
||||||
exif?: Buffer | undefined;
|
exif?: Buffer | undefined;
|
||||||
/** Buffer containing raw ICC profile data, if present */
|
/** Buffer containing raw ICC profile data, if present */
|
||||||
@ -1198,6 +1254,8 @@ declare namespace sharp {
|
|||||||
iptc?: Buffer | undefined;
|
iptc?: Buffer | undefined;
|
||||||
/** Buffer containing raw XMP data, if present */
|
/** Buffer containing raw XMP data, if present */
|
||||||
xmp?: Buffer | undefined;
|
xmp?: Buffer | undefined;
|
||||||
|
/** String containing XMP data, if valid UTF-8 */
|
||||||
|
xmpAsString?: string | undefined;
|
||||||
/** Buffer containing raw TIFFTAG_PHOTOSHOP data, if present */
|
/** Buffer containing raw TIFFTAG_PHOTOSHOP data, if present */
|
||||||
tifftagPhotoshop?: Buffer | undefined;
|
tifftagPhotoshop?: Buffer | undefined;
|
||||||
/** The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC) */
|
/** The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC) */
|
||||||
@ -1336,6 +1394,8 @@ declare namespace sharp {
|
|||||||
nearLossless?: boolean | undefined;
|
nearLossless?: boolean | undefined;
|
||||||
/** Use high quality chroma subsampling (optional, default false) */
|
/** Use high quality chroma subsampling (optional, default false) */
|
||||||
smartSubsample?: boolean | undefined;
|
smartSubsample?: boolean | undefined;
|
||||||
|
/** Auto-adjust the deblocking filter, slow but can improve low contrast edges (optional, default false) */
|
||||||
|
smartDeblock?: boolean | undefined;
|
||||||
/** Level of CPU effort to reduce file size, integer 0-6 (optional, default 4) */
|
/** Level of CPU effort to reduce file size, integer 0-6 (optional, default 4) */
|
||||||
effort?: number | undefined;
|
effort?: number | undefined;
|
||||||
/** Prevent use of animation key frames to minimise file size (slow) (optional, default false) */
|
/** Prevent use of animation key frames to minimise file size (slow) (optional, default false) */
|
||||||
@ -1388,9 +1448,11 @@ declare namespace sharp {
|
|||||||
/** Level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) (optional, default 1.0) */
|
/** Level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most) (optional, default 1.0) */
|
||||||
dither?: number | undefined;
|
dither?: number | undefined;
|
||||||
/** Maximum inter-frame error for transparency, between 0 (lossless) and 32 (optional, default 0) */
|
/** Maximum inter-frame error for transparency, between 0 (lossless) and 32 (optional, default 0) */
|
||||||
interFrameMaxError?: number;
|
interFrameMaxError?: number | undefined;
|
||||||
/** Maximum inter-palette error for palette reuse, between 0 and 256 (optional, default 3) */
|
/** Maximum inter-palette error for palette reuse, between 0 and 256 (optional, default 3) */
|
||||||
interPaletteMaxError?: number;
|
interPaletteMaxError?: number | undefined;
|
||||||
|
/** Keep duplicate frames in the output instead of combining them (optional, default false) */
|
||||||
|
keepDuplicateFrames?: boolean | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TiffOptions extends OutputOptions {
|
interface TiffOptions extends OutputOptions {
|
||||||
@ -1508,7 +1570,7 @@ declare namespace sharp {
|
|||||||
|
|
||||||
interface Noise {
|
interface Noise {
|
||||||
/** type of generated noise, currently only gaussian is supported. */
|
/** type of generated noise, currently only gaussian is supported. */
|
||||||
type?: 'gaussian' | undefined;
|
type: 'gaussian';
|
||||||
/** mean of pixels in generated noise. */
|
/** mean of pixels in generated noise. */
|
||||||
mean?: number | undefined;
|
mean?: number | undefined;
|
||||||
/** standard deviation of pixels in generated noise. */
|
/** standard deviation of pixels in generated noise. */
|
||||||
@ -1699,6 +1761,10 @@ declare namespace sharp {
|
|||||||
/** When using the attention crop strategy, the focal point of the cropped region */
|
/** When using the attention crop strategy, the focal point of the cropped region */
|
||||||
attentionX?: number | undefined;
|
attentionX?: number | undefined;
|
||||||
attentionY?: number | undefined;
|
attentionY?: number | undefined;
|
||||||
|
/** Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP */
|
||||||
|
pages?: number | undefined;
|
||||||
|
/** Number of pixels high each page in a multi-page image will be. */
|
||||||
|
pageHeight?: number | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AvailableFormatInfo {
|
interface AvailableFormatInfo {
|
||||||
@ -1721,6 +1787,8 @@ declare namespace sharp {
|
|||||||
mitchell: 'mitchell';
|
mitchell: 'mitchell';
|
||||||
lanczos2: 'lanczos2';
|
lanczos2: 'lanczos2';
|
||||||
lanczos3: 'lanczos3';
|
lanczos3: 'lanczos3';
|
||||||
|
mks2013: 'mks2013';
|
||||||
|
mks2021: 'mks2021';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PresetEnum {
|
interface PresetEnum {
|
||||||
@ -1739,11 +1807,38 @@ declare namespace sharp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ColourspaceEnum {
|
interface ColourspaceEnum {
|
||||||
multiband: string;
|
|
||||||
'b-w': string;
|
'b-w': string;
|
||||||
bw: string;
|
cmc: string;
|
||||||
cmyk: string;
|
cmyk: string;
|
||||||
|
fourier: string;
|
||||||
|
grey16: string;
|
||||||
|
histogram: string;
|
||||||
|
hsv: string;
|
||||||
|
lab: string;
|
||||||
|
labq: string;
|
||||||
|
labs: string;
|
||||||
|
lch: string;
|
||||||
|
matrix: string;
|
||||||
|
multiband: string;
|
||||||
|
rgb: string;
|
||||||
|
rgb16: string;
|
||||||
|
scrgb: string;
|
||||||
srgb: string;
|
srgb: string;
|
||||||
|
xyz: string;
|
||||||
|
yxy: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DepthEnum {
|
||||||
|
char: string;
|
||||||
|
complex: string;
|
||||||
|
double: string;
|
||||||
|
dpcomplex: string;
|
||||||
|
float: string;
|
||||||
|
int: string;
|
||||||
|
short: string;
|
||||||
|
uchar: string;
|
||||||
|
uint: string;
|
||||||
|
ushort: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type FailOnOptions = 'none' | 'truncated' | 'error' | 'warning';
|
type FailOnOptions = 'none' | 'truncated' | 'error' | 'warning';
|
||||||
@ -1811,7 +1906,9 @@ declare namespace sharp {
|
|||||||
|
|
||||||
interface FormatEnum {
|
interface FormatEnum {
|
||||||
avif: AvailableFormatInfo;
|
avif: AvailableFormatInfo;
|
||||||
|
dcraw: AvailableFormatInfo;
|
||||||
dz: AvailableFormatInfo;
|
dz: AvailableFormatInfo;
|
||||||
|
exr: AvailableFormatInfo;
|
||||||
fits: AvailableFormatInfo;
|
fits: AvailableFormatInfo;
|
||||||
gif: AvailableFormatInfo;
|
gif: AvailableFormatInfo;
|
||||||
heif: AvailableFormatInfo;
|
heif: AvailableFormatInfo;
|
||||||
@ -1825,6 +1922,7 @@ declare namespace sharp {
|
|||||||
pdf: AvailableFormatInfo;
|
pdf: AvailableFormatInfo;
|
||||||
png: AvailableFormatInfo;
|
png: AvailableFormatInfo;
|
||||||
ppm: AvailableFormatInfo;
|
ppm: AvailableFormatInfo;
|
||||||
|
rad: AvailableFormatInfo;
|
||||||
raw: AvailableFormatInfo;
|
raw: AvailableFormatInfo;
|
||||||
svg: AvailableFormatInfo;
|
svg: AvailableFormatInfo;
|
||||||
tiff: AvailableFormatInfo;
|
tiff: AvailableFormatInfo;
|
||||||
|
139
lib/input.js
139
lib/input.js
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const color = require('color');
|
|
||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
const sharp = require('./sharp');
|
const sharp = require('./sharp');
|
||||||
|
|
||||||
@ -23,14 +22,27 @@ const align = {
|
|||||||
high: 'high'
|
high: 'high'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const inputStreamParameters = [
|
||||||
|
// Limits and error handling
|
||||||
|
'failOn', 'limitInputPixels', 'unlimited',
|
||||||
|
// Format-generic
|
||||||
|
'animated', 'autoOrient', 'density', 'ignoreIcc', 'page', 'pages', 'sequentialRead',
|
||||||
|
// Format-specific
|
||||||
|
'jp2', 'openSlide', 'pdf', 'raw', 'svg', 'tiff',
|
||||||
|
// Deprecated
|
||||||
|
'failOnError', 'openSlideLevel', 'pdfBackground', 'tiffSubifd'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract input options, if any, from an object.
|
* Extract input options, if any, from an object.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function _inputOptionsFromObject (obj) {
|
function _inputOptionsFromObject (obj) {
|
||||||
const { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient } = obj;
|
const params = inputStreamParameters
|
||||||
return [raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient].some(is.defined)
|
.filter(p => is.defined(obj[p]))
|
||||||
? { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient }
|
.map(p => ([p, obj[p]]));
|
||||||
|
return params.length
|
||||||
|
? Object.fromEntries(params)
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,8 +185,6 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
inputDescriptor.rawWidth = inputOptions.raw.width;
|
inputDescriptor.rawWidth = inputOptions.raw.width;
|
||||||
inputDescriptor.rawHeight = inputOptions.raw.height;
|
inputDescriptor.rawHeight = inputOptions.raw.height;
|
||||||
inputDescriptor.rawChannels = inputOptions.raw.channels;
|
inputDescriptor.rawChannels = inputOptions.raw.channels;
|
||||||
inputDescriptor.rawPremultiplied = !!inputOptions.raw.premultiplied;
|
|
||||||
|
|
||||||
switch (input.constructor) {
|
switch (input.constructor) {
|
||||||
case Uint8Array:
|
case Uint8Array:
|
||||||
case Uint8ClampedArray:
|
case Uint8ClampedArray:
|
||||||
@ -208,6 +218,25 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error('Expected width, height and channels for raw pixel input');
|
throw new Error('Expected width, height and channels for raw pixel input');
|
||||||
}
|
}
|
||||||
|
inputDescriptor.rawPremultiplied = false;
|
||||||
|
if (is.defined(inputOptions.raw.premultiplied)) {
|
||||||
|
if (is.bool(inputOptions.raw.premultiplied)) {
|
||||||
|
inputDescriptor.rawPremultiplied = inputOptions.raw.premultiplied;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('raw.premultiplied', 'boolean', inputOptions.raw.premultiplied);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputDescriptor.rawPageHeight = 0;
|
||||||
|
if (is.defined(inputOptions.raw.pageHeight)) {
|
||||||
|
if (is.integer(inputOptions.raw.pageHeight) && inputOptions.raw.pageHeight > 0 && inputOptions.raw.pageHeight <= inputOptions.raw.height) {
|
||||||
|
if (inputOptions.raw.height % inputOptions.raw.pageHeight !== 0) {
|
||||||
|
throw new Error(`Expected raw.height ${inputOptions.raw.height} to be a multiple of raw.pageHeight ${inputOptions.raw.pageHeight}`);
|
||||||
|
}
|
||||||
|
inputDescriptor.rawPageHeight = inputOptions.raw.pageHeight;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('raw.pageHeight', 'positive integer', inputOptions.raw.pageHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Multi-page input (GIF, TIFF, PDF)
|
// Multi-page input (GIF, TIFF, PDF)
|
||||||
if (is.defined(inputOptions.animated)) {
|
if (is.defined(inputOptions.animated)) {
|
||||||
@ -231,25 +260,67 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
|
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Multi-level input (OpenSlide)
|
// OpenSlide specific options
|
||||||
if (is.defined(inputOptions.level)) {
|
if (is.object(inputOptions.openSlide) && is.defined(inputOptions.openSlide.level)) {
|
||||||
|
if (is.integer(inputOptions.openSlide.level) && is.inRange(inputOptions.openSlide.level, 0, 256)) {
|
||||||
|
inputDescriptor.openSlideLevel = inputOptions.openSlide.level;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('openSlide.level', 'integer between 0 and 256', inputOptions.openSlide.level);
|
||||||
|
}
|
||||||
|
} else if (is.defined(inputOptions.level)) {
|
||||||
|
// Deprecated
|
||||||
if (is.integer(inputOptions.level) && is.inRange(inputOptions.level, 0, 256)) {
|
if (is.integer(inputOptions.level) && is.inRange(inputOptions.level, 0, 256)) {
|
||||||
inputDescriptor.level = inputOptions.level;
|
inputDescriptor.openSlideLevel = inputOptions.level;
|
||||||
} else {
|
} else {
|
||||||
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
|
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sub Image File Directory (TIFF)
|
// TIFF specific options
|
||||||
if (is.defined(inputOptions.subifd)) {
|
if (is.object(inputOptions.tiff) && is.defined(inputOptions.tiff.subifd)) {
|
||||||
|
if (is.integer(inputOptions.tiff.subifd) && is.inRange(inputOptions.tiff.subifd, -1, 100000)) {
|
||||||
|
inputDescriptor.tiffSubifd = inputOptions.tiff.subifd;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('tiff.subifd', 'integer between -1 and 100000', inputOptions.tiff.subifd);
|
||||||
|
}
|
||||||
|
} else if (is.defined(inputOptions.subifd)) {
|
||||||
|
// Deprecated
|
||||||
if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
|
if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
|
||||||
inputDescriptor.subifd = inputOptions.subifd;
|
inputDescriptor.tiffSubifd = inputOptions.subifd;
|
||||||
} else {
|
} else {
|
||||||
throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
|
throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// PDF background colour
|
// SVG specific options
|
||||||
if (is.defined(inputOptions.pdfBackground)) {
|
if (is.object(inputOptions.svg)) {
|
||||||
this._setBackgroundColourOption('pdfBackground', inputOptions.pdfBackground);
|
if (is.defined(inputOptions.svg.stylesheet)) {
|
||||||
|
if (is.string(inputOptions.svg.stylesheet)) {
|
||||||
|
inputDescriptor.svgStylesheet = inputOptions.svg.stylesheet;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('svg.stylesheet', 'string', inputOptions.svg.stylesheet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is.defined(inputOptions.svg.highBitdepth)) {
|
||||||
|
if (is.bool(inputOptions.svg.highBitdepth)) {
|
||||||
|
inputDescriptor.svgHighBitdepth = inputOptions.svg.highBitdepth;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('svg.highBitdepth', 'boolean', inputOptions.svg.highBitdepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// PDF specific options
|
||||||
|
if (is.object(inputOptions.pdf) && is.defined(inputOptions.pdf.background)) {
|
||||||
|
inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdf.background);
|
||||||
|
} else if (is.defined(inputOptions.pdfBackground)) {
|
||||||
|
// Deprecated
|
||||||
|
inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdfBackground);
|
||||||
|
}
|
||||||
|
// JPEG 2000 specific options
|
||||||
|
if (is.object(inputOptions.jp2) && is.defined(inputOptions.jp2.oneshot)) {
|
||||||
|
if (is.bool(inputOptions.jp2.oneshot)) {
|
||||||
|
inputDescriptor.jp2Oneshot = inputOptions.jp2.oneshot;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('jp2.oneshot', 'boolean', inputOptions.jp2.oneshot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Create new image
|
// Create new image
|
||||||
if (is.defined(inputOptions.create)) {
|
if (is.defined(inputOptions.create)) {
|
||||||
@ -262,39 +333,50 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
inputDescriptor.createWidth = inputOptions.create.width;
|
inputDescriptor.createWidth = inputOptions.create.width;
|
||||||
inputDescriptor.createHeight = inputOptions.create.height;
|
inputDescriptor.createHeight = inputOptions.create.height;
|
||||||
inputDescriptor.createChannels = inputOptions.create.channels;
|
inputDescriptor.createChannels = inputOptions.create.channels;
|
||||||
|
inputDescriptor.createPageHeight = 0;
|
||||||
|
if (is.defined(inputOptions.create.pageHeight)) {
|
||||||
|
if (is.integer(inputOptions.create.pageHeight) && inputOptions.create.pageHeight > 0 && inputOptions.create.pageHeight <= inputOptions.create.height) {
|
||||||
|
if (inputOptions.create.height % inputOptions.create.pageHeight !== 0) {
|
||||||
|
throw new Error(`Expected create.height ${inputOptions.create.height} to be a multiple of create.pageHeight ${inputOptions.create.pageHeight}`);
|
||||||
|
}
|
||||||
|
inputDescriptor.createPageHeight = inputOptions.create.pageHeight;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('create.pageHeight', 'positive integer', inputOptions.create.pageHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Noise
|
// Noise
|
||||||
if (is.defined(inputOptions.create.noise)) {
|
if (is.defined(inputOptions.create.noise)) {
|
||||||
if (!is.object(inputOptions.create.noise)) {
|
if (!is.object(inputOptions.create.noise)) {
|
||||||
throw new Error('Expected noise to be an object');
|
throw new Error('Expected noise to be an object');
|
||||||
}
|
}
|
||||||
if (!is.inArray(inputOptions.create.noise.type, ['gaussian'])) {
|
if (inputOptions.create.noise.type !== 'gaussian') {
|
||||||
throw new Error('Only gaussian noise is supported at the moment');
|
throw new Error('Only gaussian noise is supported at the moment');
|
||||||
}
|
}
|
||||||
|
inputDescriptor.createNoiseType = inputOptions.create.noise.type;
|
||||||
if (!is.inRange(inputOptions.create.channels, 1, 4)) {
|
if (!is.inRange(inputOptions.create.channels, 1, 4)) {
|
||||||
throw is.invalidParameterError('create.channels', 'number between 1 and 4', inputOptions.create.channels);
|
throw is.invalidParameterError('create.channels', 'number between 1 and 4', inputOptions.create.channels);
|
||||||
}
|
}
|
||||||
inputDescriptor.createNoiseType = inputOptions.create.noise.type;
|
inputDescriptor.createNoiseMean = 128;
|
||||||
|
if (is.defined(inputOptions.create.noise.mean)) {
|
||||||
if (is.number(inputOptions.create.noise.mean) && is.inRange(inputOptions.create.noise.mean, 0, 10000)) {
|
if (is.number(inputOptions.create.noise.mean) && is.inRange(inputOptions.create.noise.mean, 0, 10000)) {
|
||||||
inputDescriptor.createNoiseMean = inputOptions.create.noise.mean;
|
inputDescriptor.createNoiseMean = inputOptions.create.noise.mean;
|
||||||
} else {
|
} else {
|
||||||
throw is.invalidParameterError('create.noise.mean', 'number between 0 and 10000', inputOptions.create.noise.mean);
|
throw is.invalidParameterError('create.noise.mean', 'number between 0 and 10000', inputOptions.create.noise.mean);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
inputDescriptor.createNoiseSigma = 30;
|
||||||
|
if (is.defined(inputOptions.create.noise.sigma)) {
|
||||||
if (is.number(inputOptions.create.noise.sigma) && is.inRange(inputOptions.create.noise.sigma, 0, 10000)) {
|
if (is.number(inputOptions.create.noise.sigma) && is.inRange(inputOptions.create.noise.sigma, 0, 10000)) {
|
||||||
inputDescriptor.createNoiseSigma = inputOptions.create.noise.sigma;
|
inputDescriptor.createNoiseSigma = inputOptions.create.noise.sigma;
|
||||||
} else {
|
} else {
|
||||||
throw is.invalidParameterError('create.noise.sigma', 'number between 0 and 10000', inputOptions.create.noise.sigma);
|
throw is.invalidParameterError('create.noise.sigma', 'number between 0 and 10000', inputOptions.create.noise.sigma);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (is.defined(inputOptions.create.background)) {
|
} else if (is.defined(inputOptions.create.background)) {
|
||||||
if (!is.inRange(inputOptions.create.channels, 3, 4)) {
|
if (!is.inRange(inputOptions.create.channels, 3, 4)) {
|
||||||
throw is.invalidParameterError('create.channels', 'number between 3 and 4', inputOptions.create.channels);
|
throw is.invalidParameterError('create.channels', 'number between 3 and 4', inputOptions.create.channels);
|
||||||
}
|
}
|
||||||
const background = color(inputOptions.create.background);
|
inputDescriptor.createBackground = this._getBackgroundColourOption(inputOptions.create.background);
|
||||||
inputDescriptor.createBackground = [
|
|
||||||
background.red(),
|
|
||||||
background.green(),
|
|
||||||
background.blue(),
|
|
||||||
Math.round(background.alpha() * 255)
|
|
||||||
];
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Expected valid noise or background to create a new input image');
|
throw new Error('Expected valid noise or background to create a new input image');
|
||||||
}
|
}
|
||||||
@ -410,13 +492,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.defined(inputOptions.join.background)) {
|
if (is.defined(inputOptions.join.background)) {
|
||||||
const background = color(inputOptions.join.background);
|
inputDescriptor.joinBackground = this._getBackgroundColourOption(inputOptions.join.background);
|
||||||
inputDescriptor.joinBackground = [
|
|
||||||
background.red(),
|
|
||||||
background.green(),
|
|
||||||
background.blue(),
|
|
||||||
Math.round(background.alpha() * 255)
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
if (is.defined(inputOptions.join.halign)) {
|
if (is.defined(inputOptions.join.halign)) {
|
||||||
if (is.string(inputOptions.join.halign) && is.string(this.constructor.align[inputOptions.join.halign])) {
|
if (is.string(inputOptions.join.halign) && is.string(this.constructor.align[inputOptions.join.halign])) {
|
||||||
@ -529,6 +605,7 @@ function _isStreamInput () {
|
|||||||
* - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
|
* - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
|
||||||
* - `iptc`: Buffer containing raw IPTC data, if present
|
* - `iptc`: Buffer containing raw IPTC data, if present
|
||||||
* - `xmp`: Buffer containing raw XMP data, if present
|
* - `xmp`: Buffer containing raw XMP data, if present
|
||||||
|
* - `xmpAsString`: String containing XMP data, if valid UTF-8.
|
||||||
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
||||||
* - `formatMagick`: String containing format for images loaded via *magick
|
* - `formatMagick`: String containing format for images loaded via *magick
|
||||||
* - `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
|
* - `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
|
||||||
|
@ -18,9 +18,9 @@ const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).versio
|
|||||||
|
|
||||||
const prebuiltPlatforms = [
|
const prebuiltPlatforms = [
|
||||||
'darwin-arm64', 'darwin-x64',
|
'darwin-arm64', 'darwin-x64',
|
||||||
'linux-arm', 'linux-arm64', 'linux-s390x', 'linux-x64',
|
'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-s390x', 'linux-x64',
|
||||||
'linuxmusl-arm64', 'linuxmusl-x64',
|
'linuxmusl-arm64', 'linuxmusl-x64',
|
||||||
'win32-ia32', 'win32-x64'
|
'win32-arm64', 'win32-ia32', 'win32-x64'
|
||||||
];
|
];
|
||||||
|
|
||||||
const spawnSyncOptions = {
|
const spawnSyncOptions = {
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const color = require('color');
|
|
||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,13 +66,7 @@ function rotate (angle, options) {
|
|||||||
} else if (is.number(angle)) {
|
} else if (is.number(angle)) {
|
||||||
this.options.rotationAngle = angle;
|
this.options.rotationAngle = angle;
|
||||||
if (is.object(options) && options.background) {
|
if (is.object(options) && options.background) {
|
||||||
const backgroundColour = color(options.background);
|
this._setBackgroundColourOption('rotationBackground', options.background);
|
||||||
this.options.rotationBackground = [
|
|
||||||
backgroundColour.red(),
|
|
||||||
backgroundColour.green(),
|
|
||||||
backgroundColour.blue(),
|
|
||||||
Math.round(backgroundColour.alpha() * 255)
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw is.invalidParameterError('angle', 'numeric', angle);
|
throw is.invalidParameterError('angle', 'numeric', angle);
|
||||||
|
@ -312,6 +312,59 @@ function withIccProfile (icc, options) {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep XMP metadata from the input image in the output image.
|
||||||
|
*
|
||||||
|
* @since 0.34.3
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const outputWithXmp = await sharp(inputWithXmp)
|
||||||
|
* .keepXmp()
|
||||||
|
* .toBuffer();
|
||||||
|
*
|
||||||
|
* @returns {Sharp}
|
||||||
|
*/
|
||||||
|
function keepXmp () {
|
||||||
|
this.options.keepMetadata |= 0b00010;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set XMP metadata in the output image.
|
||||||
|
*
|
||||||
|
* Supported by PNG, JPEG, WebP, and TIFF output.
|
||||||
|
*
|
||||||
|
* @since 0.34.3
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const xmpString = `
|
||||||
|
* <?xml version="1.0"?>
|
||||||
|
* <x:xmpmeta xmlns:x="adobe:ns:meta/">
|
||||||
|
* <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||||
|
* <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
* <dc:creator><rdf:Seq><rdf:li>John Doe</rdf:li></rdf:Seq></dc:creator>
|
||||||
|
* </rdf:Description>
|
||||||
|
* </rdf:RDF>
|
||||||
|
* </x:xmpmeta>`;
|
||||||
|
*
|
||||||
|
* const data = await sharp(input)
|
||||||
|
* .withXmp(xmpString)
|
||||||
|
* .toBuffer();
|
||||||
|
*
|
||||||
|
* @param {string} xmp String containing XMP metadata to be embedded in the output image.
|
||||||
|
* @returns {Sharp}
|
||||||
|
* @throws {Error} Invalid parameters
|
||||||
|
*/
|
||||||
|
function withXmp (xmp) {
|
||||||
|
if (is.string(xmp) && xmp.length > 0) {
|
||||||
|
this.options.withXmp = xmp;
|
||||||
|
this.options.keepMetadata |= 0b00010;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('xmp', 'non-empty string', xmp);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep all metadata (EXIF, ICC, XMP, IPTC) from the input image in the output image.
|
* Keep all metadata (EXIF, ICC, XMP, IPTC) from the input image in the output image.
|
||||||
*
|
*
|
||||||
@ -729,6 +782,7 @@ function webp (options) {
|
|||||||
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
|
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
|
||||||
* @param {number} [options.interFrameMaxError=0] - maximum inter-frame error for transparency, between 0 (lossless) and 32
|
* @param {number} [options.interFrameMaxError=0] - maximum inter-frame error for transparency, between 0 (lossless) and 32
|
||||||
* @param {number} [options.interPaletteMaxError=3] - maximum inter-palette error for palette reuse, between 0 and 256
|
* @param {number} [options.interPaletteMaxError=3] - maximum inter-palette error for palette reuse, between 0 and 256
|
||||||
|
* @param {boolean} [options.keepDuplicateFrames=false] - keep duplicate frames in the output instead of combining them
|
||||||
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
||||||
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
||||||
* @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
|
* @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
|
||||||
@ -779,6 +833,13 @@ function gif (options) {
|
|||||||
throw is.invalidParameterError('interPaletteMaxError', 'number between 0.0 and 256.0', options.interPaletteMaxError);
|
throw is.invalidParameterError('interPaletteMaxError', 'number between 0.0 and 256.0', options.interPaletteMaxError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is.defined(options.keepDuplicateFrames)) {
|
||||||
|
if (is.bool(options.keepDuplicateFrames)) {
|
||||||
|
this._setBooleanOption('gifKeepDuplicateFrames', options.keepDuplicateFrames);
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('keepDuplicateFrames', 'boolean', options.keepDuplicateFrames);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
trySetAnimationOptions(options, this.options);
|
trySetAnimationOptions(options, this.options);
|
||||||
return this._updateFormatOut('gif', options);
|
return this._updateFormatOut('gif', options);
|
||||||
@ -1019,6 +1080,9 @@ function tiff (options) {
|
|||||||
* AVIF image sequences are not supported.
|
* AVIF image sequences are not supported.
|
||||||
* Prebuilt binaries support a bitdepth of 8 only.
|
* Prebuilt binaries support a bitdepth of 8 only.
|
||||||
*
|
*
|
||||||
|
* This feature is experimental on the Windows ARM64 platform
|
||||||
|
* and requires a CPU with ARM64v8.4 or later.
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* const data = await sharp(input)
|
* const data = await sharp(input)
|
||||||
* .avif({ effort: 2 })
|
* .avif({ effort: 2 })
|
||||||
@ -1565,6 +1629,8 @@ module.exports = function (Sharp) {
|
|||||||
withExifMerge,
|
withExifMerge,
|
||||||
keepIccProfile,
|
keepIccProfile,
|
||||||
withIccProfile,
|
withIccProfile,
|
||||||
|
keepXmp,
|
||||||
|
withXmp,
|
||||||
keepMetadata,
|
keepMetadata,
|
||||||
withMetadata,
|
withMetadata,
|
||||||
toFormat,
|
toFormat,
|
||||||
|
@ -129,7 +129,7 @@ function isResizeExpected (options) {
|
|||||||
*
|
*
|
||||||
* Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
|
* Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
|
||||||
*
|
*
|
||||||
* <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.svg">
|
* <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="/api-resize-fit.svg">
|
||||||
*
|
*
|
||||||
* When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
* When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
|
||||||
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
|
||||||
@ -150,6 +150,8 @@ function isResizeExpected (options) {
|
|||||||
* - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
|
* - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
|
||||||
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
|
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
|
||||||
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
|
||||||
|
* - `mks2013`: Use a [Magic Kernel Sharp](https://johncostella.com/magic/mks.pdf) 2013 kernel, as adopted by Facebook.
|
||||||
|
* - `mks2021`: Use a Magic Kernel Sharp 2021 kernel, with more accurate (reduced) sharpening than the 2013 version.
|
||||||
*
|
*
|
||||||
* When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
|
* When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
|
||||||
* Downsampling kernels without a matching upsampling interpolator map to `cubic`.
|
* Downsampling kernels without a matching upsampling interpolator map to `cubic`.
|
||||||
|
@ -135,15 +135,9 @@ cache(true);
|
|||||||
* e.g. libaom manages its own 4 threads when encoding AVIF images,
|
* e.g. libaom manages its own 4 threads when encoding AVIF images,
|
||||||
* and these are independent of the value set here.
|
* and these are independent of the value set here.
|
||||||
*
|
*
|
||||||
* The maximum number of images that sharp can process in parallel
|
* :::note
|
||||||
* is controlled by libuv's `UV_THREADPOOL_SIZE` environment variable,
|
* Further {@link /performance|control over performance} is available.
|
||||||
* which defaults to 4.
|
* :::
|
||||||
*
|
|
||||||
* https://nodejs.org/api/cli.html#uv_threadpool_sizesize
|
|
||||||
*
|
|
||||||
* For example, by default, a machine with 8 CPU cores will process
|
|
||||||
* 4 images in parallel and use up to 8 threads per image,
|
|
||||||
* so there will be up to 32 concurrent threads.
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const threads = sharp.concurrency(); // 4
|
* const threads = sharp.concurrency(); // 4
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-darwin-arm64",
|
"name": "@img/sharp-darwin-arm64",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with macOS 64-bit ARM",
|
"description": "Prebuilt sharp for use with macOS 64-bit ARM",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-darwin-arm64": "1.1.0"
|
"@img/sharp-libvips-darwin-arm64": "1.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-darwin-x64",
|
"name": "@img/sharp-darwin-x64",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with macOS x64",
|
"description": "Prebuilt sharp for use with macOS x64",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-darwin-x64": "1.1.0"
|
"@img/sharp-libvips-darwin-x64": "1.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// Populate contents of all packages with the current GitHub release
|
|
||||||
|
|
||||||
const { readFile, writeFile, appendFile, copyFile, rm } = require('node:fs/promises');
|
|
||||||
const path = require('node:path');
|
|
||||||
const { Readable } = require('node:stream');
|
|
||||||
const { pipeline } = require('node:stream/promises');
|
|
||||||
const { createGunzip } = require('node:zlib');
|
|
||||||
const { extract } = require('tar-fs');
|
|
||||||
|
|
||||||
const { workspaces } = require('./package.json');
|
|
||||||
const { version } = require('../package.json');
|
|
||||||
|
|
||||||
const mapTarballEntry = (header) => {
|
|
||||||
header.name = path.basename(header.name);
|
|
||||||
return header;
|
|
||||||
};
|
|
||||||
|
|
||||||
const licensing = `
|
|
||||||
## Licensing
|
|
||||||
|
|
||||||
Copyright 2013 Lovell Fuller and others.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
`;
|
|
||||||
|
|
||||||
workspaces.map(async platform => {
|
|
||||||
const prebuildPlatform = platform === 'wasm32' ? 'emscripten-wasm32' : platform;
|
|
||||||
const url = `https://github.com/lovell/sharp/releases/download/v${version}/sharp-v${version}-napi-v9-${prebuildPlatform}.tar.gz`;
|
|
||||||
const dir = path.join(__dirname, platform);
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (!response.ok) {
|
|
||||||
console.log(`Skipping ${platform}: ${response.statusText}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Extract prebuild tarball
|
|
||||||
const lib = path.join(dir, 'lib');
|
|
||||||
await rm(lib, { force: true, recursive: true });
|
|
||||||
await pipeline(
|
|
||||||
Readable.fromWeb(response.body),
|
|
||||||
createGunzip(),
|
|
||||||
extract(lib, { map: mapTarballEntry })
|
|
||||||
);
|
|
||||||
// Generate README
|
|
||||||
const { name, description } = require(`./${platform}/package.json`);
|
|
||||||
await writeFile(path.join(dir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`);
|
|
||||||
// Copy Apache-2.0 LICENSE
|
|
||||||
await copyFile(path.join(__dirname, '..', 'LICENSE'), path.join(dir, 'LICENSE'));
|
|
||||||
// Copy files for packages without an explicit sharp-libvips dependency (Windows, wasm)
|
|
||||||
if (platform.startsWith('win') || platform.startsWith('wasm')) {
|
|
||||||
const libvipsPlatform = platform === 'wasm32' ? 'dev-wasm32' : platform;
|
|
||||||
const sharpLibvipsDir = path.join(require(`@img/sharp-libvips-${libvipsPlatform}/lib`), '..');
|
|
||||||
// Copy versions.json
|
|
||||||
await copyFile(path.join(sharpLibvipsDir, 'versions.json'), path.join(dir, 'versions.json'));
|
|
||||||
// Append third party licensing to README
|
|
||||||
const readme = await readFile(path.join(sharpLibvipsDir, 'README.md'), { encoding: 'utf-8' });
|
|
||||||
const thirdParty = readme.substring(readme.indexOf('\nThis software contains'));
|
|
||||||
appendFile(path.join(dir, 'README.md'), thirdParty);
|
|
||||||
}
|
|
||||||
});
|
|
@ -3,24 +3,62 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Populate contents of a single npm/sharpen-sharp-<build-platform> package
|
// Populate the npm package for the current platform with the local build
|
||||||
// with the local/CI build directory for local/CI prebuild testing
|
|
||||||
|
|
||||||
const fs = require('node:fs');
|
const { copyFileSync, cpSync, readFileSync, writeFileSync, appendFileSync } = require('node:fs');
|
||||||
const path = require('node:path');
|
const { basename, join } = require('node:path');
|
||||||
|
|
||||||
const { buildPlatformArch } = require('../lib/libvips');
|
const { buildPlatformArch } = require('../lib/libvips');
|
||||||
const platform = buildPlatformArch();
|
|
||||||
const dest = path.join(__dirname, platform);
|
|
||||||
|
|
||||||
// Use same config as prebuild to copy binary files
|
const licensing = `
|
||||||
const release = path.join(__dirname, '..', 'src', 'build', 'Release');
|
## Licensing
|
||||||
const prebuildrc = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '.prebuildrc'), 'utf8'));
|
|
||||||
const include = new RegExp(prebuildrc['include-regex'], 'i');
|
Copyright 2013 Lovell Fuller and others.
|
||||||
fs.cpSync(release, path.join(dest, 'lib'), {
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
`;
|
||||||
|
|
||||||
|
const platform = buildPlatformArch();
|
||||||
|
const destDir = join(__dirname, platform);
|
||||||
|
console.log(`Populating npm package for platform: ${platform}`);
|
||||||
|
|
||||||
|
// Copy binaries
|
||||||
|
const releaseDir = join(__dirname, '..', 'src', 'build', 'Release');
|
||||||
|
const libDir = join(destDir, 'lib');
|
||||||
|
cpSync(releaseDir, libDir, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
filter: (file) => {
|
filter: (file) => {
|
||||||
const name = path.basename(file);
|
const name = basename(file);
|
||||||
return name === 'Release' || include.test(name);
|
return name === 'Release' ||
|
||||||
|
(name.startsWith('sharp-') && name.includes('.node')) ||
|
||||||
|
(name.startsWith('libvips-') && name.endsWith('.dll'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Generate README
|
||||||
|
const { name, description } = require(`./${platform}/package.json`);
|
||||||
|
writeFileSync(join(destDir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`);
|
||||||
|
|
||||||
|
// Copy Apache-2.0 LICENSE
|
||||||
|
copyFileSync(join(__dirname, '..', 'LICENSE'), join(destDir, 'LICENSE'));
|
||||||
|
|
||||||
|
// Copy files for packages without an explicit sharp-libvips dependency (Windows, wasm)
|
||||||
|
if (platform.startsWith('win') || platform.startsWith('wasm')) {
|
||||||
|
const libvipsPlatform = platform === 'wasm32' ? 'dev-wasm32' : platform;
|
||||||
|
const sharpLibvipsDir = join(require(`@img/sharp-libvips-${libvipsPlatform}/lib`), '..');
|
||||||
|
// Copy versions.json
|
||||||
|
copyFileSync(join(sharpLibvipsDir, 'versions.json'), join(destDir, 'versions.json'));
|
||||||
|
// Append third party licensing to README
|
||||||
|
const readme = readFileSync(join(sharpLibvipsDir, 'README.md'), { encoding: 'utf-8' });
|
||||||
|
const thirdParty = readme.substring(readme.indexOf('\nThis software contains'));
|
||||||
|
appendFileSync(join(destDir, 'README.md'), thirdParty);
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-linux-arm",
|
"name": "@img/sharp-linux-arm",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
|
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linux-arm": "1.1.0"
|
"@img/sharp-libvips-linux-arm": "1.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-linux-arm64",
|
"name": "@img/sharp-linux-arm64",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with Linux (glibc) 64-bit ARM",
|
"description": "Prebuilt sharp for use with Linux (glibc) 64-bit ARM",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linux-arm64": "1.1.0"
|
"@img/sharp-libvips-linux-arm64": "1.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-linux-ppc64",
|
"name": "@img/sharp-linux-ppc64",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with Linux (glibc) ppc64",
|
"description": "Prebuilt sharp for use with Linux (glibc) ppc64",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linux-ppc64": "1.1.0"
|
"@img/sharp-libvips-linux-ppc64": "1.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-linux-s390x",
|
"name": "@img/sharp-linux-s390x",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with Linux (glibc) s390x",
|
"description": "Prebuilt sharp for use with Linux (glibc) s390x",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linux-s390x": "1.1.0"
|
"@img/sharp-libvips-linux-s390x": "1.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-linux-x64",
|
"name": "@img/sharp-linux-x64",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with Linux (glibc) x64",
|
"description": "Prebuilt sharp for use with Linux (glibc) x64",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linux-x64": "1.1.0"
|
"@img/sharp-libvips-linux-x64": "1.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-linuxmusl-arm64",
|
"name": "@img/sharp-linuxmusl-arm64",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with Linux (musl) 64-bit ARM",
|
"description": "Prebuilt sharp for use with Linux (musl) 64-bit ARM",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linuxmusl-arm64": "1.1.0"
|
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-linuxmusl-x64",
|
"name": "@img/sharp-linuxmusl-x64",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with Linux (musl) x64",
|
"description": "Prebuilt sharp for use with Linux (musl) x64",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"preferUnplugged": true,
|
"preferUnplugged": true,
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-libvips-linuxmusl-x64": "1.1.0"
|
"@img/sharp-libvips-linuxmusl-x64": "1.2.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp",
|
"name": "@img/sharp",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"darwin-arm64",
|
"darwin-arm64",
|
||||||
@ -13,6 +13,7 @@
|
|||||||
"linuxmusl-arm64",
|
"linuxmusl-arm64",
|
||||||
"linuxmusl-x64",
|
"linuxmusl-x64",
|
||||||
"wasm32",
|
"wasm32",
|
||||||
|
"win32-arm64",
|
||||||
"win32-ia32",
|
"win32-ia32",
|
||||||
"win32-x64"
|
"win32-x64"
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-wasm32",
|
"name": "@img/sharp-wasm32",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with wasm32",
|
"description": "Prebuilt sharp for use with wasm32",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emnapi/runtime": "^1.4.0"
|
"@emnapi/runtime": "^1.4.4"
|
||||||
},
|
},
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"wasm32"
|
"wasm32"
|
||||||
|
39
npm/win32-arm64/package.json
Normal file
39
npm/win32-arm64/package.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "@img/sharp-win32-arm64",
|
||||||
|
"version": "0.34.3-rc.0",
|
||||||
|
"description": "Prebuilt sharp for use with Windows 64-bit ARM",
|
||||||
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/win32-arm64"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"versions.json"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-win32-arm64.node",
|
||||||
|
"./package": "./package.json",
|
||||||
|
"./versions": "./versions.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-win32-ia32",
|
"name": "@img/sharp-win32-ia32",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with Windows x86 (32-bit)",
|
"description": "Prebuilt sharp for use with Windows x86 (32-bit)",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@img/sharp-win32-x64",
|
"name": "@img/sharp-win32-x64",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"description": "Prebuilt sharp for use with Windows x64",
|
"description": "Prebuilt sharp for use with Windows x64",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
83
package.json
83
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
||||||
"version": "0.34.1",
|
"version": "0.34.3-rc.0",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://sharp.pixelplumbing.com",
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@ -92,7 +92,7 @@
|
|||||||
"Don Denton <don@happycollision.com>"
|
"Don Denton <don@happycollision.com>"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "node install/check",
|
"install": "node install/check.js",
|
||||||
"clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*",
|
"clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*",
|
||||||
"test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types",
|
"test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types",
|
||||||
"test-lint": "semistandard && cpplint",
|
"test-lint": "semistandard && cpplint",
|
||||||
@ -100,8 +100,7 @@
|
|||||||
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"",
|
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"",
|
||||||
"test-leak": "./test/leak/leak.sh",
|
"test-leak": "./test/leak/leak.sh",
|
||||||
"test-types": "tsd",
|
"test-types": "tsd",
|
||||||
"package-from-local-build": "node npm/from-local-build",
|
"package-from-local-build": "node npm/from-local-build.js",
|
||||||
"package-from-github-release": "node npm/from-github-release",
|
|
||||||
"docs-build": "node docs/build.mjs",
|
"docs-build": "node docs/build.mjs",
|
||||||
"docs-serve": "cd docs && npm start",
|
"docs-serve": "cd docs && npm start",
|
||||||
"docs-publish": "cd docs && npm run build && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
|
"docs-publish": "cd docs && npm run build && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
|
||||||
@ -138,68 +137,66 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"detect-libc": "^2.0.3",
|
"detect-libc": "^2.0.4",
|
||||||
"semver": "^7.7.1"
|
"semver": "^7.7.2"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@img/sharp-darwin-arm64": "0.34.1",
|
"@img/sharp-darwin-arm64": "0.34.3-rc.0",
|
||||||
"@img/sharp-darwin-x64": "0.34.1",
|
"@img/sharp-darwin-x64": "0.34.3-rc.0",
|
||||||
"@img/sharp-libvips-darwin-arm64": "1.1.0",
|
"@img/sharp-libvips-darwin-arm64": "1.2.0",
|
||||||
"@img/sharp-libvips-darwin-x64": "1.1.0",
|
"@img/sharp-libvips-darwin-x64": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-arm": "1.1.0",
|
"@img/sharp-libvips-linux-arm": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-arm64": "1.1.0",
|
"@img/sharp-libvips-linux-arm64": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-ppc64": "1.1.0",
|
"@img/sharp-libvips-linux-ppc64": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-s390x": "1.1.0",
|
"@img/sharp-libvips-linux-s390x": "1.2.0",
|
||||||
"@img/sharp-libvips-linux-x64": "1.1.0",
|
"@img/sharp-libvips-linux-x64": "1.2.0",
|
||||||
"@img/sharp-libvips-linuxmusl-arm64": "1.1.0",
|
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
|
||||||
"@img/sharp-libvips-linuxmusl-x64": "1.1.0",
|
"@img/sharp-libvips-linuxmusl-x64": "1.2.0",
|
||||||
"@img/sharp-linux-arm": "0.34.1",
|
"@img/sharp-linux-arm": "0.34.3-rc.0",
|
||||||
"@img/sharp-linux-arm64": "0.34.1",
|
"@img/sharp-linux-arm64": "0.34.3-rc.0",
|
||||||
"@img/sharp-linux-s390x": "0.34.1",
|
"@img/sharp-linux-ppc64": "0.34.3-rc.0",
|
||||||
"@img/sharp-linux-x64": "0.34.1",
|
"@img/sharp-linux-s390x": "0.34.3-rc.0",
|
||||||
"@img/sharp-linuxmusl-arm64": "0.34.1",
|
"@img/sharp-linux-x64": "0.34.3-rc.0",
|
||||||
"@img/sharp-linuxmusl-x64": "0.34.1",
|
"@img/sharp-linuxmusl-arm64": "0.34.3-rc.0",
|
||||||
"@img/sharp-wasm32": "0.34.1",
|
"@img/sharp-linuxmusl-x64": "0.34.3-rc.0",
|
||||||
"@img/sharp-win32-ia32": "0.34.1",
|
"@img/sharp-wasm32": "0.34.3-rc.0",
|
||||||
"@img/sharp-win32-x64": "0.34.1"
|
"@img/sharp-win32-arm64": "0.34.3-rc.0",
|
||||||
|
"@img/sharp-win32-ia32": "0.34.3-rc.0",
|
||||||
|
"@img/sharp-win32-x64": "0.34.3-rc.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@emnapi/runtime": "^1.4.0",
|
"@emnapi/runtime": "^1.4.4",
|
||||||
"@img/sharp-libvips-dev": "1.1.0",
|
"@img/sharp-libvips-dev": "1.2.0",
|
||||||
"@img/sharp-libvips-dev-wasm32": "1.1.0",
|
"@img/sharp-libvips-dev-wasm32": "1.2.0",
|
||||||
"@img/sharp-libvips-win32-ia32": "1.1.0",
|
"@img/sharp-libvips-win32-arm64": "1.2.0",
|
||||||
"@img/sharp-libvips-win32-x64": "1.1.0",
|
"@img/sharp-libvips-win32-ia32": "1.2.0",
|
||||||
|
"@img/sharp-libvips-win32-x64": "1.2.0",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"cc": "^3.0.1",
|
"cc": "^3.0.1",
|
||||||
"emnapi": "^1.4.0",
|
"emnapi": "^1.4.4",
|
||||||
"exif-reader": "^2.0.2",
|
"exif-reader": "^2.0.2",
|
||||||
"extract-zip": "^2.0.1",
|
"extract-zip": "^2.0.1",
|
||||||
"icc": "^3.0.0",
|
"icc": "^3.0.0",
|
||||||
"jsdoc-to-markdown": "^9.1.1",
|
"jsdoc-to-markdown": "^9.1.1",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^11.1.0",
|
"mocha": "^11.7.1",
|
||||||
"node-addon-api": "^8.3.1",
|
"node-addon-api": "^8.4.0",
|
||||||
|
"node-gyp": "^11.2.0",
|
||||||
"nyc": "^17.1.0",
|
"nyc": "^17.1.0",
|
||||||
"prebuild": "^13.0.1",
|
|
||||||
"semistandard": "^17.0.0",
|
"semistandard": "^17.0.0",
|
||||||
"tar-fs": "^3.0.8",
|
"tar-fs": "^3.1.0",
|
||||||
"tsd": "^0.31.2"
|
"tsd": "^0.32.0"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"libvips": ">=8.16.1"
|
"libvips": ">=8.17.1"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"binary": {
|
|
||||||
"napi_versions": [
|
|
||||||
9
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"semistandard": {
|
"semistandard": {
|
||||||
"env": [
|
"env": [
|
||||||
"mocha"
|
"mocha"
|
||||||
|
@ -163,6 +163,8 @@
|
|||||||
},
|
},
|
||||||
'xcode_settings': {
|
'xcode_settings': {
|
||||||
'OTHER_LDFLAGS': [
|
'OTHER_LDFLAGS': [
|
||||||
|
'-Wl,-s',
|
||||||
|
'-Wl,-dead_strip',
|
||||||
# Ensure runtime linking is relative to sharp.node
|
# Ensure runtime linking is relative to sharp.node
|
||||||
'-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'',
|
'-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'',
|
||||||
'-Wl,-rpath,\'@loader_path/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'',
|
'-Wl,-rpath,\'@loader_path/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'',
|
||||||
@ -176,6 +178,9 @@
|
|||||||
'defines': [
|
'defines': [
|
||||||
'_GLIBCXX_USE_CXX11_ABI=1'
|
'_GLIBCXX_USE_CXX11_ABI=1'
|
||||||
],
|
],
|
||||||
|
'cflags_cc': [
|
||||||
|
'<!(node -p "require(\'detect-libc\').isNonGlibcLinuxSync() ? \'\' : \'-flto=auto\'")'
|
||||||
|
],
|
||||||
'link_settings': {
|
'link_settings': {
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'-l:libvips-cpp.so.<(vips_version)'
|
'-l:libvips-cpp.so.<(vips_version)'
|
||||||
|
165
src/common.cc
165
src/common.cc
@ -93,6 +93,7 @@ namespace sharp {
|
|||||||
descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
|
descriptor->rawWidth = AttrAsUint32(input, "rawWidth");
|
||||||
descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
|
descriptor->rawHeight = AttrAsUint32(input, "rawHeight");
|
||||||
descriptor->rawPremultiplied = AttrAsBool(input, "rawPremultiplied");
|
descriptor->rawPremultiplied = AttrAsBool(input, "rawPremultiplied");
|
||||||
|
descriptor->rawPageHeight = AttrAsUint32(input, "rawPageHeight");
|
||||||
}
|
}
|
||||||
// Multi-page input (GIF, TIFF, PDF)
|
// Multi-page input (GIF, TIFF, PDF)
|
||||||
if (HasAttr(input, "pages")) {
|
if (HasAttr(input, "pages")) {
|
||||||
@ -101,23 +102,35 @@ namespace sharp {
|
|||||||
if (HasAttr(input, "page")) {
|
if (HasAttr(input, "page")) {
|
||||||
descriptor->page = AttrAsUint32(input, "page");
|
descriptor->page = AttrAsUint32(input, "page");
|
||||||
}
|
}
|
||||||
|
// SVG
|
||||||
|
if (HasAttr(input, "svgStylesheet")) {
|
||||||
|
descriptor->svgStylesheet = AttrAsStr(input, "svgStylesheet");
|
||||||
|
}
|
||||||
|
if (HasAttr(input, "svgHighBitdepth")) {
|
||||||
|
descriptor->svgHighBitdepth = AttrAsBool(input, "svgHighBitdepth");
|
||||||
|
}
|
||||||
// Multi-level input (OpenSlide)
|
// Multi-level input (OpenSlide)
|
||||||
if (HasAttr(input, "level")) {
|
if (HasAttr(input, "openSlideLevel")) {
|
||||||
descriptor->level = AttrAsUint32(input, "level");
|
descriptor->openSlideLevel = AttrAsUint32(input, "openSlideLevel");
|
||||||
}
|
}
|
||||||
// subIFD (OME-TIFF)
|
// subIFD (OME-TIFF)
|
||||||
if (HasAttr(input, "subifd")) {
|
if (HasAttr(input, "subifd")) {
|
||||||
descriptor->subifd = AttrAsInt32(input, "subifd");
|
descriptor->tiffSubifd = AttrAsInt32(input, "tiffSubifd");
|
||||||
}
|
}
|
||||||
// // PDF background color
|
// // PDF background color
|
||||||
if (HasAttr(input, "pdfBackground")) {
|
if (HasAttr(input, "pdfBackground")) {
|
||||||
descriptor->pdfBackground = AttrAsVectorOfDouble(input, "pdfBackground");
|
descriptor->pdfBackground = AttrAsVectorOfDouble(input, "pdfBackground");
|
||||||
}
|
}
|
||||||
|
// Use JPEG 2000 oneshot mode?
|
||||||
|
if (HasAttr(input, "jp2Oneshot")) {
|
||||||
|
descriptor->jp2Oneshot = AttrAsBool(input, "jp2Oneshot");
|
||||||
|
}
|
||||||
// Create new image
|
// Create new image
|
||||||
if (HasAttr(input, "createChannels")) {
|
if (HasAttr(input, "createChannels")) {
|
||||||
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
||||||
descriptor->createWidth = AttrAsUint32(input, "createWidth");
|
descriptor->createWidth = AttrAsUint32(input, "createWidth");
|
||||||
descriptor->createHeight = AttrAsUint32(input, "createHeight");
|
descriptor->createHeight = AttrAsUint32(input, "createHeight");
|
||||||
|
descriptor->createPageHeight = AttrAsUint32(input, "createPageHeight");
|
||||||
if (HasAttr(input, "createNoiseType")) {
|
if (HasAttr(input, "createNoiseType")) {
|
||||||
descriptor->createNoiseType = AttrAsStr(input, "createNoiseType");
|
descriptor->createNoiseType = AttrAsStr(input, "createNoiseType");
|
||||||
descriptor->createNoiseMean = AttrAsDouble(input, "createNoiseMean");
|
descriptor->createNoiseMean = AttrAsDouble(input, "createNoiseMean");
|
||||||
@ -271,6 +284,7 @@ namespace sharp {
|
|||||||
case ImageType::EXR: id = "exr"; break;
|
case ImageType::EXR: id = "exr"; break;
|
||||||
case ImageType::JXL: id = "jxl"; break;
|
case ImageType::JXL: id = "jxl"; break;
|
||||||
case ImageType::RAD: id = "rad"; break;
|
case ImageType::RAD: id = "rad"; break;
|
||||||
|
case ImageType::DCRAW: id = "dcraw"; break;
|
||||||
case ImageType::VIPS: id = "vips"; break;
|
case ImageType::VIPS: id = "vips"; break;
|
||||||
case ImageType::RAW: id = "raw"; break;
|
case ImageType::RAW: id = "raw"; break;
|
||||||
case ImageType::UNKNOWN: id = "unknown"; break;
|
case ImageType::UNKNOWN: id = "unknown"; break;
|
||||||
@ -319,6 +333,8 @@ namespace sharp {
|
|||||||
{ "VipsForeignLoadJxlBuffer", ImageType::JXL },
|
{ "VipsForeignLoadJxlBuffer", ImageType::JXL },
|
||||||
{ "VipsForeignLoadRadFile", ImageType::RAD },
|
{ "VipsForeignLoadRadFile", ImageType::RAD },
|
||||||
{ "VipsForeignLoadRadBuffer", ImageType::RAD },
|
{ "VipsForeignLoadRadBuffer", ImageType::RAD },
|
||||||
|
{ "VipsForeignLoadDcRawFile", ImageType::DCRAW },
|
||||||
|
{ "VipsForeignLoadDcRawBuffer", ImageType::DCRAW },
|
||||||
{ "VipsForeignLoadVips", ImageType::VIPS },
|
{ "VipsForeignLoadVips", ImageType::VIPS },
|
||||||
{ "VipsForeignLoadVipsFile", ImageType::VIPS },
|
{ "VipsForeignLoadVipsFile", ImageType::VIPS },
|
||||||
{ "VipsForeignLoadRaw", ImageType::RAW }
|
{ "VipsForeignLoadRaw", ImageType::RAW }
|
||||||
@ -383,6 +399,48 @@ namespace sharp {
|
|||||||
imageType == ImageType::HEIF;
|
imageType == ImageType::HEIF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Format-specific options builder
|
||||||
|
*/
|
||||||
|
vips::VOption* GetOptionsForImageType(ImageType imageType, InputDescriptor *descriptor) {
|
||||||
|
vips::VOption *option = VImage::option()
|
||||||
|
->set("access", descriptor->access)
|
||||||
|
->set("fail_on", descriptor->failOn);
|
||||||
|
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
|
||||||
|
option->set("unlimited", true);
|
||||||
|
}
|
||||||
|
if (ImageTypeSupportsPage(imageType)) {
|
||||||
|
option->set("n", descriptor->pages);
|
||||||
|
option->set("page", descriptor->page);
|
||||||
|
}
|
||||||
|
switch (imageType) {
|
||||||
|
case ImageType::SVG:
|
||||||
|
option->set("dpi", descriptor->density)
|
||||||
|
->set("stylesheet", descriptor->svgStylesheet.data())
|
||||||
|
->set("high_bitdepth", descriptor->svgHighBitdepth);
|
||||||
|
break;
|
||||||
|
case ImageType::TIFF:
|
||||||
|
option->set("tiffSubifd", descriptor->tiffSubifd);
|
||||||
|
break;
|
||||||
|
case ImageType::PDF:
|
||||||
|
option->set("dpi", descriptor->density)
|
||||||
|
->set("background", descriptor->pdfBackground);
|
||||||
|
break;
|
||||||
|
case ImageType::OPENSLIDE:
|
||||||
|
option->set("openSlideLevel", descriptor->openSlideLevel);
|
||||||
|
break;
|
||||||
|
case ImageType::JP2:
|
||||||
|
option->set("oneshot", descriptor->jp2Oneshot);
|
||||||
|
break;
|
||||||
|
case ImageType::MAGICK:
|
||||||
|
option->set("density", std::to_string(descriptor->density).data());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
|
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
|
||||||
*/
|
*/
|
||||||
@ -400,6 +458,10 @@ namespace sharp {
|
|||||||
} else {
|
} else {
|
||||||
image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16;
|
image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16;
|
||||||
}
|
}
|
||||||
|
if (descriptor->rawPageHeight > 0) {
|
||||||
|
image.set(VIPS_META_PAGE_HEIGHT, descriptor->rawPageHeight);
|
||||||
|
image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->rawHeight / descriptor->rawPageHeight));
|
||||||
|
}
|
||||||
if (descriptor->rawPremultiplied) {
|
if (descriptor->rawPremultiplied) {
|
||||||
image = image.unpremultiply();
|
image = image.unpremultiply();
|
||||||
}
|
}
|
||||||
@ -409,31 +471,7 @@ namespace sharp {
|
|||||||
imageType = DetermineImageType(descriptor->buffer, descriptor->bufferLength);
|
imageType = DetermineImageType(descriptor->buffer, descriptor->bufferLength);
|
||||||
if (imageType != ImageType::UNKNOWN) {
|
if (imageType != ImageType::UNKNOWN) {
|
||||||
try {
|
try {
|
||||||
vips::VOption *option = VImage::option()
|
vips::VOption *option = GetOptionsForImageType(imageType, descriptor);
|
||||||
->set("access", descriptor->access)
|
|
||||||
->set("fail_on", descriptor->failOn);
|
|
||||||
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
|
|
||||||
option->set("unlimited", true);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
|
|
||||||
option->set("dpi", descriptor->density);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::MAGICK) {
|
|
||||||
option->set("density", std::to_string(descriptor->density).data());
|
|
||||||
}
|
|
||||||
if (ImageTypeSupportsPage(imageType)) {
|
|
||||||
option->set("n", descriptor->pages);
|
|
||||||
option->set("page", descriptor->page);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::OPENSLIDE) {
|
|
||||||
option->set("level", descriptor->level);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::TIFF) {
|
|
||||||
option->set("subifd", descriptor->subifd);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::PDF) {
|
|
||||||
option->set("background", descriptor->pdfBackground);
|
|
||||||
}
|
|
||||||
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
image = SetDensity(image, descriptor->density);
|
image = SetDensity(image, descriptor->density);
|
||||||
@ -473,6 +511,10 @@ namespace sharp {
|
|||||||
channels < 3 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB))
|
channels < 3 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB))
|
||||||
.new_from_image(background);
|
.new_from_image(background);
|
||||||
}
|
}
|
||||||
|
if (descriptor->createPageHeight > 0) {
|
||||||
|
image.set(VIPS_META_PAGE_HEIGHT, descriptor->createPageHeight);
|
||||||
|
image.set(VIPS_META_N_PAGES, static_cast<int>(descriptor->createHeight / descriptor->createPageHeight));
|
||||||
|
}
|
||||||
image = image.cast(VIPS_FORMAT_UCHAR);
|
image = image.cast(VIPS_FORMAT_UCHAR);
|
||||||
imageType = ImageType::RAW;
|
imageType = ImageType::RAW;
|
||||||
} else if (descriptor->textValue.length() > 0) {
|
} else if (descriptor->textValue.length() > 0) {
|
||||||
@ -516,31 +558,7 @@ namespace sharp {
|
|||||||
}
|
}
|
||||||
if (imageType != ImageType::UNKNOWN) {
|
if (imageType != ImageType::UNKNOWN) {
|
||||||
try {
|
try {
|
||||||
vips::VOption *option = VImage::option()
|
vips::VOption *option = GetOptionsForImageType(imageType, descriptor);
|
||||||
->set("access", descriptor->access)
|
|
||||||
->set("fail_on", descriptor->failOn);
|
|
||||||
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
|
|
||||||
option->set("unlimited", true);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
|
|
||||||
option->set("dpi", descriptor->density);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::MAGICK) {
|
|
||||||
option->set("density", std::to_string(descriptor->density).data());
|
|
||||||
}
|
|
||||||
if (ImageTypeSupportsPage(imageType)) {
|
|
||||||
option->set("n", descriptor->pages);
|
|
||||||
option->set("page", descriptor->page);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::OPENSLIDE) {
|
|
||||||
option->set("level", descriptor->level);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::TIFF) {
|
|
||||||
option->set("subifd", descriptor->subifd);
|
|
||||||
}
|
|
||||||
if (imageType == ImageType::PDF) {
|
|
||||||
option->set("background", descriptor->pdfBackground);
|
|
||||||
}
|
|
||||||
image = VImage::new_from_file(descriptor->file.data(), option);
|
image = VImage::new_from_file(descriptor->file.data(), option);
|
||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
image = SetDensity(image, descriptor->density);
|
image = SetDensity(image, descriptor->density);
|
||||||
@ -651,22 +669,21 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop) {
|
VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop) {
|
||||||
bool hasDelay = !delay.empty();
|
bool hasDelay = !delay.empty();
|
||||||
|
|
||||||
// Avoid a copy if none of the animation properties are needed.
|
|
||||||
if (nPages == 1 && !hasDelay && loop == -1) return image;
|
|
||||||
|
|
||||||
if (delay.size() == 1) {
|
|
||||||
// We have just one delay, repeat that value for all frames.
|
|
||||||
delay.insert(delay.end(), nPages - 1, delay[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attaching metadata, need to copy the image.
|
|
||||||
VImage copy = image.copy();
|
VImage copy = image.copy();
|
||||||
|
|
||||||
// Only set page-height if we have more than one page, or this could
|
// Only set page-height if we have more than one page, or this could
|
||||||
// accidentally turn into an animated image later.
|
// accidentally turn into an animated image later.
|
||||||
if (nPages > 1) copy.set(VIPS_META_PAGE_HEIGHT, pageHeight);
|
if (nPages > 1) copy.set(VIPS_META_PAGE_HEIGHT, pageHeight);
|
||||||
if (hasDelay) copy.set("delay", delay);
|
if (hasDelay) {
|
||||||
|
if (delay.size() == 1) {
|
||||||
|
// We have just one delay, repeat that value for all frames.
|
||||||
|
delay.insert(delay.end(), nPages - 1, delay[0]);
|
||||||
|
}
|
||||||
|
copy.set("delay", delay);
|
||||||
|
}
|
||||||
|
if (nPages == 1 && !hasDelay && loop == -1) {
|
||||||
|
loop = 1;
|
||||||
|
}
|
||||||
if (loop != -1) copy.set("loop", loop);
|
if (loop != -1) copy.set("loop", loop);
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
@ -952,14 +969,6 @@ namespace sharp {
|
|||||||
return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
|
return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Return the image alpha maximum. Useful for combining alpha bands. scRGB
|
|
||||||
images are 0 - 1 for image data, but the alpha is 0 - 255.
|
|
||||||
*/
|
|
||||||
double MaximumImageAlpha(VipsInterpretation const interpretation) {
|
|
||||||
return Is16Bit(interpretation) ? 65535.0 : 255.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Convert RGBA value to another colourspace
|
Convert RGBA value to another colourspace
|
||||||
*/
|
*/
|
||||||
@ -1002,16 +1011,16 @@ namespace sharp {
|
|||||||
0.0722 * colour[2])
|
0.0722 * colour[2])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Add alpha channel to alphaColour colour
|
// Add alpha channel(s) to alphaColour colour
|
||||||
if (colour[3] < 255.0 || image.has_alpha()) {
|
if (colour[3] < 255.0 || image.has_alpha()) {
|
||||||
alphaColour.push_back(colour[3] * multiplier);
|
int extraBands = image.bands() > 4 ? image.bands() - 3 : 1;
|
||||||
|
alphaColour.insert(alphaColour.end(), extraBands, colour[3] * multiplier);
|
||||||
}
|
}
|
||||||
// Ensure alphaColour colour uses correct colourspace
|
// Ensure alphaColour colour uses correct colourspace
|
||||||
alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation(), premultiply);
|
alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation(), premultiply);
|
||||||
// Add non-transparent alpha channel, if required
|
// Add non-transparent alpha channel, if required
|
||||||
if (colour[3] < 255.0 && !image.has_alpha()) {
|
if (colour[3] < 255.0 && !image.has_alpha()) {
|
||||||
image = image.bandjoin(
|
image = image.bandjoin_const({ 255 * multiplier });
|
||||||
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier).cast(image.format()));
|
|
||||||
}
|
}
|
||||||
return std::make_tuple(image, alphaColour);
|
return std::make_tuple(image, alphaColour);
|
||||||
}
|
}
|
||||||
@ -1031,9 +1040,7 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
VImage EnsureAlpha(VImage image, double const value) {
|
VImage EnsureAlpha(VImage image, double const value) {
|
||||||
if (!image.has_alpha()) {
|
if (!image.has_alpha()) {
|
||||||
std::vector<double> alpha;
|
image = image.bandjoin_const({ value * vips_interpretation_max_alpha(image.interpretation()) });
|
||||||
alpha.push_back(value * sharp::MaximumImageAlpha(image.interpretation()));
|
|
||||||
image = image.bandjoin_const(alpha);
|
|
||||||
}
|
}
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
41
src/common.h
41
src/common.h
@ -15,9 +15,9 @@
|
|||||||
// Verify platform and compiler compatibility
|
// Verify platform and compiler compatibility
|
||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION < 8) || \
|
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 16) || \
|
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 17) || \
|
||||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 16 && VIPS_MICRO_VERSION < 1)
|
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 17 && VIPS_MICRO_VERSION < 1)
|
||||||
#error "libvips version 8.16.1+ is required - please see https://sharp.pixelplumbing.com/install"
|
#error "libvips version 8.17.1+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__has_include)
|
#if defined(__has_include)
|
||||||
@ -48,13 +48,13 @@ namespace sharp {
|
|||||||
int rawWidth;
|
int rawWidth;
|
||||||
int rawHeight;
|
int rawHeight;
|
||||||
bool rawPremultiplied;
|
bool rawPremultiplied;
|
||||||
|
int rawPageHeight;
|
||||||
int pages;
|
int pages;
|
||||||
int page;
|
int page;
|
||||||
int level;
|
|
||||||
int subifd;
|
|
||||||
int createChannels;
|
int createChannels;
|
||||||
int createWidth;
|
int createWidth;
|
||||||
int createHeight;
|
int createHeight;
|
||||||
|
int createPageHeight;
|
||||||
std::vector<double> createBackground;
|
std::vector<double> createBackground;
|
||||||
std::string createNoiseType;
|
std::string createNoiseType;
|
||||||
double createNoiseMean;
|
double createNoiseMean;
|
||||||
@ -77,7 +77,12 @@ namespace sharp {
|
|||||||
std::vector<double> joinBackground;
|
std::vector<double> joinBackground;
|
||||||
VipsAlign joinHalign;
|
VipsAlign joinHalign;
|
||||||
VipsAlign joinValign;
|
VipsAlign joinValign;
|
||||||
|
std::string svgStylesheet;
|
||||||
|
bool svgHighBitdepth;
|
||||||
|
int tiffSubifd;
|
||||||
|
int openSlideLevel;
|
||||||
std::vector<double> pdfBackground;
|
std::vector<double> pdfBackground;
|
||||||
|
bool jp2Oneshot;
|
||||||
|
|
||||||
InputDescriptor():
|
InputDescriptor():
|
||||||
autoOrient(false),
|
autoOrient(false),
|
||||||
@ -95,13 +100,13 @@ namespace sharp {
|
|||||||
rawWidth(0),
|
rawWidth(0),
|
||||||
rawHeight(0),
|
rawHeight(0),
|
||||||
rawPremultiplied(false),
|
rawPremultiplied(false),
|
||||||
|
rawPageHeight(0),
|
||||||
pages(1),
|
pages(1),
|
||||||
page(0),
|
page(0),
|
||||||
level(0),
|
|
||||||
subifd(-1),
|
|
||||||
createChannels(0),
|
createChannels(0),
|
||||||
createWidth(0),
|
createWidth(0),
|
||||||
createHeight(0),
|
createHeight(0),
|
||||||
|
createPageHeight(0),
|
||||||
createBackground{ 0.0, 0.0, 0.0, 255.0 },
|
createBackground{ 0.0, 0.0, 0.0, 255.0 },
|
||||||
createNoiseMean(0.0),
|
createNoiseMean(0.0),
|
||||||
createNoiseSigma(0.0),
|
createNoiseSigma(0.0),
|
||||||
@ -120,7 +125,11 @@ namespace sharp {
|
|||||||
joinBackground{ 0.0, 0.0, 0.0, 255.0 },
|
joinBackground{ 0.0, 0.0, 0.0, 255.0 },
|
||||||
joinHalign(VIPS_ALIGN_LOW),
|
joinHalign(VIPS_ALIGN_LOW),
|
||||||
joinValign(VIPS_ALIGN_LOW),
|
joinValign(VIPS_ALIGN_LOW),
|
||||||
pdfBackground{ 255.0, 255.0, 255.0, 255.0 } {}
|
svgHighBitdepth(false),
|
||||||
|
tiffSubifd(-1),
|
||||||
|
openSlideLevel(0),
|
||||||
|
pdfBackground{ 255.0, 255.0, 255.0, 255.0 },
|
||||||
|
jp2Oneshot(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convenience methods to access the attributes of a Napi::Object
|
// Convenience methods to access the attributes of a Napi::Object
|
||||||
@ -160,6 +169,7 @@ namespace sharp {
|
|||||||
EXR,
|
EXR,
|
||||||
JXL,
|
JXL,
|
||||||
RAD,
|
RAD,
|
||||||
|
DCRAW,
|
||||||
VIPS,
|
VIPS,
|
||||||
RAW,
|
RAW,
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
@ -216,14 +226,9 @@ namespace sharp {
|
|||||||
ImageType DetermineImageType(char const *file);
|
ImageType DetermineImageType(char const *file);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Does this image type support multiple pages?
|
Format-specific options builder
|
||||||
*/
|
*/
|
||||||
bool ImageTypeSupportsPage(ImageType imageType);
|
vips::VOption* GetOptionsForImageType(ImageType imageType, InputDescriptor *descriptor);
|
||||||
|
|
||||||
/*
|
|
||||||
Does this image type support removal of safety limits?
|
|
||||||
*/
|
|
||||||
bool ImageTypeSupportsUnlimited(ImageType imageType);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
|
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
|
||||||
@ -357,12 +362,6 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
bool Is16Bit(VipsInterpretation const interpretation);
|
bool Is16Bit(VipsInterpretation const interpretation);
|
||||||
|
|
||||||
/*
|
|
||||||
Return the image alpha maximum. Useful for combining alpha bands. scRGB
|
|
||||||
images are 0 - 1 for image data, but the alpha is 0 - 255.
|
|
||||||
*/
|
|
||||||
double MaximumImageAlpha(VipsInterpretation const interpretation);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Convert RGBA value to another colourspace
|
Convert RGBA value to another colourspace
|
||||||
*/
|
*/
|
||||||
|
@ -262,6 +262,10 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|||||||
}
|
}
|
||||||
if (baton->xmpLength > 0) {
|
if (baton->xmpLength > 0) {
|
||||||
info.Set("xmp", Napi::Buffer<char>::NewOrCopy(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
|
info.Set("xmp", Napi::Buffer<char>::NewOrCopy(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
|
||||||
|
if (g_utf8_validate(static_cast<char const *>(baton->xmp), baton->xmpLength, nullptr)) {
|
||||||
|
info.Set("xmpAsString",
|
||||||
|
Napi::String::New(env, static_cast<char const *>(baton->xmp), baton->xmpLength));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (baton->tifftagPhotoshopLength > 0) {
|
if (baton->tifftagPhotoshopLength > 0) {
|
||||||
info.Set("tifftagPhotoshop",
|
info.Set("tifftagPhotoshop",
|
||||||
|
@ -241,11 +241,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// factor for jpegload*, a double scale factor for webpload*,
|
// factor for jpegload*, a double scale factor for webpload*,
|
||||||
// pdfload* and svgload*
|
// pdfload* and svgload*
|
||||||
if (jpegShrinkOnLoad > 1) {
|
if (jpegShrinkOnLoad > 1) {
|
||||||
vips::VOption *option = VImage::option()
|
vips::VOption *option = GetOptionsForImageType(inputImageType, baton->input)->set("shrink", jpegShrinkOnLoad);
|
||||||
->set("access", access)
|
|
||||||
->set("shrink", jpegShrinkOnLoad)
|
|
||||||
->set("unlimited", baton->input->unlimited)
|
|
||||||
->set("fail_on", baton->input->failOn);
|
|
||||||
if (baton->input->buffer != nullptr) {
|
if (baton->input->buffer != nullptr) {
|
||||||
// Reload JPEG buffer
|
// Reload JPEG buffer
|
||||||
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
||||||
@ -256,14 +252,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
image = VImage::jpegload(const_cast<char*>(baton->input->file.data()), option);
|
image = VImage::jpegload(const_cast<char*>(baton->input->file.data()), option);
|
||||||
}
|
}
|
||||||
} else if (scale != 1.0) {
|
} else if (scale != 1.0) {
|
||||||
vips::VOption *option = VImage::option()
|
vips::VOption *option = GetOptionsForImageType(inputImageType, baton->input)->set("scale", scale);
|
||||||
->set("access", access)
|
|
||||||
->set("scale", scale)
|
|
||||||
->set("fail_on", baton->input->failOn);
|
|
||||||
if (inputImageType == sharp::ImageType::WEBP) {
|
if (inputImageType == sharp::ImageType::WEBP) {
|
||||||
option->set("n", baton->input->pages);
|
|
||||||
option->set("page", baton->input->page);
|
|
||||||
|
|
||||||
if (baton->input->buffer != nullptr) {
|
if (baton->input->buffer != nullptr) {
|
||||||
// Reload WebP buffer
|
// Reload WebP buffer
|
||||||
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
||||||
@ -274,9 +264,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
image = VImage::webpload(const_cast<char*>(baton->input->file.data()), option);
|
image = VImage::webpload(const_cast<char*>(baton->input->file.data()), option);
|
||||||
}
|
}
|
||||||
} else if (inputImageType == sharp::ImageType::SVG) {
|
} else if (inputImageType == sharp::ImageType::SVG) {
|
||||||
option->set("unlimited", baton->input->unlimited);
|
|
||||||
option->set("dpi", baton->input->density);
|
|
||||||
|
|
||||||
if (baton->input->buffer != nullptr) {
|
if (baton->input->buffer != nullptr) {
|
||||||
// Reload SVG buffer
|
// Reload SVG buffer
|
||||||
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
||||||
@ -291,10 +278,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
throw vips::VError("Input SVG image will exceed 32767x32767 pixel limit when scaled");
|
throw vips::VError("Input SVG image will exceed 32767x32767 pixel limit when scaled");
|
||||||
}
|
}
|
||||||
} else if (inputImageType == sharp::ImageType::PDF) {
|
} else if (inputImageType == sharp::ImageType::PDF) {
|
||||||
option->set("n", baton->input->pages);
|
|
||||||
option->set("page", baton->input->page);
|
|
||||||
option->set("dpi", baton->input->density);
|
|
||||||
|
|
||||||
if (baton->input->buffer != nullptr) {
|
if (baton->input->buffer != nullptr) {
|
||||||
// Reload PDF buffer
|
// Reload PDF buffer
|
||||||
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
VipsBlob *blob = vips_blob_new(nullptr, baton->input->buffer, baton->input->bufferLength);
|
||||||
@ -304,7 +287,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Reload PDF file
|
// Reload PDF file
|
||||||
image = VImage::pdfload(const_cast<char*>(baton->input->file.data()), option);
|
image = VImage::pdfload(const_cast<char*>(baton->input->file.data()), option);
|
||||||
}
|
}
|
||||||
|
|
||||||
sharp::SetDensity(image, baton->input->density);
|
sharp::SetDensity(image, baton->input->density);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -668,7 +650,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
||||||
composite->input->access = access;
|
composite->input->access = access;
|
||||||
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
||||||
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline);
|
|
||||||
|
|
||||||
if (composite->input->autoOrient) {
|
if (composite->input->autoOrient) {
|
||||||
// Respect EXIF Orientation
|
// Respect EXIF Orientation
|
||||||
@ -733,8 +714,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// gravity was used for extract_area, set it back to its default value of 0
|
// gravity was used for extract_area, set it back to its default value of 0
|
||||||
composite->gravity = 0;
|
composite->gravity = 0;
|
||||||
}
|
}
|
||||||
// Ensure image to composite is sRGB with unpremultiplied alpha
|
// Ensure image to composite is with unpremultiplied alpha
|
||||||
compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
||||||
compositeImage = sharp::EnsureAlpha(compositeImage, 1);
|
compositeImage = sharp::EnsureAlpha(compositeImage, 1);
|
||||||
if (composite->premultiplied) compositeImage = compositeImage.unpremultiply();
|
if (composite->premultiplied) compositeImage = compositeImage.unpremultiply();
|
||||||
// Calculate position
|
// Calculate position
|
||||||
@ -759,7 +739,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
xs.push_back(left);
|
xs.push_back(left);
|
||||||
ys.push_back(top);
|
ys.push_back(top);
|
||||||
}
|
}
|
||||||
image = VImage::composite(images, modes, VImage::option()->set("x", xs)->set("y", ys));
|
image = VImage::composite(images, modes, VImage::option()
|
||||||
|
->set("compositing_space", baton->colourspacePipeline == VIPS_INTERPRETATION_LAST
|
||||||
|
? VIPS_INTERPRETATION_sRGB
|
||||||
|
: baton->colourspacePipeline)
|
||||||
|
->set("x", xs)
|
||||||
|
->set("y", ys));
|
||||||
image = sharp::RemoveGifPalette(image);
|
image = sharp::RemoveGifPalette(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,7 +876,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
image.set(s.first.data(), s.second.data());
|
image.set(s.first.data(), s.second.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// XMP buffer
|
||||||
|
if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_XMP) && !baton->withXmp.empty()) {
|
||||||
|
image = image.copy();
|
||||||
|
image.set(VIPS_META_XMP_NAME, nullptr,
|
||||||
|
const_cast<void*>(static_cast<void const*>(baton->withXmp.c_str())), baton->withXmp.size());
|
||||||
|
}
|
||||||
// Number of channels used in output image
|
// Number of channels used in output image
|
||||||
baton->channels = image.bands();
|
baton->channels = image.bands();
|
||||||
baton->width = image.width();
|
baton->width = image.width();
|
||||||
@ -1002,6 +992,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("interlace", baton->gifProgressive)
|
->set("interlace", baton->gifProgressive)
|
||||||
->set("interframe_maxerror", baton->gifInterFrameMaxError)
|
->set("interframe_maxerror", baton->gifInterFrameMaxError)
|
||||||
->set("interpalette_maxerror", baton->gifInterPaletteMaxError)
|
->set("interpalette_maxerror", baton->gifInterPaletteMaxError)
|
||||||
|
->set("keep_duplicate_frames", baton->gifKeepDuplicateFrames)
|
||||||
->set("dither", baton->gifDither)));
|
->set("dither", baton->gifDither)));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
baton->bufferOutLength = area->length;
|
baton->bufferOutLength = area->length;
|
||||||
@ -1205,6 +1196,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("effort", baton->gifEffort)
|
->set("effort", baton->gifEffort)
|
||||||
->set("reuse", baton->gifReuse)
|
->set("reuse", baton->gifReuse)
|
||||||
->set("interlace", baton->gifProgressive)
|
->set("interlace", baton->gifProgressive)
|
||||||
|
->set("interframe_maxerror", baton->gifInterFrameMaxError)
|
||||||
|
->set("interpalette_maxerror", baton->gifInterPaletteMaxError)
|
||||||
|
->set("keep_duplicate_frames", baton->gifKeepDuplicateFrames)
|
||||||
->set("dither", baton->gifDither));
|
->set("dither", baton->gifDither));
|
||||||
baton->formatOut = "gif";
|
baton->formatOut = "gif";
|
||||||
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
||||||
@ -1359,7 +1353,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Add file size to info
|
// Add file size to info
|
||||||
if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
|
if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
|
||||||
try {
|
try {
|
||||||
uint32_t const size = static_cast<uint32_t>(std::filesystem::file_size(baton->fileOut));
|
uint32_t const size = static_cast<uint32_t>(
|
||||||
|
std::filesystem::file_size(std::filesystem::u8path(baton->fileOut)));
|
||||||
info.Set("size", size);
|
info.Set("size", size);
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
}
|
}
|
||||||
@ -1716,6 +1711,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
|
baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
|
||||||
|
baton->withXmp = sharp::AttrAsStr(options, "withXmp");
|
||||||
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
||||||
baton->loop = sharp::AttrAsUint32(options, "loop");
|
baton->loop = sharp::AttrAsUint32(options, "loop");
|
||||||
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
|
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
|
||||||
@ -1756,6 +1752,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
||||||
baton->gifInterFrameMaxError = sharp::AttrAsDouble(options, "gifInterFrameMaxError");
|
baton->gifInterFrameMaxError = sharp::AttrAsDouble(options, "gifInterFrameMaxError");
|
||||||
baton->gifInterPaletteMaxError = sharp::AttrAsDouble(options, "gifInterPaletteMaxError");
|
baton->gifInterPaletteMaxError = sharp::AttrAsDouble(options, "gifInterPaletteMaxError");
|
||||||
|
baton->gifKeepDuplicateFrames = sharp::AttrAsBool(options, "gifKeepDuplicateFrames");
|
||||||
baton->gifReuse = sharp::AttrAsBool(options, "gifReuse");
|
baton->gifReuse = sharp::AttrAsBool(options, "gifReuse");
|
||||||
baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
|
baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
|
||||||
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
||||||
|
@ -169,6 +169,7 @@ struct PipelineBaton {
|
|||||||
double gifDither;
|
double gifDither;
|
||||||
double gifInterFrameMaxError;
|
double gifInterFrameMaxError;
|
||||||
double gifInterPaletteMaxError;
|
double gifInterPaletteMaxError;
|
||||||
|
bool gifKeepDuplicateFrames;
|
||||||
bool gifReuse;
|
bool gifReuse;
|
||||||
bool gifProgressive;
|
bool gifProgressive;
|
||||||
int tiffQuality;
|
int tiffQuality;
|
||||||
@ -201,6 +202,7 @@ struct PipelineBaton {
|
|||||||
std::string withIccProfile;
|
std::string withIccProfile;
|
||||||
std::unordered_map<std::string, std::string> withExif;
|
std::unordered_map<std::string, std::string> withExif;
|
||||||
bool withExifMerge;
|
bool withExifMerge;
|
||||||
|
std::string withXmp;
|
||||||
int timeoutSeconds;
|
int timeoutSeconds;
|
||||||
std::vector<double> convKernel;
|
std::vector<double> convKernel;
|
||||||
int convKernelWidth;
|
int convKernelWidth;
|
||||||
@ -342,6 +344,7 @@ struct PipelineBaton {
|
|||||||
gifDither(1.0),
|
gifDither(1.0),
|
||||||
gifInterFrameMaxError(0.0),
|
gifInterFrameMaxError(0.0),
|
||||||
gifInterPaletteMaxError(3.0),
|
gifInterPaletteMaxError(3.0),
|
||||||
|
gifKeepDuplicateFrames(false),
|
||||||
gifReuse(true),
|
gifReuse(true),
|
||||||
gifProgressive(false),
|
gifProgressive(false),
|
||||||
tiffQuality(80),
|
tiffQuality(80),
|
||||||
@ -384,7 +387,7 @@ struct PipelineBaton {
|
|||||||
ensureAlpha(-1.0),
|
ensureAlpha(-1.0),
|
||||||
colourspacePipeline(VIPS_INTERPRETATION_LAST),
|
colourspacePipeline(VIPS_INTERPRETATION_LAST),
|
||||||
colourspace(VIPS_INTERPRETATION_LAST),
|
colourspace(VIPS_INTERPRETATION_LAST),
|
||||||
loop(1),
|
loop(-1),
|
||||||
tileSize(256),
|
tileSize(256),
|
||||||
tileOverlap(0),
|
tileOverlap(0),
|
||||||
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
|
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
|
||||||
|
@ -18,8 +18,10 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
|
|||||||
vips_init("sharp");
|
vips_init("sharp");
|
||||||
});
|
});
|
||||||
|
|
||||||
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
|
for (auto domain : { "VIPS", "vips2tiff" }) {
|
||||||
|
g_log_set_handler(domain, static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
|
||||||
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
|
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
// Methods available to JavaScript
|
// Methods available to JavaScript
|
||||||
exports.Set("metadata", Napi::Function::New(env, metadata));
|
exports.Set("metadata", Napi::Function::New(env, metadata));
|
||||||
|
@ -60,7 +60,7 @@ class StatsWorker : public Napi::AsyncWorker {
|
|||||||
// Image is not opaque when alpha layer is present and contains a non-mamixa value
|
// Image is not opaque when alpha layer is present and contains a non-mamixa value
|
||||||
if (image.has_alpha()) {
|
if (image.has_alpha()) {
|
||||||
double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
|
double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
|
||||||
if (minAlpha != sharp::MaximumImageAlpha(image.interpretation())) {
|
if (minAlpha != vips_interpretation_max_alpha(image.interpretation())) {
|
||||||
baton->isOpaque = false;
|
baton->isOpaque = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ Napi::Value format(const Napi::CallbackInfo& info) {
|
|||||||
Napi::Object format = Napi::Object::New(env);
|
Napi::Object format = Napi::Object::New(env);
|
||||||
for (std::string const f : {
|
for (std::string const f : {
|
||||||
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz",
|
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz",
|
||||||
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl", "rad"
|
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl", "rad", "dcraw"
|
||||||
}) {
|
}) {
|
||||||
// Input
|
// Input
|
||||||
const VipsObjectClass *oc = vips_class_find("VipsOperation", (f + "load").c_str());
|
const VipsObjectClass *oc = vips_class_find("VipsOperation", (f + "load").c_str());
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
FROM ubuntu:24.10
|
FROM ubuntu:25.04
|
||||||
ARG BRANCH=main
|
ARG BRANCH=main
|
||||||
|
|
||||||
# Install basic dependencies
|
# Install basic dependencies
|
||||||
RUN apt-get -y update && apt-get install -y build-essential curl git ca-certificates gnupg
|
RUN apt-get -y update && apt-get install -y build-essential curl git ca-certificates gnupg
|
||||||
|
|
||||||
# Install latest Node.js LTS
|
# Install latest Node.js LTS
|
||||||
RUN curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh
|
RUN curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
|
||||||
RUN bash nodesource_setup.sh
|
RUN bash nodesource_setup.sh
|
||||||
RUN apt-get install -y nodejs
|
RUN apt-get install -y nodejs
|
||||||
|
|
||||||
|
@ -552,6 +552,32 @@ async.series({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).add('sharp-mks2013', {
|
||||||
|
defer: true,
|
||||||
|
fn: function (deferred) {
|
||||||
|
sharp(inputJpgBuffer)
|
||||||
|
.resize(width, height, { kernel: 'mks2013' })
|
||||||
|
.toBuffer(function (err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add('sharp-mks2021', {
|
||||||
|
defer: true,
|
||||||
|
fn: function (deferred) {
|
||||||
|
sharp(inputJpgBuffer)
|
||||||
|
.resize(width, height, { kernel: 'mks2021' })
|
||||||
|
.toBuffer(function (err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}).on('cycle', function (event) {
|
}).on('cycle', function (event) {
|
||||||
console.log('kernels ' + String(event.target));
|
console.log('kernels ' + String(event.target));
|
||||||
}).on('complete', function () {
|
}).on('complete', function () {
|
||||||
|
BIN
test/fixtures/expected/composite-red-scrgb.png
vendored
Normal file
BIN
test/fixtures/expected/composite-red-scrgb.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
test/fixtures/expected/extract.tiff
vendored
BIN
test/fixtures/expected/extract.tiff
vendored
Binary file not shown.
1
test/fixtures/index.js
vendored
1
test/fixtures/index.js
vendored
@ -117,6 +117,7 @@ module.exports = {
|
|||||||
inputTiffFogra: getPath('fogra-0-100-100-0.tif'), // https://github.com/lovell/sharp/issues/4045
|
inputTiffFogra: getPath('fogra-0-100-100-0.tif'), // https://github.com/lovell/sharp/issues/4045
|
||||||
|
|
||||||
inputJp2: getPath('relax.jp2'), // https://www.fnordware.com/j2k/relax.jp2
|
inputJp2: getPath('relax.jp2'), // https://www.fnordware.com/j2k/relax.jp2
|
||||||
|
inputJp2TileParts: getPath('relax_tileparts.jp2'), // kdu_expand -i relax.jp2 -o relax-tmp.tif ; kdu_compress -i relax-tmp.tif -o relax_tileparts.jp2 -jp2_space sRGB Clayers=8 -rate 1.0,0.04 Stiles='{128,128}' ORGtparts=L ; rm relax-tmp.tif
|
||||||
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
|
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
|
||||||
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
|
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
|
||||||
inputGifAnimated: getPath('rotating-squares.gif'), // CC0 https://loading.io/spinner/blocks/-rotating-squares-preloader-gif
|
inputGifAnimated: getPath('rotating-squares.gif'), // CC0 https://loading.io/spinner/blocks/-rotating-squares-preloader-gif
|
||||||
|
BIN
test/fixtures/relax_tileparts.jp2
vendored
Normal file
BIN
test/fixtures/relax_tileparts.jp2
vendored
Normal file
Binary file not shown.
@ -188,6 +188,8 @@ sharp(input)
|
|||||||
// of the image data in inputBuffer
|
// of the image data in inputBuffer
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sharp(input).resize({ kernel: 'mks2013' });
|
||||||
|
|
||||||
transformer = sharp()
|
transformer = sharp()
|
||||||
.resize(200, 200, {
|
.resize(200, 200, {
|
||||||
fit: 'cover',
|
fit: 'cover',
|
||||||
@ -373,6 +375,8 @@ sharp(input)
|
|||||||
.gif({ reuse: false })
|
.gif({ reuse: false })
|
||||||
.gif({ progressive: true })
|
.gif({ progressive: true })
|
||||||
.gif({ progressive: false })
|
.gif({ progressive: false })
|
||||||
|
.gif({ keepDuplicateFrames: true })
|
||||||
|
.gif({ keepDuplicateFrames: false })
|
||||||
.toBuffer({ resolveWithObject: true })
|
.toBuffer({ resolveWithObject: true })
|
||||||
.then(({ data, info }) => {
|
.then(({ data, info }) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
@ -414,6 +418,7 @@ sharp({
|
|||||||
channels: 4,
|
channels: 4,
|
||||||
height: 25000,
|
height: 25000,
|
||||||
width: 25000,
|
width: 25000,
|
||||||
|
pageHeight: 1000,
|
||||||
},
|
},
|
||||||
limitInputPixels: false,
|
limitInputPixels: false,
|
||||||
})
|
})
|
||||||
@ -431,9 +436,6 @@ sharp('input.jpg').clahe({ width: 10, height: 10, maxSlope: 5 }).toFile('outfile
|
|||||||
// Support `unlimited` input option
|
// Support `unlimited` input option
|
||||||
sharp('input.png', { unlimited: true }).resize(320, 240).toFile('outfile.png');
|
sharp('input.png', { unlimited: true }).resize(320, 240).toFile('outfile.png');
|
||||||
|
|
||||||
// Support `subifd` input option for tiffs
|
|
||||||
sharp('input.tiff', { subifd: 3 }).resize(320, 240).toFile('outfile.png');
|
|
||||||
|
|
||||||
// Support creating with noise
|
// Support creating with noise
|
||||||
sharp({
|
sharp({
|
||||||
create: {
|
create: {
|
||||||
@ -690,6 +692,8 @@ sharp(input)
|
|||||||
k2: 'v2'
|
k2: 'v2'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.keepXmp()
|
||||||
|
.withXmp('test')
|
||||||
.keepIccProfile()
|
.keepIccProfile()
|
||||||
.withIccProfile('filename')
|
.withIccProfile('filename')
|
||||||
.withIccProfile('filename', { attach: false });
|
.withIccProfile('filename', { attach: false });
|
||||||
@ -716,10 +720,29 @@ sharp(input).composite([
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// Support format-specific input options
|
||||||
const colour: sharp.Colour = '#fff';
|
const colour: sharp.Colour = '#fff';
|
||||||
const color: sharp.Color = '#fff';
|
const color: sharp.Color = '#fff';
|
||||||
sharp({ pdfBackground: colour });
|
sharp({ pdf: { background: colour } });
|
||||||
sharp({ pdfBackground: color });
|
sharp({ pdf: { background: color } });
|
||||||
|
sharp({ pdfBackground: colour }); // Deprecated
|
||||||
|
sharp({ pdfBackground: color }); // Deprecated
|
||||||
|
sharp({ tiff: { subifd: 3 } });
|
||||||
|
sharp({ subifd: 3 }); // Deprecated
|
||||||
|
sharp({ openSlide: { level: 0 } });
|
||||||
|
sharp({ level: 0 }); // Deprecated
|
||||||
|
sharp({ jp2: { oneshot: true } });
|
||||||
|
sharp({ jp2: { oneshot: false } });
|
||||||
|
sharp({ svg: { stylesheet: 'test' }});
|
||||||
|
sharp({ svg: { highBitdepth: true }});
|
||||||
|
sharp({ svg: { highBitdepth: false }});
|
||||||
|
|
||||||
|
// Raw input options
|
||||||
|
const raw: sharp.Raw = { width: 1, height: 1, channels: 3 };
|
||||||
|
sharp({ raw });
|
||||||
|
sharp({ raw: { ...raw, premultiplied: true } });
|
||||||
|
sharp({ raw: { ...raw, premultiplied: false } });
|
||||||
|
sharp({ raw: { ...raw, pageHeight: 1 } });
|
||||||
|
|
||||||
sharp({ autoOrient: true });
|
sharp({ autoOrient: true });
|
||||||
sharp({ autoOrient: false });
|
sharp({ autoOrient: false });
|
||||||
|
@ -122,6 +122,26 @@ describe('composite', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('scrgb pipeline', () => {
|
||||||
|
const filename = 'composite-red-scrgb.png';
|
||||||
|
const actual = fixtures.path(`output.${filename}`);
|
||||||
|
const expected = fixtures.expected(filename);
|
||||||
|
return sharp({
|
||||||
|
create: {
|
||||||
|
width: 32, height: 32, channels: 4, background: red
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.pipelineColourspace('scrgb')
|
||||||
|
.composite([{
|
||||||
|
input: fixtures.inputPngWithTransparency16bit,
|
||||||
|
blend: 'color-burn'
|
||||||
|
}])
|
||||||
|
.toFile(actual)
|
||||||
|
.then(() => {
|
||||||
|
fixtures.assertMaxColourDistance(actual, expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('multiple', async () => {
|
it('multiple', async () => {
|
||||||
const filename = 'composite-multiple.png';
|
const filename = 'composite-multiple.png';
|
||||||
const actual = fixtures.path(`output.${filename}`);
|
const actual = fixtures.path(`output.${filename}`);
|
||||||
|
@ -187,6 +187,17 @@ describe('GIF input', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('invalid keepDuplicateFrames throws', () => {
|
||||||
|
assert.throws(
|
||||||
|
() => sharp().gif({ keepDuplicateFrames: -1 }),
|
||||||
|
/Expected boolean for keepDuplicateFrames but received -1 of type number/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp().gif({ keepDuplicateFrames: 'fail' }),
|
||||||
|
/Expected boolean for keepDuplicateFrames but received fail of type string/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should work with streams when only animated is set', function (done) {
|
it('should work with streams when only animated is set', function (done) {
|
||||||
fs.createReadStream(fixtures.inputGifAnimated)
|
fs.createReadStream(fixtures.inputGifAnimated)
|
||||||
.pipe(sharp({ animated: true }))
|
.pipe(sharp({ animated: true }))
|
||||||
@ -225,6 +236,20 @@ describe('GIF input', () => {
|
|||||||
assert.strict(before.length > after.length);
|
assert.strict(before.length > after.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should keep duplicate frames via keepDuplicateFrames', async () => {
|
||||||
|
const create = { width: 8, height: 8, channels: 4, background: 'blue' };
|
||||||
|
const input = sharp([{ create }, { create }], { join: { animated: true } });
|
||||||
|
|
||||||
|
const before = await input.gif({ keepDuplicateFrames: false }).toBuffer();
|
||||||
|
const after = await input.gif({ keepDuplicateFrames: true }).toBuffer();
|
||||||
|
assert.strict(before.length < after.length);
|
||||||
|
|
||||||
|
const beforeMeta = await sharp(before).metadata();
|
||||||
|
const afterMeta = await sharp(after).metadata();
|
||||||
|
assert.strictEqual(beforeMeta.pages, 1);
|
||||||
|
assert.strictEqual(afterMeta.pages, 2);
|
||||||
|
});
|
||||||
|
|
||||||
it('non-animated input defaults to no-loop', async () => {
|
it('non-animated input defaults to no-loop', async () => {
|
||||||
for (const input of [fixtures.inputGif, fixtures.inputPng]) {
|
for (const input of [fixtures.inputGif, fixtures.inputPng]) {
|
||||||
const data = await sharp(input)
|
const data = await sharp(input)
|
||||||
@ -238,4 +263,15 @@ describe('GIF input', () => {
|
|||||||
assert.strictEqual(1, loop);
|
assert.strictEqual(1, loop);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Animated GIF to animated WebP merges identical frames', async () => {
|
||||||
|
const webp = await sharp(fixtures.inputGifAnimated, { animated: true })
|
||||||
|
.webp()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { delay, loop, pages } = await sharp(webp).metadata();
|
||||||
|
assert.deepStrictEqual([120, 120, 90, 120, 120, 90, 120, 90, 30], delay);
|
||||||
|
assert.strictEqual(0, loop);
|
||||||
|
assert.strictEqual(9, pages);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
120
test/unit/io.js
120
test/unit/io.js
@ -867,52 +867,91 @@ describe('Input/output', function () {
|
|||||||
sharp({ pages: '1' });
|
sharp({ pages: '1' });
|
||||||
}, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
|
}, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
|
||||||
});
|
});
|
||||||
it('Valid level property', function () {
|
it('Valid openSlide.level property', function () {
|
||||||
|
sharp({ openSlide: { level: 1 } });
|
||||||
sharp({ level: 1 });
|
sharp({ level: 1 });
|
||||||
});
|
});
|
||||||
it('Invalid level property (string) throws', function () {
|
it('Invalid openSlide.level property (string) throws', function () {
|
||||||
assert.throws(function () {
|
assert.throws(
|
||||||
sharp({ level: '1' });
|
() => sharp({ openSlide: { level: '1' } }),
|
||||||
}, /Expected integer between 0 and 256 for level but received 1 of type string/);
|
/Expected integer between 0 and 256 for openSlide.level but received 1 of type string/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ level: '1' }),
|
||||||
|
/Expected integer between 0 and 256 for level but received 1 of type string/
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('Invalid level property (negative) throws', function () {
|
it('Invalid openSlide.level property (negative) throws', function () {
|
||||||
assert.throws(function () {
|
assert.throws(
|
||||||
sharp({ level: -1 });
|
() => sharp({ openSlide: { level: -1 } }),
|
||||||
}, /Expected integer between 0 and 256 for level but received -1 of type number/);
|
/Expected integer between 0 and 256 for openSlide\.level but received -1 of type number/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ level: -1 }),
|
||||||
|
/Expected integer between 0 and 256 for level but received -1 of type number/
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('Valid subifd property', function () {
|
it('Valid tiff.subifd property', function () {
|
||||||
|
sharp({ tiff: { subifd: 1 } });
|
||||||
sharp({ subifd: 1 });
|
sharp({ subifd: 1 });
|
||||||
});
|
});
|
||||||
it('Invalid subifd property (string) throws', function () {
|
it('Invalid tiff.subifd property (string) throws', function () {
|
||||||
assert.throws(function () {
|
assert.throws(
|
||||||
sharp({ subifd: '1' });
|
() => sharp({ tiff: { subifd: '1' } }),
|
||||||
}, /Expected integer between -1 and 100000 for subifd but received 1 of type string/);
|
/Expected integer between -1 and 100000 for tiff\.subifd but received 1 of type string/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ subifd: '1' }),
|
||||||
|
/Expected integer between -1 and 100000 for subifd but received 1 of type string/
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('Invalid subifd property (float) throws', function () {
|
it('Invalid tiff.subifd property (float) throws', function () {
|
||||||
assert.throws(function () {
|
assert.throws(
|
||||||
sharp({ subifd: 1.2 });
|
() => sharp({ tiff: { subifd: 1.2 } }),
|
||||||
}, /Expected integer between -1 and 100000 for subifd but received 1.2 of type number/);
|
/Expected integer between -1 and 100000 for tiff\.subifd but received 1.2 of type number/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ subifd: 1.2 }),
|
||||||
|
/Expected integer between -1 and 100000 for subifd but received 1.2 of type number/
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('Valid pdfBackground property (string)', function () {
|
it('Valid pdf.background property (string)', function () {
|
||||||
|
sharp({ pdf: { background: '#00ff00' } });
|
||||||
sharp({ pdfBackground: '#00ff00' });
|
sharp({ pdfBackground: '#00ff00' });
|
||||||
});
|
});
|
||||||
it('Valid pdfBackground property (object)', function () {
|
it('Valid pdf.background property (object)', function () {
|
||||||
|
sharp({ pdf: { background: { r: 0, g: 255, b: 0 } } });
|
||||||
sharp({ pdfBackground: { r: 0, g: 255, b: 0 } });
|
sharp({ pdfBackground: { r: 0, g: 255, b: 0 } });
|
||||||
});
|
});
|
||||||
it('Invalid pdfBackground property (string) throws', function () {
|
it('Invalid pdf.background property (string) throws', function () {
|
||||||
assert.throws(function () {
|
assert.throws(
|
||||||
sharp({ pdfBackground: '00ff00' });
|
() => sharp({ pdf: { background: '00ff00' } }),
|
||||||
}, /Unable to parse color from string/);
|
/Unable to parse color from string/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ pdfBackground: '00ff00' }),
|
||||||
|
/Unable to parse color from string/
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('Invalid pdfBackground property (number) throws', function () {
|
it('Invalid pdf.background property (number) throws', function () {
|
||||||
assert.throws(function () {
|
assert.throws(
|
||||||
sharp({ pdfBackground: 255 });
|
() => sharp({ pdf: { background: 255 } }),
|
||||||
}, /Expected object or string for background/);
|
/Expected object or string for background/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ pdf: { background: 255 } }),
|
||||||
|
/Expected object or string for background/
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('Invalid pdfBackground property (object)', function () {
|
it('Invalid pdf.background property (object)', function () {
|
||||||
assert.throws(function () {
|
assert.throws(
|
||||||
sharp({ pdfBackground: { red: 0, green: 255, blue: 0 } });
|
() => sharp({ pdf: { background: { red: 0, green: 255, blue: 0 } } }),
|
||||||
}, /Unable to parse color from object/);
|
/Unable to parse color from object/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ pdfBackground: { red: 0, green: 255, blue: 0 } }),
|
||||||
|
/Unable to parse color from object/
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1036,4 +1075,21 @@ describe('Input/output', function () {
|
|||||||
});
|
});
|
||||||
readable.pipe(inPipeline).pipe(badPipeline);
|
readable.pipe(inPipeline).pipe(badPipeline);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports wide-character filenames', async () => {
|
||||||
|
const filename = fixtures.path('output.图片.jpg');
|
||||||
|
const create = {
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
channels: 3,
|
||||||
|
background: 'green'
|
||||||
|
};
|
||||||
|
await sharp({ create }).toFile(filename);
|
||||||
|
|
||||||
|
const { width, height, channels, format } = await sharp(filename).metadata();
|
||||||
|
assert.strictEqual(width, 8);
|
||||||
|
assert.strictEqual(height, 8);
|
||||||
|
assert.strictEqual(channels, 3);
|
||||||
|
assert.strictEqual(format, 'jpeg');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -93,10 +93,38 @@ describe('JP2 output', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Invalid JP2 chromaSubsampling value throws error', function () {
|
it('can use the jp2Oneshot option to handle multi-part tiled JPEG 2000 file', async () => {
|
||||||
assert.throws(function () {
|
const outputJpg = fixtures.path('output.jpg');
|
||||||
sharp().jpeg({ chromaSubsampling: '4:2:2' });
|
await assert.rejects(
|
||||||
|
() => sharp(fixtures.inputJp2TileParts).toFile(outputJpg)
|
||||||
|
);
|
||||||
|
await assert.doesNotReject(async () => {
|
||||||
|
await sharp(fixtures.inputJp2TileParts, { jp2Oneshot: true }).toFile(outputJpg);
|
||||||
|
const { format, width, height } = await sharp(outputJpg).metadata();
|
||||||
|
assert.strictEqual(format, 'jpeg');
|
||||||
|
assert.strictEqual(width, 320);
|
||||||
|
assert.strictEqual(height, 240);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Invalid JP2 chromaSubsampling value throws error', () => {
|
||||||
|
assert.throws(
|
||||||
|
() => sharp().jp2({ chromaSubsampling: '4:2:2' }),
|
||||||
|
/Expected one of 4:2:0, 4:4:4 but received 4:2:2 of type string/
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('valid JP2 oneshot value does not throw error', () => {
|
||||||
|
assert.doesNotThrow(
|
||||||
|
() => sharp({ jp2: { oneshot: true } })
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid JP2 oneshot value throws error', () => {
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ jp2: { oneshot: 'fail' } }),
|
||||||
|
/Expected boolean for jp2.oneshot but received fail of type string/
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -179,7 +179,7 @@ describe('libvips binaries', function () {
|
|||||||
process.env.npm_config_arch = 's390x';
|
process.env.npm_config_arch = 's390x';
|
||||||
process.env.npm_config_libc = '';
|
process.env.npm_config_libc = '';
|
||||||
const locatorHash = libvips.yarnLocator();
|
const locatorHash = libvips.yarnLocator();
|
||||||
assert.strictEqual(locatorHash, '9b2ea457de');
|
assert.strictEqual(locatorHash, '30afd744f9');
|
||||||
delete process.env.npm_config_platform;
|
delete process.env.npm_config_platform;
|
||||||
delete process.env.npm_config_arch;
|
delete process.env.npm_config_arch;
|
||||||
delete process.env.npm_config_libc;
|
delete process.env.npm_config_libc;
|
||||||
|
@ -82,6 +82,7 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||||
assert.strictEqual(12466, metadata.xmp.byteLength);
|
assert.strictEqual(12466, metadata.xmp.byteLength);
|
||||||
assert.strictEqual(metadata.xmp.indexOf(Buffer.from('<?xpacket begin="')), 0);
|
assert.strictEqual(metadata.xmp.indexOf(Buffer.from('<?xpacket begin="')), 0);
|
||||||
|
assert(metadata.xmpAsString.startsWith('<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>'));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -106,6 +107,8 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual(3248, metadata.autoOrient.height);
|
assert.strictEqual(3248, metadata.autoOrient.height);
|
||||||
assert.strictEqual('undefined', typeof metadata.exif);
|
assert.strictEqual('undefined', typeof metadata.exif);
|
||||||
assert.strictEqual('undefined', typeof metadata.icc);
|
assert.strictEqual('undefined', typeof metadata.icc);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.xmp);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.xmpAsString);
|
||||||
assert.strictEqual('inch', metadata.resolutionUnit);
|
assert.strictEqual('inch', metadata.resolutionUnit);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -1100,6 +1103,170 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual(exif2.Image.Software, 'sharp');
|
assert.strictEqual(exif2.Image.Software, 'sharp');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('XMP metadata tests', function () {
|
||||||
|
it('withMetadata preserves existing XMP metadata from input', async () => {
|
||||||
|
const data = await sharp(fixtures.inputJpgWithIptcAndXmp)
|
||||||
|
.resize(320, 240)
|
||||||
|
.withMetadata()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
assert.strictEqual('object', typeof metadata.xmp);
|
||||||
|
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||||
|
assert.strictEqual(true, metadata.xmp.length > 0);
|
||||||
|
// Check that XMP starts with the expected XML declaration
|
||||||
|
assert.strictEqual(metadata.xmp.indexOf(Buffer.from('<?xpacket begin="')), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keepXmp preserves existing XMP metadata from input', async () => {
|
||||||
|
const data = await sharp(fixtures.inputJpgWithIptcAndXmp)
|
||||||
|
.resize(320, 240)
|
||||||
|
.keepXmp()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
assert.strictEqual('object', typeof metadata.xmp);
|
||||||
|
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||||
|
assert.strictEqual(true, metadata.xmp.length > 0);
|
||||||
|
// Check that XMP starts with the expected XML declaration
|
||||||
|
assert.strictEqual(metadata.xmp.indexOf(Buffer.from('<?xpacket begin="')), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withXmp with custom XMP replaces existing XMP', async () => {
|
||||||
|
const customXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:creator><rdf:Seq><rdf:li>Test Creator</rdf:li></rdf:Seq></dc:creator><dc:title><rdf:Alt><rdf:li xml:lang="x-default">Test Title</rdf:li></rdf:Alt></dc:title></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||||
|
|
||||||
|
const data = await sharp(fixtures.inputJpgWithIptcAndXmp)
|
||||||
|
.resize(320, 240)
|
||||||
|
.withXmp(customXmp)
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
assert.strictEqual('object', typeof metadata.xmp);
|
||||||
|
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||||
|
|
||||||
|
// Check that the XMP contains our custom content
|
||||||
|
const xmpString = metadata.xmp.toString();
|
||||||
|
assert.strictEqual(true, xmpString.includes('Test Creator'));
|
||||||
|
assert.strictEqual(true, xmpString.includes('Test Title'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withXmp with custom XMP buffer on image without existing XMP', async () => {
|
||||||
|
const customXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:description><rdf:Alt><rdf:li xml:lang="x-default">Added via Sharp</rdf:li></rdf:Alt></dc:description></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||||
|
|
||||||
|
const data = await sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.withXmp(customXmp)
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
assert.strictEqual('object', typeof metadata.xmp);
|
||||||
|
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||||
|
|
||||||
|
// Check that the XMP contains our custom content
|
||||||
|
const xmpString = metadata.xmp.toString();
|
||||||
|
assert.strictEqual(true, xmpString.includes('Added via Sharp'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withXmp with valid XMP metadata for different image formats', async () => {
|
||||||
|
const customXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:subject><rdf:Bag><rdf:li>test</rdf:li><rdf:li>metadata</rdf:li></rdf:Bag></dc:subject></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||||
|
|
||||||
|
// Test with JPEG output
|
||||||
|
const jpegData = await sharp(fixtures.inputJpg)
|
||||||
|
.resize(100, 100)
|
||||||
|
.jpeg()
|
||||||
|
.withXmp(customXmp)
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const jpegMetadata = await sharp(jpegData).metadata();
|
||||||
|
assert.strictEqual('object', typeof jpegMetadata.xmp);
|
||||||
|
assert.strictEqual(true, jpegMetadata.xmp instanceof Buffer);
|
||||||
|
assert.strictEqual(true, jpegMetadata.xmp.toString().includes('test'));
|
||||||
|
|
||||||
|
// Test with PNG output (PNG should also support XMP metadata)
|
||||||
|
const pngData = await sharp(fixtures.inputJpg)
|
||||||
|
.resize(100, 100)
|
||||||
|
.png()
|
||||||
|
.withXmp(customXmp)
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const pngMetadata = await sharp(pngData).metadata();
|
||||||
|
// PNG format should preserve XMP metadata when using withXmp
|
||||||
|
assert.strictEqual('object', typeof pngMetadata.xmp);
|
||||||
|
assert.strictEqual(true, pngMetadata.xmp instanceof Buffer);
|
||||||
|
assert.strictEqual(true, pngMetadata.xmp.toString().includes('test'));
|
||||||
|
|
||||||
|
// Test with WebP output (WebP should also support XMP metadata)
|
||||||
|
const webpData = await sharp(fixtures.inputJpg)
|
||||||
|
.resize(100, 100)
|
||||||
|
.webp()
|
||||||
|
.withXmp(customXmp)
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const webpMetadata = await sharp(webpData).metadata();
|
||||||
|
// WebP format should preserve XMP metadata when using withXmp
|
||||||
|
assert.strictEqual('object', typeof webpMetadata.xmp);
|
||||||
|
assert.strictEqual(true, webpMetadata.xmp instanceof Buffer);
|
||||||
|
assert.strictEqual(true, webpMetadata.xmp.toString().includes('test'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('XMP metadata persists through multiple operations', async () => {
|
||||||
|
const customXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:identifier>persistent-test</dc:identifier></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||||
|
|
||||||
|
const data = await sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.withXmp(customXmp)
|
||||||
|
.rotate(90)
|
||||||
|
.blur(1)
|
||||||
|
.sharpen()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
assert.strictEqual('object', typeof metadata.xmp);
|
||||||
|
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||||
|
assert.strictEqual(true, metadata.xmp.toString().includes('persistent-test'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withXmp XMP works with WebP format specifically', async () => {
|
||||||
|
const webpXmp = '<?xml version="1.0"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:creator><rdf:Seq><rdf:li>WebP Creator</rdf:li></rdf:Seq></dc:creator><dc:format>image/webp</dc:format></rdf:Description></rdf:RDF></x:xmpmeta>';
|
||||||
|
|
||||||
|
const data = await sharp(fixtures.inputJpg)
|
||||||
|
.resize(120, 80)
|
||||||
|
.webp({ quality: 80 })
|
||||||
|
.withXmp(webpXmp)
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
assert.strictEqual('webp', metadata.format);
|
||||||
|
assert.strictEqual('object', typeof metadata.xmp);
|
||||||
|
assert.strictEqual(true, metadata.xmp instanceof Buffer);
|
||||||
|
|
||||||
|
const xmpString = metadata.xmp.toString();
|
||||||
|
assert.strictEqual(true, xmpString.includes('WebP Creator'));
|
||||||
|
assert.strictEqual(true, xmpString.includes('image/webp'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withXmp XMP validation - non-string input', function () {
|
||||||
|
assert.throws(
|
||||||
|
() => sharp().withXmp(123),
|
||||||
|
/Expected non-empty string for xmp but received 123 of type number/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withXmp XMP validation - null input', function () {
|
||||||
|
assert.throws(
|
||||||
|
() => sharp().withXmp(null),
|
||||||
|
/Expected non-empty string for xmp but received null of type object/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withXmp XMP validation - empty string', function () {
|
||||||
|
assert.throws(
|
||||||
|
() => sharp().withXmp(''),
|
||||||
|
/Expected non-empty string for xmp/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Invalid parameters', function () {
|
describe('Invalid parameters', function () {
|
||||||
it('String orientation', function () {
|
it('String orientation', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
|
@ -173,6 +173,26 @@ describe('Gaussian noise', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('animated noise', async () => {
|
||||||
|
const gif = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 16,
|
||||||
|
height: 64,
|
||||||
|
pageHeight: 16,
|
||||||
|
channels: 3,
|
||||||
|
noise: { type: 'gaussian' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.gif()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { width, height, pages, delay } = await sharp(gif).metadata();
|
||||||
|
assert.strictEqual(width, 16);
|
||||||
|
assert.strictEqual(height, 16);
|
||||||
|
assert.strictEqual(pages, 4);
|
||||||
|
assert.strictEqual(delay.length, 4);
|
||||||
|
});
|
||||||
|
|
||||||
it('no create object properties specified', function () {
|
it('no create object properties specified', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp({
|
sharp({
|
||||||
@ -259,4 +279,29 @@ describe('Gaussian noise', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Invalid pageHeight', () => {
|
||||||
|
const create = {
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
channels: 4,
|
||||||
|
noise: { type: 'gaussian' }
|
||||||
|
};
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ create: { ...create, pageHeight: 'zoinks' } }),
|
||||||
|
/Expected positive integer for create\.pageHeight but received zoinks of type string/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ create: { ...create, pageHeight: -1 } }),
|
||||||
|
/Expected positive integer for create\.pageHeight but received -1 of type number/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ create: { ...create, pageHeight: 9 } }),
|
||||||
|
/Expected positive integer for create\.pageHeight but received 9 of type number/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ create: { ...create, pageHeight: 3 } }),
|
||||||
|
/Expected create\.height 8 to be a multiple of create\.pageHeight 3/
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -55,6 +55,35 @@ describe('Raw pixel data', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Invalid premultiplied', () => {
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ raw: { width: 1, height: 1, channels: 4, premultiplied: 'zoinks' } }),
|
||||||
|
/Expected boolean for raw\.premultiplied but received zoinks of type string/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid pageHeight', () => {
|
||||||
|
const width = 8;
|
||||||
|
const height = 8;
|
||||||
|
const channels = 4;
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ raw: { width, height, channels, pageHeight: 'zoinks' } }),
|
||||||
|
/Expected positive integer for raw\.pageHeight but received zoinks of type string/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ raw: { width, height, channels, pageHeight: -1 } }),
|
||||||
|
/Expected positive integer for raw\.pageHeight but received -1 of type number/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ raw: { width, height, channels, pageHeight: 9 } }),
|
||||||
|
/Expected positive integer for raw\.pageHeight but received 9 of type number/
|
||||||
|
);
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ raw: { width, height, channels, pageHeight: 3 } }),
|
||||||
|
/Expected raw\.height 8 to be a multiple of raw\.pageHeight 3/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('RGB', function (done) {
|
it('RGB', function (done) {
|
||||||
// Convert to raw pixel data
|
// Convert to raw pixel data
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
@ -285,6 +314,23 @@ describe('Raw pixel data', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Animated', async () => {
|
||||||
|
const gif = await sharp(
|
||||||
|
Buffer.alloc(8),
|
||||||
|
{ raw: { width: 1, height: 2, channels: 4, pageHeight: 1 }, animated: true }
|
||||||
|
)
|
||||||
|
.gif({ keepDuplicateFrames: true })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
console.log(await sharp(gif).metadata());
|
||||||
|
|
||||||
|
const { width, height, pages, delay } = await sharp(gif).metadata();
|
||||||
|
assert.strictEqual(width, 1);
|
||||||
|
assert.strictEqual(height, 1);
|
||||||
|
assert.strictEqual(pages, 2);
|
||||||
|
assert.strictEqual(delay.length, 2);
|
||||||
|
});
|
||||||
|
|
||||||
describe('16-bit roundtrip', () => {
|
describe('16-bit roundtrip', () => {
|
||||||
it('grey', async () => {
|
it('grey', async () => {
|
||||||
const grey = 42000;
|
const grey = 42000;
|
||||||
|
@ -806,4 +806,33 @@ describe('Resize fit=contain', function () {
|
|||||||
fixtures.assertSimilar(fixtures.expected('./embedgravitybird/9-c.png'), data, done);
|
fixtures.assertSimilar(fixtures.expected('./embedgravitybird/9-c.png'), data, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('multiple alpha channels', async () => {
|
||||||
|
const create = {
|
||||||
|
width: 20,
|
||||||
|
height: 12,
|
||||||
|
channels: 4,
|
||||||
|
background: 'green'
|
||||||
|
};
|
||||||
|
const multipleAlphaChannels = await sharp({ create })
|
||||||
|
.joinChannel({ create })
|
||||||
|
.tiff({ compression: 'deflate' })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const data = await sharp(multipleAlphaChannels)
|
||||||
|
.resize({
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
fit: 'contain',
|
||||||
|
background: 'blue'
|
||||||
|
})
|
||||||
|
.tiff({ compression: 'deflate' })
|
||||||
|
.toBuffer();
|
||||||
|
const { format, width, height, space, channels } = await sharp(data).metadata();
|
||||||
|
assert.deepStrictEqual(format, 'tiff');
|
||||||
|
assert.deepStrictEqual(width, 8);
|
||||||
|
assert.deepStrictEqual(height, 8);
|
||||||
|
assert.deepStrictEqual(space, 'srgb');
|
||||||
|
assert.deepStrictEqual(channels, 8);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -139,6 +139,41 @@ describe('SVG input', function () {
|
|||||||
assert.strictEqual(info.channels, 4);
|
assert.strictEqual(info.channels, 4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Can apply custom CSS', async () => {
|
||||||
|
const svg = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="10" height="10" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="5" cy="5" r="4" fill="green" />
|
||||||
|
</svg>`;
|
||||||
|
const stylesheet = 'circle { fill: red }';
|
||||||
|
|
||||||
|
const [r, g, b, a] = await sharp(Buffer.from(svg), { svg: { stylesheet } })
|
||||||
|
.extract({ left: 5, top: 5, width: 1, height: 1 })
|
||||||
|
.raw()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
assert.deepEqual([r, g, b, a], [255, 0, 0, 255]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid stylesheet input option throws', () =>
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ svg: { stylesheet: 123 } }),
|
||||||
|
/Expected string for svg\.stylesheet but received 123 of type number/
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Valid highBitdepth input option does not throw', () =>
|
||||||
|
assert.doesNotThrow(
|
||||||
|
() => sharp({ svg: { highBitdepth: true } })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Invalid highBitdepth input option throws', () =>
|
||||||
|
assert.throws(
|
||||||
|
() => sharp({ svg: { highBitdepth: 123 } }),
|
||||||
|
/Expected boolean for svg\.highBitdepth but received 123 of type number/
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
it('Fails to render SVG larger than 32767x32767', () =>
|
it('Fails to render SVG larger than 32767x32767', () =>
|
||||||
assert.rejects(
|
assert.rejects(
|
||||||
() => sharp(Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" width="32768" height="1" />')).toBuffer(),
|
() => sharp(Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" width="32768" height="1" />')).toBuffer(),
|
||||||
|
@ -59,7 +59,7 @@ describe('Text to image', function () {
|
|||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual('png', info.format);
|
||||||
assert.strictEqual(3, info.channels);
|
assert.strictEqual(3, info.channels);
|
||||||
assert.ok(inRange(info.width, 400, 600), `Actual width ${info.width}`);
|
assert.ok(inRange(info.width, 400, 600), `Actual width ${info.width}`);
|
||||||
assert.ok(inRange(info.height, 300, 500), `Actual height ${info.height}`);
|
assert.ok(inRange(info.height, 290, 500), `Actual height ${info.height}`);
|
||||||
assert.ok(inRange(info.textAutofitDpi, 900, 1300), `Actual textAutofitDpi ${info.textAutofitDpi}`);
|
assert.ok(inRange(info.textAutofitDpi, 900, 1300), `Actual textAutofitDpi ${info.textAutofitDpi}`);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -10,18 +10,20 @@ const sharp = require('../../');
|
|||||||
describe('Utilities', function () {
|
describe('Utilities', function () {
|
||||||
describe('Cache', function () {
|
describe('Cache', function () {
|
||||||
it('Can be disabled', function (done) {
|
it('Can be disabled', function (done) {
|
||||||
queueMicrotask(() => {
|
const check = setInterval(() => {
|
||||||
sharp.cache(false);
|
|
||||||
const cache = sharp.cache(false);
|
const cache = sharp.cache(false);
|
||||||
assert.strictEqual(cache.memory.current, 0);
|
const empty =
|
||||||
assert.strictEqual(cache.memory.max, 0);
|
cache.memory.current +
|
||||||
assert.strictEqual(typeof cache.memory.high, 'number');
|
cache.memory.max +
|
||||||
assert.strictEqual(cache.files.current, 0);
|
cache.files.current +
|
||||||
assert.strictEqual(cache.files.max, 0);
|
cache.files.max +
|
||||||
assert.strictEqual(cache.items.current, 0);
|
cache.items.current +
|
||||||
assert.strictEqual(cache.items.max, 0);
|
cache.items.max === 0;
|
||||||
|
if (empty) {
|
||||||
|
clearInterval(check);
|
||||||
done();
|
done();
|
||||||
});
|
}
|
||||||
|
}, 2000);
|
||||||
});
|
});
|
||||||
it('Can be enabled with defaults', function () {
|
it('Can be enabled with defaults', function () {
|
||||||
const cache = sharp.cache(true);
|
const cache = sharp.cache(true);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user