mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 13:46:19 +01:00
Compare commits
57 Commits
v0.32.4
...
v0.33.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9aa7339ce | ||
|
|
08108f5fad | ||
|
|
58e3c4c70e | ||
|
|
f8cf25ca56 | ||
|
|
ca95979ecc | ||
|
|
392f6afb5e | ||
|
|
7c97aabaf8 | ||
|
|
fcc7e84bee | ||
|
|
226a9a13ef | ||
|
|
4d3c9ae3d1 | ||
|
|
f31011d759 | ||
|
|
7cf4ae5648 | ||
|
|
9161c605e1 | ||
|
|
70ac6905c7 | ||
|
|
265d70111a | ||
|
|
59327bdd53 | ||
|
|
3043e01171 | ||
|
|
36feb7551b | ||
|
|
a41c62be2b | ||
|
|
854ed65016 | ||
|
|
8f63d131a4 | ||
|
|
70a3067963 | ||
|
|
aabbe1fa08 | ||
|
|
0f8bb9196e | ||
|
|
efee9f1779 | ||
|
|
61c5cb4669 | ||
|
|
e618c17bd0 | ||
|
|
6578118f32 | ||
|
|
ba20b8ada4 | ||
|
|
eefaa99872 | ||
|
|
dbce6fab79 | ||
|
|
af0fcb37c2 | ||
|
|
c6f54e59da | ||
|
|
846563e45f | ||
|
|
9c217ab580 | ||
|
|
e7381e522e | ||
|
|
4340d60ccf | ||
|
|
7f64d464de | ||
|
|
67e927bdb6 | ||
|
|
9c7713ed54 | ||
|
|
8be6da1def | ||
|
|
95635683ac | ||
|
|
44a0ee3fd3 | ||
|
|
ccd51c8cbf | ||
|
|
bb7469b2d1 | ||
|
|
a2cac61209 | ||
|
|
5c19f6dd9b | ||
|
|
3d01775972 | ||
|
|
87562a5111 | ||
|
|
2829e17743 | ||
|
|
ffefbd2ecc | ||
|
|
bc8f983329 | ||
|
|
440936a699 | ||
|
|
0bc79cdb95 | ||
|
|
9a66e25f53 | ||
|
|
8370935ccf | ||
|
|
f908987f35 |
@@ -3,14 +3,6 @@ version: 2.1
|
|||||||
workflows:
|
workflows:
|
||||||
build:
|
build:
|
||||||
jobs:
|
jobs:
|
||||||
- linux-arm64-glibc-node-14:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^v.*/
|
|
||||||
- linux-arm64-musl-node-14:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^v.*/
|
|
||||||
- linux-arm64-glibc-node-18:
|
- linux-arm64-glibc-node-18:
|
||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
@@ -19,9 +11,17 @@ workflows:
|
|||||||
filters:
|
filters:
|
||||||
tags:
|
tags:
|
||||||
only: /^v.*/
|
only: /^v.*/
|
||||||
|
- linux-arm64-glibc-node-20:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^v.*/
|
||||||
|
- linux-arm64-musl-node-20:
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^v.*/
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linux-arm64-glibc-node-14:
|
linux-arm64-glibc-node-18:
|
||||||
resource_class: arm.medium
|
resource_class: arm.medium
|
||||||
machine:
|
machine:
|
||||||
image: ubuntu-2004:current
|
image: ubuntu-2004:current
|
||||||
@@ -30,13 +30,20 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
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 "apt-get update && apt-get install -y build-essential git python3 curl fonts-noto-core"
|
||||||
sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
sudo docker exec sharp sh -c "mkdir -p /etc/apt/keyrings"
|
||||||
sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_14.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
sudo docker exec sharp sh -c "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"
|
sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
||||||
- run: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
- run: sudo docker exec sharp sh -c "npm install --build-from-source"
|
||||||
- run: sudo docker exec sharp sh -c "npm test"
|
- run: sudo docker exec sharp sh -c "npm test"
|
||||||
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 7 --upload=$prebuild_upload\" || true"
|
- run: |
|
||||||
linux-arm64-glibc-node-18:
|
sudo docker exec sharp sh -c "npm run package-from-local-build"
|
||||||
|
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@sharpen/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 \"npx prebuild --upload=$prebuild_upload\" || true"
|
||||||
|
linux-arm64-glibc-node-20:
|
||||||
resource_class: arm.medium
|
resource_class: arm.medium
|
||||||
machine:
|
machine:
|
||||||
image: ubuntu-2004:current
|
image: ubuntu-2004:current
|
||||||
@@ -45,25 +52,20 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
sudo docker run -dit --name sharp --workdir /mnt/sharp arm64v8/debian:bullseye
|
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 "apt-get update && apt-get install -y build-essential git python3 curl fonts-noto-core"
|
||||||
sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
sudo docker exec sharp sh -c "mkdir -p /etc/apt/keyrings"
|
||||||
sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_18.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
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 "apt-get update && apt-get install -y nodejs"
|
||||||
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
|
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
|
||||||
sudo docker cp . sharp:/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 install --build-from-source"
|
||||||
- run: sudo docker exec sharp sh -c "npm test"
|
- run: sudo docker exec sharp sh -c "npm test"
|
||||||
linux-arm64-musl-node-14:
|
|
||||||
resource_class: arm.medium
|
|
||||||
machine:
|
|
||||||
image: ubuntu-2004:current
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: |
|
- run: |
|
||||||
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:14-alpine3.12
|
sudo docker exec sharp sh -c "npm run package-from-local-build"
|
||||||
sudo docker exec sharp sh -c "apk add build-base git python3 font-noto --update-cache"
|
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@sharpen/sharp-linux-arm64=file:./npm/linux-arm64\""
|
||||||
- run: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
sudo docker exec sharp sh -c "npm run clean"
|
||||||
- run: sudo docker exec sharp sh -c "npm test"
|
sudo docker exec sharp sh -c "npm install --ignore-scripts"
|
||||||
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"npx prebuild --runtime napi --target 7 --upload=$prebuild_upload\" || true"
|
sudo docker exec sharp sh -c "npm test"
|
||||||
linux-arm64-musl-node-18:
|
linux-arm64-musl-node-18:
|
||||||
resource_class: arm.medium
|
resource_class: arm.medium
|
||||||
machine:
|
machine:
|
||||||
@@ -71,9 +73,33 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: |
|
- run: |
|
||||||
sudo docker run -dit --name sharp --workdir /mnt/sharp node:18-alpine3.14
|
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.@sharpen/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 \"npx prebuild --upload=$prebuild_upload\" || true"
|
||||||
|
linux-arm64-musl-node-20:
|
||||||
|
resource_class: arm.medium
|
||||||
|
machine:
|
||||||
|
image: ubuntu-2004: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 "apk add build-base git python3 font-noto --update-cache"
|
||||||
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
|
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
|
||||||
sudo docker cp . sharp:/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 install --build-from-source"
|
||||||
- run: sudo docker exec sharp sh -c "npm test"
|
- 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.@sharpen/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"
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ task:
|
|||||||
- pkg upgrade -y
|
- pkg upgrade -y
|
||||||
- pkg install -y devel/git devel/pkgconf graphics/vips www/node20 www/npm
|
- pkg install -y devel/git devel/pkgconf graphics/vips www/node20 www/npm
|
||||||
install_script:
|
install_script:
|
||||||
- npm install --build-from-source --unsafe-perm
|
- npm install --build-from-source
|
||||||
test_script:
|
test_script:
|
||||||
- npm test
|
- npm test
|
||||||
|
|||||||
21
.github/ISSUE_TEMPLATE/installation.md
vendored
21
.github/ISSUE_TEMPLATE/installation.md
vendored
@@ -12,7 +12,6 @@ labels: installation
|
|||||||
<!-- Please place an [x] in the box to confirm. -->
|
<!-- Please place an [x] in the box to confirm. -->
|
||||||
|
|
||||||
- [ ] I have read the [documentation relating to installation](https://sharp.pixelplumbing.com/install).
|
- [ ] I have read the [documentation relating to installation](https://sharp.pixelplumbing.com/install).
|
||||||
- [ ] I have ensured that the architecture and platform of Node.js used for `npm install` is the same as the architecture and platform of Node.js used at runtime.
|
|
||||||
|
|
||||||
### Are you using the latest version of sharp?
|
### Are you using the latest version of sharp?
|
||||||
|
|
||||||
@@ -24,13 +23,25 @@ If you cannot confirm this, please upgrade to the latest version and try again b
|
|||||||
|
|
||||||
If you are using another package which depends on a version of `sharp` that is not the latest, please open an issue against that package instead.
|
If you are using another package which depends on a version of `sharp` that is not the latest, please open an issue against that package instead.
|
||||||
|
|
||||||
### Is this a problem with filesystem permissions?
|
### Are you using a supported runtime?
|
||||||
|
|
||||||
If you are using npm v6 or earlier and installing as a `root` or `sudo` user, have you tried with the `npm install --unsafe-perm` flag?
|
<!-- Please place an [x] in the relevant box to confirm. -->
|
||||||
|
|
||||||
If you are using npm v7 or later, does the user running `npm install` own the directory it is run in?
|
- [ ] I am using Node.js 18 with a version >= 18.17.0
|
||||||
|
- [ ] I am using Node.js 20 with a version >= 20.3.0
|
||||||
|
- [ ] I am using Node.js 21 or later
|
||||||
|
|
||||||
If you are using the `ignore-scripts` feature of `npm`, have you tried with the `npm install --ignore-scripts=false` flag?
|
If you cannot confirm any of these, please upgrade to the latest version and try again before opening an issue.
|
||||||
|
|
||||||
|
### Are you using a supported package manager?
|
||||||
|
|
||||||
|
<!-- Please place an [x] in the relevant box to confirm. -->
|
||||||
|
|
||||||
|
- [ ] I am using npm >= 9.6.5
|
||||||
|
- [ ] I am using yarn >= 3.2.0
|
||||||
|
- [ ] I am using pnpm >= 7.1.0
|
||||||
|
|
||||||
|
If you cannot confirm any of these, please upgrade to the latest version and try again before opening an issue.
|
||||||
|
|
||||||
### What is the complete output of running `npm install --verbose --foreground-scripts sharp` in an empty directory?
|
### What is the complete output of running `npm install --verbose --foreground-scripts sharp` in an empty directory?
|
||||||
|
|
||||||
|
|||||||
40
.github/workflows/ci-darwin-arm64v8.yml
vendored
40
.github/workflows/ci-darwin-arm64v8.yml
vendored
@@ -1,40 +0,0 @@
|
|||||||
name: CI (MacStadium)
|
|
||||||
on:
|
|
||||||
- push
|
|
||||||
- pull_request
|
|
||||||
permissions: {}
|
|
||||||
jobs:
|
|
||||||
CI:
|
|
||||||
permissions:
|
|
||||||
contents: write # for npx prebuild to make release
|
|
||||||
name: Node.js ${{ matrix.nodejs_version }} ${{ matrix.nodejs_arch }} ${{ matrix.prebuild && '- prebuild' }}
|
|
||||||
runs-on: macos-m1
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- nodejs_version: 14
|
|
||||||
nodejs_arch: x64
|
|
||||||
- nodejs_version: 18
|
|
||||||
nodejs_arch: arm64
|
|
||||||
prebuild: true
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: /usr/bin/arch -arch arm64e /bin/bash -l {0}
|
|
||||||
steps:
|
|
||||||
- name: Dependencies (Node.js)
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.nodejs_version }}
|
|
||||||
architecture: ${{ matrix.nodejs_arch }}
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Install
|
|
||||||
run: npm install --build-from-source --unsafe-perm
|
|
||||||
- name: Test
|
|
||||||
run: npm test
|
|
||||||
- name: Prebuild
|
|
||||||
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
|
|
||||||
env:
|
|
||||||
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: npx prebuild --runtime napi --target 7
|
|
||||||
166
.github/workflows/ci.yml
vendored
166
.github/workflows/ci.yml
vendored
@@ -1,13 +1,13 @@
|
|||||||
name: CI (GitHub)
|
name: CI
|
||||||
on:
|
on:
|
||||||
- push
|
- push
|
||||||
- pull_request
|
- pull_request
|
||||||
permissions: {}
|
permissions: {}
|
||||||
jobs:
|
jobs:
|
||||||
CI:
|
github-runner:
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # for npx prebuild to make release
|
contents: write
|
||||||
name: ${{ matrix.container || matrix.os }} - Node.js ${{ matrix.nodejs_version }} ${{ matrix.nodejs_arch }} ${{ matrix.prebuild && '- prebuild' }}
|
name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} ${{ matrix.prebuild && '- prebuild' }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
container: ${{ matrix.container }}
|
container: ${{ matrix.container }}
|
||||||
strategy:
|
strategy:
|
||||||
@@ -15,57 +15,63 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-22.04
|
||||||
container: centos:7
|
container: rockylinux:8
|
||||||
nodejs_version: 14
|
nodejs_version: "^18.17.0"
|
||||||
|
nodejs_version_major: 18
|
||||||
|
platform: linux-x64
|
||||||
prebuild: true
|
prebuild: true
|
||||||
- os: ubuntu-22.04
|
|
||||||
container: centos:7
|
|
||||||
nodejs_version: 16
|
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-22.04
|
||||||
container: rockylinux:8
|
container: rockylinux:8
|
||||||
nodejs_version: 18
|
nodejs_version: "^20.3.0"
|
||||||
|
nodejs_version_major: 20
|
||||||
|
platform: linux-x64
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-22.04
|
||||||
container: node:14-alpine3.12
|
container: node:18-alpine3.17
|
||||||
|
nodejs_version_major: 18
|
||||||
|
platform: linuxmusl-x64
|
||||||
prebuild: true
|
prebuild: true
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-22.04
|
||||||
container: node:16-alpine3.12
|
container: node:20-alpine3.18
|
||||||
- os: ubuntu-22.04
|
nodejs_version_major: 20
|
||||||
container: node:18-alpine3.14
|
platform: linuxmusl-x64
|
||||||
- os: macos-11
|
- os: macos-11
|
||||||
nodejs_version: 14
|
nodejs_arch: x64
|
||||||
|
nodejs_version: "^18.17.0"
|
||||||
|
nodejs_version_major: 18
|
||||||
|
platform: darwin-x64
|
||||||
prebuild: true
|
prebuild: true
|
||||||
nodejs_arch: x64
|
|
||||||
- os: macos-11
|
- os: macos-11
|
||||||
nodejs_version: 16
|
|
||||||
nodejs_arch: x64
|
|
||||||
- os: macos-11
|
|
||||||
nodejs_version: 18
|
|
||||||
nodejs_arch: x64
|
nodejs_arch: x64
|
||||||
|
nodejs_version: "^20.3.0"
|
||||||
|
nodejs_version_major: 20
|
||||||
|
platform: darwin-x64
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
nodejs_version: 14
|
|
||||||
nodejs_arch: x86
|
nodejs_arch: x86
|
||||||
|
nodejs_version: "^18.17.0"
|
||||||
|
nodejs_version_major: 18
|
||||||
|
platform: win32-ia32
|
||||||
prebuild: true
|
prebuild: true
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
nodejs_version: 16
|
|
||||||
nodejs_arch: x86
|
nodejs_arch: x86
|
||||||
|
nodejs_version: "^20.3.0"
|
||||||
|
nodejs_version_major: 20
|
||||||
|
platform: win32-ia32
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
nodejs_version: 18
|
|
||||||
nodejs_arch: x86
|
|
||||||
- os: windows-2019
|
|
||||||
nodejs_version: 14
|
|
||||||
nodejs_arch: x64
|
nodejs_arch: x64
|
||||||
|
nodejs_version: "^18.17.0"
|
||||||
|
nodejs_version_major: 18
|
||||||
|
platform: win32-x64
|
||||||
prebuild: true
|
prebuild: true
|
||||||
- os: windows-2019
|
- os: windows-2019
|
||||||
nodejs_version: 16
|
|
||||||
nodejs_arch: x64
|
|
||||||
- os: windows-2019
|
|
||||||
nodejs_version: 18
|
|
||||||
nodejs_arch: x64
|
nodejs_arch: x64
|
||||||
|
nodejs_version: "^20.3.0"
|
||||||
|
nodejs_version_major: 20
|
||||||
|
platform: win32-x64
|
||||||
steps:
|
steps:
|
||||||
- name: Dependencies (Linux glibc)
|
- name: Dependencies (Linux glibc)
|
||||||
if: contains(matrix.container, 'centos')
|
if: contains(matrix.container, 'centos')
|
||||||
run: |
|
run: |
|
||||||
curl -sL https://rpm.nodesource.com/setup_${{ matrix.nodejs_version }}.x | bash -
|
yum install -y https://rpm.nodesource.com/pub_${{ matrix.nodejs_version_major }}.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm
|
||||||
yum install -y https://packages.endpointdev.com/rhel/7/os/x86_64/endpoint-repo.x86_64.rpm
|
yum install -y https://packages.endpointdev.com/rhel/7/os/x86_64/endpoint-repo.x86_64.rpm
|
||||||
yum install -y centos-release-scl
|
yum install -y centos-release-scl
|
||||||
yum install -y devtoolset-11-gcc-c++ make git python3 nodejs fontconfig google-noto-sans-fonts
|
yum install -y devtoolset-11-gcc-c++ make git python3 nodejs fontconfig google-noto-sans-fonts
|
||||||
@@ -73,17 +79,18 @@ jobs:
|
|||||||
- name: Dependencies (Rocky Linux glibc)
|
- name: Dependencies (Rocky Linux glibc)
|
||||||
if: contains(matrix.container, 'rockylinux')
|
if: contains(matrix.container, 'rockylinux')
|
||||||
run: |
|
run: |
|
||||||
curl -sL https://rpm.nodesource.com/setup_${{ matrix.nodejs_version }}.x | bash -
|
dnf install -y https://rpm.nodesource.com/pub_${{ matrix.nodejs_version_major }}.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm
|
||||||
dnf install -y gcc-toolset-11-gcc-c++ make git python3 nodejs fontconfig google-noto-sans-fonts
|
dnf install -y --setopt=nodesource-nodejs.module_hotfixes=1 nodejs
|
||||||
|
dnf install -y gcc-toolset-11-gcc-c++ make git python3 fontconfig google-noto-sans-fonts
|
||||||
echo "/opt/rh/gcc-toolset-11/root/usr/bin" >> $GITHUB_PATH
|
echo "/opt/rh/gcc-toolset-11/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
|
||||||
- name: Dependencies (Python 3.10 - macOS, Windows)
|
- name: Dependencies (Python 3.11 - macOS, Windows)
|
||||||
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
|
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.11'
|
||||||
- name: Dependencies (Node.js - macOS, Windows)
|
- name: Dependencies (Node.js - macOS, Windows)
|
||||||
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
|
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
@@ -91,16 +98,93 @@ jobs:
|
|||||||
node-version: ${{ matrix.nodejs_version }}
|
node-version: ${{ matrix.nodejs_version }}
|
||||||
architecture: ${{ matrix.nodejs_arch }}
|
architecture: ${{ matrix.nodejs_arch }}
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Fix working directory ownership
|
|
||||||
if: matrix.container
|
|
||||||
run: chown root.root .
|
|
||||||
- name: Install
|
- name: Install
|
||||||
run: npm install --build-from-source --unsafe-perm
|
run: npm install --build-from-source
|
||||||
- name: Test
|
- name: Test
|
||||||
run: npm test
|
run: npm test
|
||||||
|
- name: Test packaging
|
||||||
|
run: |
|
||||||
|
npm run package-from-local-build
|
||||||
|
npm pkg set "optionalDependencies.@sharpen/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}"
|
||||||
|
npm run clean
|
||||||
|
npm install --ignore-scripts
|
||||||
|
npm test
|
||||||
- name: Prebuild
|
- name: Prebuild
|
||||||
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
|
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
|
||||||
env:
|
env:
|
||||||
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
|
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: npx prebuild --runtime napi --target 7
|
run: npx prebuild
|
||||||
|
github-runner-qemu:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
name: linux-arm - Node.js 18 - prebuild
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: uraimo/run-on-arch-action@v2
|
||||||
|
with:
|
||||||
|
arch: armv6
|
||||||
|
distro: buster
|
||||||
|
env: |
|
||||||
|
nodejs_version: "18.17.0"
|
||||||
|
prebuild_upload: "${{ startsWith(github.ref, 'refs/tags/') && secrets.GITHUB_TOKEN || '' }}"
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y fontconfig fonts-noto-core g++ git libatomic1 make python3 xz-utils
|
||||||
|
mkdir /opt/nodejs
|
||||||
|
curl --silent https://unofficial-builds.nodejs.org/download/release/v${nodejs_version}/node-v${nodejs_version}-linux-armv6l.tar.xz | tar xJC /opt/nodejs --strip-components=1
|
||||||
|
export PATH=$PATH:/opt/nodejs/bin
|
||||||
|
npm install --build-from-source
|
||||||
|
npx mocha --no-config --spec=test/unit/io.js
|
||||||
|
npm run package-from-local-build
|
||||||
|
npm pkg set "optionalDependencies.@sharpen/sharp-linux-arm=file:./npm/linux-arm"
|
||||||
|
npm run clean
|
||||||
|
npm install --ignore-scripts
|
||||||
|
npx mocha --no-config --spec=test/unit/io.js --timeout=30000
|
||||||
|
[[ -n $prebuild_upload ]] && npx prebuild || true
|
||||||
|
macstadium-runner:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} ${{ matrix.prebuild && '- prebuild' }}
|
||||||
|
runs-on: macos-m1
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- nodejs_arch: x64
|
||||||
|
nodejs_version: "^18.17.0"
|
||||||
|
nodejs_version_major: 18
|
||||||
|
platform: darwin-x64
|
||||||
|
- nodejs_arch: arm64
|
||||||
|
nodejs_version: "^18.17.0"
|
||||||
|
nodejs_version_major: 18
|
||||||
|
platform: darwin-arm64
|
||||||
|
prebuild: true
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: /usr/bin/arch -arch arm64e /bin/bash -l {0}
|
||||||
|
steps:
|
||||||
|
- name: Dependencies (Node.js)
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.nodejs_version }}
|
||||||
|
architecture: ${{ matrix.nodejs_arch }}
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install
|
||||||
|
run: npm install --build-from-source
|
||||||
|
- name: Test
|
||||||
|
run: npm test
|
||||||
|
- name: Test packaging
|
||||||
|
run: |
|
||||||
|
npm run package-from-local-build
|
||||||
|
npm pkg set "optionalDependencies.@sharpen/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}"
|
||||||
|
npm run clean
|
||||||
|
npm install --ignore-scripts
|
||||||
|
npm test
|
||||||
|
- name: Prebuild
|
||||||
|
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
|
||||||
|
env:
|
||||||
|
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: npx prebuild
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,8 @@
|
|||||||
build
|
build
|
||||||
node_modules
|
node_modules
|
||||||
/coverage
|
/coverage
|
||||||
|
npm/*/*
|
||||||
|
!npm/*/package.json
|
||||||
test/bench/node_modules
|
test/bench/node_modules
|
||||||
test/fixtures/output*
|
test/fixtures/output*
|
||||||
test/fixtures/vips-properties.xml
|
test/fixtures/vips-properties.xml
|
||||||
@@ -9,7 +11,6 @@ test/saliency/report.json
|
|||||||
test/saliency/Image*
|
test/saliency/Image*
|
||||||
test/saliency/[Uu]serData*
|
test/saliency/[Uu]serData*
|
||||||
!test/saliency/userData.js
|
!test/saliency/userData.js
|
||||||
vendor
|
|
||||||
.gitattributes
|
.gitattributes
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
"include-regex": "(sharp-.+\\.node|libvips-cpp\\.dll)",
|
"runtime": "napi",
|
||||||
|
"include-regex": "(sharp-.+\\.node|libvips-.+\\.dll|libglib-.+\\.dll|libgobject-.+\\.dll)",
|
||||||
|
"prerelease": true,
|
||||||
"strip": true
|
"strip": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Lanczos resampling ensures quality is not sacrificed for speed.
|
|||||||
As well as image resizing, operations such as
|
As well as image resizing, operations such as
|
||||||
rotation, extraction, compositing and gamma correction are available.
|
rotation, extraction, compositing and gamma correction are available.
|
||||||
|
|
||||||
Most modern macOS, Windows and Linux systems running Node.js >= 14.15.0
|
Most modern macOS, Windows and Linux systems running Node.js >= 18.17.0
|
||||||
do not require any additional install or runtime dependencies.
|
do not require any additional install or runtime dependencies.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
80
binding.gyp
80
binding.gyp
@@ -1,8 +1,13 @@
|
|||||||
|
# Copyright 2013 Lovell Fuller and others.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
{
|
{
|
||||||
'variables': {
|
'variables': {
|
||||||
'vips_version': '<!(node -p "require(\'./lib/libvips\').minimumLibvipsVersion")',
|
'vips_version': '<!(node -p "require(\'./lib/libvips\').minimumLibvipsVersion")',
|
||||||
'platform_and_arch': '<!(node -p "require(\'./lib/platform\')()")',
|
'platform_and_arch': '<!(node -p "require(\'./lib/libvips\').buildPlatformArch()")',
|
||||||
'sharp_vendor_dir': './vendor/<(vips_version)/<(platform_and_arch)'
|
'sharp_libvips_include_dir': '<!(node -p "require(\'./lib/libvips\').buildSharpLibvipsIncludeDir()")',
|
||||||
|
'sharp_libvips_cplusplus_dir': '<!(node -p "require(\'./lib/libvips\').buildSharpLibvipsCPlusPlusDir()")',
|
||||||
|
'sharp_libvips_lib_dir': '<!(node -p "require(\'./lib/libvips\').buildSharpLibvipsLibDir()")'
|
||||||
},
|
},
|
||||||
'targets': [{
|
'targets': [{
|
||||||
'target_name': 'libvips-cpp',
|
'target_name': 'libvips-cpp',
|
||||||
@@ -15,19 +20,19 @@
|
|||||||
'_ALLOW_KEYWORD_MACROS'
|
'_ALLOW_KEYWORD_MACROS'
|
||||||
],
|
],
|
||||||
'sources': [
|
'sources': [
|
||||||
'src/libvips/cplusplus/VConnection.cpp',
|
'<(sharp_libvips_cplusplus_dir)/VConnection.cpp',
|
||||||
'src/libvips/cplusplus/VError.cpp',
|
'<(sharp_libvips_cplusplus_dir)/VError.cpp',
|
||||||
'src/libvips/cplusplus/VImage.cpp',
|
'<(sharp_libvips_cplusplus_dir)/VImage.cpp',
|
||||||
'src/libvips/cplusplus/VInterpolate.cpp',
|
'<(sharp_libvips_cplusplus_dir)/VInterpolate.cpp',
|
||||||
'src/libvips/cplusplus/VRegion.cpp'
|
'<(sharp_libvips_cplusplus_dir)/VRegion.cpp'
|
||||||
],
|
],
|
||||||
'include_dirs': [
|
'include_dirs': [
|
||||||
'<(sharp_vendor_dir)/include',
|
'<(sharp_libvips_include_dir)',
|
||||||
'<(sharp_vendor_dir)/include/glib-2.0',
|
'<(sharp_libvips_include_dir)/glib-2.0',
|
||||||
'<(sharp_vendor_dir)/lib/glib-2.0/include'
|
'<(sharp_libvips_lib_dir)/glib-2.0/include'
|
||||||
],
|
],
|
||||||
'link_settings': {
|
'link_settings': {
|
||||||
'library_dirs': ['<(sharp_vendor_dir)/lib'],
|
'library_dirs': ['<(sharp_libvips_lib_dir)'],
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'libvips.lib',
|
'libvips.lib',
|
||||||
'libglib-2.0.lib',
|
'libglib-2.0.lib',
|
||||||
@@ -70,7 +75,7 @@
|
|||||||
}, {
|
}, {
|
||||||
'target_name': 'sharp-<(platform_and_arch)',
|
'target_name': 'sharp-<(platform_and_arch)',
|
||||||
'defines': [
|
'defines': [
|
||||||
'NAPI_VERSION=7',
|
'NAPI_VERSION=9',
|
||||||
'NODE_ADDON_API_DISABLE_DEPRECATED',
|
'NODE_ADDON_API_DISABLE_DEPRECATED',
|
||||||
'NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS'
|
'NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS'
|
||||||
],
|
],
|
||||||
@@ -79,7 +84,6 @@
|
|||||||
'libvips-cpp'
|
'libvips-cpp'
|
||||||
],
|
],
|
||||||
'variables': {
|
'variables': {
|
||||||
'runtime_link%': 'shared',
|
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['OS != "win"', {
|
['OS != "win"', {
|
||||||
'pkg_config_path': '<!(node -p "require(\'./lib/libvips\').pkgConfigPath()")',
|
'pkg_config_path': '<!(node -p "require(\'./lib/libvips\').pkgConfigPath()")',
|
||||||
@@ -106,12 +110,8 @@
|
|||||||
['use_global_libvips == "true"', {
|
['use_global_libvips == "true"', {
|
||||||
# Use pkg-config for include and lib
|
# Use pkg-config for include and lib
|
||||||
'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s\/-I//g)'],
|
'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s\/-I//g)'],
|
||||||
|
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)'],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['runtime_link == "static"', {
|
|
||||||
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips-cpp)']
|
|
||||||
}, {
|
|
||||||
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)']
|
|
||||||
}],
|
|
||||||
['OS == "linux"', {
|
['OS == "linux"', {
|
||||||
'defines': [
|
'defines': [
|
||||||
# Inspect libvips-cpp.so to determine which C++11 ABI version was used and set _GLIBCXX_USE_CXX11_ABI accordingly. This is quite horrible.
|
# Inspect libvips-cpp.so to determine which C++11 ABI version was used and set _GLIBCXX_USE_CXX11_ABI accordingly. This is quite horrible.
|
||||||
@@ -122,9 +122,9 @@
|
|||||||
}, {
|
}, {
|
||||||
# Use pre-built libvips stored locally within node_modules
|
# Use pre-built libvips stored locally within node_modules
|
||||||
'include_dirs': [
|
'include_dirs': [
|
||||||
'<(sharp_vendor_dir)/include',
|
'<(sharp_libvips_include_dir)',
|
||||||
'<(sharp_vendor_dir)/include/glib-2.0',
|
'<(sharp_libvips_include_dir)/glib-2.0',
|
||||||
'<(sharp_vendor_dir)/lib/glib-2.0/include'
|
'<(sharp_libvips_lib_dir)/glib-2.0/include'
|
||||||
],
|
],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['OS == "win"', {
|
['OS == "win"', {
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
'_FILE_OFFSET_BITS=64'
|
'_FILE_OFFSET_BITS=64'
|
||||||
],
|
],
|
||||||
'link_settings': {
|
'link_settings': {
|
||||||
'library_dirs': ['<(sharp_vendor_dir)/lib'],
|
'library_dirs': ['<(sharp_libvips_lib_dir)'],
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'libvips.lib',
|
'libvips.lib',
|
||||||
'libglib-2.0.lib',
|
'libglib-2.0.lib',
|
||||||
@@ -143,7 +143,9 @@
|
|||||||
}],
|
}],
|
||||||
['OS == "mac"', {
|
['OS == "mac"', {
|
||||||
'link_settings': {
|
'link_settings': {
|
||||||
'library_dirs': ['../<(sharp_vendor_dir)/lib'],
|
'library_dirs': [
|
||||||
|
'<(sharp_libvips_lib_dir)'
|
||||||
|
],
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'libvips-cpp.42.dylib'
|
'libvips-cpp.42.dylib'
|
||||||
]
|
]
|
||||||
@@ -151,7 +153,9 @@
|
|||||||
'xcode_settings': {
|
'xcode_settings': {
|
||||||
'OTHER_LDFLAGS': [
|
'OTHER_LDFLAGS': [
|
||||||
# Ensure runtime linking is relative to sharp.node
|
# Ensure runtime linking is relative to sharp.node
|
||||||
'-Wl,-rpath,\'@loader_path/../../<(sharp_vendor_dir)/lib\''
|
'-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'',
|
||||||
|
'-Wl,-rpath,\'@loader_path/../../node_modules/@sharpen/sharp-libvips-<(platform_and_arch)/lib\'',
|
||||||
|
'-Wl,-rpath,\'@loader_path/../../../node_modules/@sharpen/sharp-libvips-<(platform_and_arch)/lib\''
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
@@ -160,13 +164,19 @@
|
|||||||
'_GLIBCXX_USE_CXX11_ABI=1'
|
'_GLIBCXX_USE_CXX11_ABI=1'
|
||||||
],
|
],
|
||||||
'link_settings': {
|
'link_settings': {
|
||||||
'library_dirs': ['../<(sharp_vendor_dir)/lib'],
|
'library_dirs': [
|
||||||
|
'<(sharp_libvips_lib_dir)'
|
||||||
|
],
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'-l:libvips-cpp.so.42'
|
'-l:libvips-cpp.so.42'
|
||||||
],
|
],
|
||||||
'ldflags': [
|
'ldflags': [
|
||||||
# Ensure runtime linking is relative to sharp.node
|
# Ensure runtime linking is relative to sharp.node
|
||||||
'-Wl,-s -Wl,--disable-new-dtags -Wl,-rpath=\'$$ORIGIN/../../<(sharp_vendor_dir)/lib\''
|
'-Wl,-s',
|
||||||
|
'-Wl,--disable-new-dtags',
|
||||||
|
'-Wl,-rpath=\'$$ORIGIN/../../sharp-libvips-<(platform_and_arch)/lib\'',
|
||||||
|
'-Wl,-rpath=\'$$ORIGIN/../../node_modules/@sharpen/sharp-libvips-<(platform_and_arch)/lib\'',
|
||||||
|
'-Wl,-rpath=\'$$ORIGIN/../../../node_modules/@sharpen/sharp-libvips-<(platform_and_arch)/lib\''
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@@ -232,5 +242,23 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
'target_name': 'copy-dll',
|
||||||
|
'type': 'none',
|
||||||
|
'dependencies': [
|
||||||
|
'sharp-<(platform_and_arch)'
|
||||||
|
],
|
||||||
|
'conditions': [
|
||||||
|
['OS == "win"', {
|
||||||
|
'copies': [{
|
||||||
|
'destination': 'build/Release',
|
||||||
|
'files': [
|
||||||
|
'<(sharp_libvips_lib_dir)/libvips-42.dll',
|
||||||
|
'<(sharp_libvips_lib_dir)/libglib-2.0-0.dll',
|
||||||
|
'<(sharp_libvips_lib_dir)/libgobject-2.0-0.dll'
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
]
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
## removeAlpha
|
## removeAlpha
|
||||||
|
> removeAlpha() ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
|
Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
|
||||||
|
|
||||||
See also [flatten](/api-operation#flatten).
|
See also [flatten](/api-operation#flatten).
|
||||||
@@ -15,6 +17,8 @@ sharp('rgba.png')
|
|||||||
|
|
||||||
|
|
||||||
## ensureAlpha
|
## ensureAlpha
|
||||||
|
> ensureAlpha([alpha]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Ensure the output image has an alpha transparency channel.
|
Ensure the output image has an alpha transparency channel.
|
||||||
If missing, the added alpha channel will have the specified
|
If missing, the added alpha channel will have the specified
|
||||||
transparency level, defaulting to fully-opaque (1).
|
transparency level, defaulting to fully-opaque (1).
|
||||||
@@ -48,6 +52,8 @@ const rgba = await sharp(rgb)
|
|||||||
|
|
||||||
|
|
||||||
## extractChannel
|
## extractChannel
|
||||||
|
> extractChannel(channel) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Extract a single channel from a multi-channel image.
|
Extract a single channel from a multi-channel image.
|
||||||
|
|
||||||
|
|
||||||
@@ -78,6 +84,8 @@ const [red1, red2, ...] = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## joinChannel
|
## joinChannel
|
||||||
|
> joinChannel(images, options) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Join one or more channels to the image.
|
Join one or more channels to the image.
|
||||||
The meaning of the added channels depends on the output colourspace, set with `toColourspace()`.
|
The meaning of the added channels depends on the output colourspace, set with `toColourspace()`.
|
||||||
By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
||||||
@@ -102,6 +110,8 @@ For raw pixel input, the `options` object should contain a `raw` attribute, whic
|
|||||||
|
|
||||||
|
|
||||||
## bandbool
|
## bandbool
|
||||||
|
> bandbool(boolOp) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
|
Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
## tint
|
## tint
|
||||||
|
> tint(rgb) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Tint the image using the provided chroma while preserving the image luminance.
|
Tint the image using the provided chroma while preserving the image luminance.
|
||||||
An alpha channel may be present and will be unchanged by the operation.
|
An alpha channel may be present and will be unchanged by the operation.
|
||||||
|
|
||||||
@@ -21,6 +23,8 @@ const output = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## greyscale
|
## greyscale
|
||||||
|
> greyscale([greyscale]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Convert to 8-bit greyscale; 256 shades of grey.
|
Convert to 8-bit greyscale; 256 shades of grey.
|
||||||
This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
|
This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
|
||||||
By default the output image will be web-friendly sRGB and contain three (identical) color channels.
|
By default the output image will be web-friendly sRGB and contain three (identical) color channels.
|
||||||
@@ -41,6 +45,8 @@ const output = await sharp(input).greyscale().toBuffer();
|
|||||||
|
|
||||||
|
|
||||||
## grayscale
|
## grayscale
|
||||||
|
> grayscale([grayscale]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Alternative spelling of `greyscale`.
|
Alternative spelling of `greyscale`.
|
||||||
|
|
||||||
|
|
||||||
@@ -52,6 +58,8 @@ Alternative spelling of `greyscale`.
|
|||||||
|
|
||||||
|
|
||||||
## pipelineColourspace
|
## pipelineColourspace
|
||||||
|
> pipelineColourspace([colourspace]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Set the pipeline colourspace.
|
Set the pipeline colourspace.
|
||||||
|
|
||||||
The input image will be converted to the provided colourspace at the start of the pipeline.
|
The input image will be converted to the provided colourspace at the start of the pipeline.
|
||||||
@@ -82,6 +90,8 @@ await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## pipelineColorspace
|
## pipelineColorspace
|
||||||
|
> pipelineColorspace([colorspace]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Alternative spelling of `pipelineColourspace`.
|
Alternative spelling of `pipelineColourspace`.
|
||||||
|
|
||||||
|
|
||||||
@@ -97,6 +107,8 @@ Alternative spelling of `pipelineColourspace`.
|
|||||||
|
|
||||||
|
|
||||||
## toColourspace
|
## toColourspace
|
||||||
|
> toColourspace([colourspace]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Set the output colourspace.
|
Set the output colourspace.
|
||||||
By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
||||||
|
|
||||||
@@ -120,6 +132,8 @@ await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## toColorspace
|
## toColorspace
|
||||||
|
> toColorspace([colorspace]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Alternative spelling of `toColourspace`.
|
Alternative spelling of `toColourspace`.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
## composite
|
## composite
|
||||||
|
> composite(images) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Composite image(s) over the processed (resized, extracted etc.) image.
|
Composite image(s) over the processed (resized, extracted etc.) image.
|
||||||
|
|
||||||
The images to composite must be the same size or smaller than the processed image.
|
The images to composite must be the same size or smaller than the processed image.
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
## Sharp
|
## Sharp
|
||||||
|
> Sharp
|
||||||
|
|
||||||
|
|
||||||
**Emits**: <code>Sharp#event:info</code>, <code>Sharp#event:warning</code>
|
**Emits**: <code>Sharp#event:info</code>, <code>Sharp#event:warning</code>
|
||||||
<a name="new_Sharp_new"></a>
|
<a name="new_Sharp_new"></a>
|
||||||
|
|
||||||
### new
|
### new
|
||||||
|
> new Sharp([input], [options])
|
||||||
|
|
||||||
Constructor factory to create an instance of `sharp`, to which further methods are chained.
|
Constructor factory to create an instance of `sharp`, to which further methods are chained.
|
||||||
|
|
||||||
JPEG, PNG, WebP, GIF, AVIF or TIFF format image data can be streamed out from this object.
|
JPEG, PNG, WebP, GIF, AVIF or TIFF format image data can be streamed out from this object.
|
||||||
@@ -159,6 +163,8 @@ await sharp({
|
|||||||
|
|
||||||
|
|
||||||
## clone
|
## clone
|
||||||
|
> clone() ⇒ [<code>Sharp</code>](#Sharp)
|
||||||
|
|
||||||
Take a "snapshot" of the Sharp instance, returning a new instance.
|
Take a "snapshot" of the Sharp instance, returning a new instance.
|
||||||
Cloned instances inherit the input of their parent instance.
|
Cloned instances inherit the input of their parent instance.
|
||||||
This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream.
|
This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
## metadata
|
## metadata
|
||||||
|
> metadata([callback]) ⇒ <code>Promise.<Object></code> \| <code>Sharp</code>
|
||||||
|
|
||||||
Fast access to (uncached) image metadata without decoding any compressed pixel data.
|
Fast access to (uncached) image metadata without decoding any compressed pixel data.
|
||||||
|
|
||||||
This is read from the header of the input image.
|
This is read from the header of the input image.
|
||||||
@@ -22,6 +24,7 @@ A `Promise` is returned when `callback` is not provided.
|
|||||||
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
- `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
- `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
||||||
- `pageHeight`: Number of pixels high each page in a multi-page image will be.
|
- `pageHeight`: Number of pixels high each page in a multi-page image will be.
|
||||||
|
- `paletteBitDepth`: Bit depth of palette-based image (GIF, PNG).
|
||||||
- `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
- `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
||||||
- `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
- `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
||||||
- `pagePrimary`: Number of the primary page in a HEIF image
|
- `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
@@ -80,6 +83,8 @@ function getNormalSize({ width, height, orientation }) {
|
|||||||
|
|
||||||
|
|
||||||
## stats
|
## stats
|
||||||
|
> stats([callback]) ⇒ <code>Promise.<Object></code>
|
||||||
|
|
||||||
Access to pixel-derived image statistics for every channel in the image.
|
Access to pixel-derived image statistics for every channel in the image.
|
||||||
A `Promise` is returned when `callback` is not provided.
|
A `Promise` is returned when `callback` is not provided.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
## rotate
|
## rotate
|
||||||
|
> rotate([angle], [options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Rotate the output image by either an explicit angle
|
Rotate the output image by either an explicit angle
|
||||||
or auto-orient based on the EXIF `Orientation` tag.
|
or auto-orient based on the EXIF `Orientation` tag.
|
||||||
|
|
||||||
@@ -11,7 +13,7 @@ the background colour can be provided with the `background` option.
|
|||||||
If no angle is provided, it is determined from the EXIF data.
|
If no angle is provided, it is determined from the EXIF data.
|
||||||
Mirroring is supported and may infer the use of a flip operation.
|
Mirroring is supported and may infer the use of a flip operation.
|
||||||
|
|
||||||
The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
|
The use of `rotate` without an angle will remove the EXIF `Orientation` tag, if any.
|
||||||
|
|
||||||
Only one rotation can occur per pipeline.
|
Only one rotation can occur per pipeline.
|
||||||
Previous calls to `rotate` in the same pipeline will be ignored.
|
Previous calls to `rotate` in the same pipeline will be ignored.
|
||||||
@@ -57,11 +59,11 @@ const resizeThenRotate = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## flip
|
## flip
|
||||||
|
> flip([flip]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Mirror the image vertically (up-down) about the x-axis.
|
Mirror the image vertically (up-down) about the x-axis.
|
||||||
This always occurs before rotation, if any.
|
This always occurs before rotation, if any.
|
||||||
|
|
||||||
The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
|
||||||
|
|
||||||
This operation does not work correctly with multi-page images.
|
This operation does not work correctly with multi-page images.
|
||||||
|
|
||||||
|
|
||||||
@@ -77,11 +79,11 @@ const output = await sharp(input).flip().toBuffer();
|
|||||||
|
|
||||||
|
|
||||||
## flop
|
## flop
|
||||||
|
> flop([flop]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Mirror the image horizontally (left-right) about the y-axis.
|
Mirror the image horizontally (left-right) about the y-axis.
|
||||||
This always occurs before rotation, if any.
|
This always occurs before rotation, if any.
|
||||||
|
|
||||||
The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
| Param | Type | Default |
|
| Param | Type | Default |
|
||||||
@@ -95,6 +97,8 @@ const output = await sharp(input).flop().toBuffer();
|
|||||||
|
|
||||||
|
|
||||||
## affine
|
## affine
|
||||||
|
> affine(matrix, [options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.
|
Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.
|
||||||
|
|
||||||
You must provide an array of length 4 or a 2x2 affine transformation matrix.
|
You must provide an array of length 4 or a 2x2 affine transformation matrix.
|
||||||
@@ -146,6 +150,8 @@ inputStream
|
|||||||
|
|
||||||
|
|
||||||
## sharpen
|
## sharpen
|
||||||
|
> sharpen([options], [flat], [jagged]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Sharpen the image.
|
Sharpen the image.
|
||||||
|
|
||||||
When used without parameters, performs a fast, mild sharpen of the output image.
|
When used without parameters, performs a fast, mild sharpen of the output image.
|
||||||
@@ -197,6 +203,8 @@ const data = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## median
|
## median
|
||||||
|
> median([size]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Apply median filter.
|
Apply median filter.
|
||||||
When used without parameters the default window is 3x3.
|
When used without parameters the default window is 3x3.
|
||||||
|
|
||||||
@@ -221,6 +229,8 @@ const output = await sharp(input).median(5).toBuffer();
|
|||||||
|
|
||||||
|
|
||||||
## blur
|
## blur
|
||||||
|
> blur([sigma]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Blur the image.
|
Blur the image.
|
||||||
|
|
||||||
When used without parameters, performs a fast 3x3 box blur (equivalent to a box linear filter).
|
When used without parameters, performs a fast 3x3 box blur (equivalent to a box linear filter).
|
||||||
@@ -252,6 +262,8 @@ const gaussianBlurred = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## flatten
|
## flatten
|
||||||
|
> flatten([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
||||||
|
|
||||||
See also [removeAlpha](/api-channel#removealpha).
|
See also [removeAlpha](/api-channel#removealpha).
|
||||||
@@ -272,6 +284,8 @@ await sharp(rgbaInput)
|
|||||||
|
|
||||||
|
|
||||||
## unflatten
|
## unflatten
|
||||||
|
> unflatten()
|
||||||
|
|
||||||
Ensure the image has an alpha channel
|
Ensure the image has an alpha channel
|
||||||
with all white pixel values made fully transparent.
|
with all white pixel values made fully transparent.
|
||||||
|
|
||||||
@@ -297,6 +311,8 @@ await sharp(rgbInput)
|
|||||||
|
|
||||||
|
|
||||||
## gamma
|
## gamma
|
||||||
|
> gamma([gamma], [gammaOut]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
|
Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
|
||||||
then increasing the encoding (brighten) post-resize at a factor of `gamma`.
|
then increasing the encoding (brighten) post-resize at a factor of `gamma`.
|
||||||
This can improve the perceived brightness of a resized image in non-linear colour spaces.
|
This can improve the perceived brightness of a resized image in non-linear colour spaces.
|
||||||
@@ -319,6 +335,8 @@ Supply a second argument to use a different output gamma value, otherwise the fi
|
|||||||
|
|
||||||
|
|
||||||
## negate
|
## negate
|
||||||
|
> negate([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Produce the "negative" of the image.
|
Produce the "negative" of the image.
|
||||||
|
|
||||||
|
|
||||||
@@ -343,6 +361,8 @@ const output = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## normalise
|
## normalise
|
||||||
|
> normalise([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Enhance output image contrast by stretching its luminance to cover a full dynamic range.
|
Enhance output image contrast by stretching its luminance to cover a full dynamic range.
|
||||||
|
|
||||||
Uses a histogram-based approach, taking a default range of 1% to 99% to reduce sensitivity to noise at the extremes.
|
Uses a histogram-based approach, taking a default range of 1% to 99% to reduce sensitivity to noise at the extremes.
|
||||||
@@ -373,6 +393,8 @@ const output = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## normalize
|
## normalize
|
||||||
|
> normalize([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Alternative spelling of normalise.
|
Alternative spelling of normalise.
|
||||||
|
|
||||||
|
|
||||||
@@ -392,6 +414,8 @@ const output = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## clahe
|
## clahe
|
||||||
|
> clahe(options) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Perform contrast limiting adaptive histogram equalization
|
Perform contrast limiting adaptive histogram equalization
|
||||||
[CLAHE](https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE).
|
[CLAHE](https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE).
|
||||||
|
|
||||||
@@ -423,6 +447,8 @@ const output = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## convolve
|
## convolve
|
||||||
|
> convolve(kernel) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Convolve the image with the specified kernel.
|
Convolve the image with the specified kernel.
|
||||||
|
|
||||||
|
|
||||||
@@ -457,6 +483,8 @@ sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## threshold
|
## threshold
|
||||||
|
> threshold([threshold], [options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Any pixel value greater than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
|
Any pixel value greater than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
|
||||||
|
|
||||||
|
|
||||||
@@ -475,6 +503,8 @@ Any pixel value greater than or equal to the threshold value will be set to 255,
|
|||||||
|
|
||||||
|
|
||||||
## boolean
|
## boolean
|
||||||
|
> boolean(operand, operator, [options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Perform a bitwise boolean operation with operand image.
|
Perform a bitwise boolean operation with operand image.
|
||||||
|
|
||||||
This operation creates an output image where each pixel is the result of
|
This operation creates an output image where each pixel is the result of
|
||||||
@@ -499,6 +529,8 @@ the selected bitwise boolean `operation` between the corresponding pixels of the
|
|||||||
|
|
||||||
|
|
||||||
## linear
|
## linear
|
||||||
|
> linear([a], [b]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Apply the linear formula `a` * input + `b` to the image to adjust image levels.
|
Apply the linear formula `a` * input + `b` to the image to adjust image levels.
|
||||||
|
|
||||||
When a single number is provided, it will be used for all image channels.
|
When a single number is provided, it will be used for all image channels.
|
||||||
@@ -533,6 +565,8 @@ await sharp(rgbInput)
|
|||||||
|
|
||||||
|
|
||||||
## recomb
|
## recomb
|
||||||
|
> recomb(inputMatrix) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Recombine the image with the specified matrix.
|
Recombine the image with the specified matrix.
|
||||||
|
|
||||||
|
|
||||||
@@ -563,6 +597,8 @@ sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## modulate
|
## modulate
|
||||||
|
> modulate([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Transforms the image using brightness, saturation, hue rotation, and lightness.
|
Transforms the image using brightness, saturation, hue rotation, and lightness.
|
||||||
Brightness and lightness both operate on luminance, with the difference being that
|
Brightness and lightness both operate on luminance, with the difference being that
|
||||||
brightness is multiplicative whereas lightness is additive.
|
brightness is multiplicative whereas lightness is additive.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
## toFile
|
## toFile
|
||||||
|
> toFile(fileOut, [callback]) ⇒ <code>Promise.<Object></code>
|
||||||
|
|
||||||
Write output image data to a file.
|
Write output image data to a file.
|
||||||
|
|
||||||
If an explicit output format is not selected, it will be inferred from the extension,
|
If an explicit output format is not selected, it will be inferred from the extension,
|
||||||
@@ -39,6 +41,8 @@ sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## toBuffer
|
## toBuffer
|
||||||
|
> toBuffer([options], [callback]) ⇒ <code>Promise.<Buffer></code>
|
||||||
|
|
||||||
Write output to a Buffer.
|
Write output to a Buffer.
|
||||||
JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
||||||
|
|
||||||
@@ -108,9 +112,11 @@ await sharp(pixelArray, { raw: { width, height, channels } })
|
|||||||
|
|
||||||
|
|
||||||
## withMetadata
|
## withMetadata
|
||||||
|
> withMetadata([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
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.
|
||||||
This will also convert to and add a web-friendly sRGB ICC profile unless a custom
|
This will also convert to and add a web-friendly sRGB ICC profile if appropriate,
|
||||||
output profile is provided.
|
unless a custom output profile is provided.
|
||||||
|
|
||||||
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
||||||
sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
||||||
@@ -167,6 +173,8 @@ const data = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## toFormat
|
## toFormat
|
||||||
|
> toFormat(format, options) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Force output to a given format.
|
Force output to a given format.
|
||||||
|
|
||||||
|
|
||||||
@@ -190,6 +198,8 @@ const data = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## jpeg
|
## jpeg
|
||||||
|
> jpeg([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Use these JPEG options for output image.
|
Use these JPEG options for output image.
|
||||||
|
|
||||||
|
|
||||||
@@ -235,6 +245,8 @@ const data = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## png
|
## png
|
||||||
|
> png([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Use these PNG options for output image.
|
Use these PNG options for output image.
|
||||||
|
|
||||||
By default, PNG output is full colour at 8 or 16 bits per pixel.
|
By default, PNG output is full colour at 8 or 16 bits per pixel.
|
||||||
@@ -278,6 +290,8 @@ const data = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## webp
|
## webp
|
||||||
|
> webp([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Use these WebP options for output image.
|
Use these WebP options for output image.
|
||||||
|
|
||||||
|
|
||||||
@@ -319,6 +333,8 @@ const outputWebp = await sharp(inputWebp, { animated: true })
|
|||||||
|
|
||||||
|
|
||||||
## gif
|
## gif
|
||||||
|
> gif([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Use these GIF options for the output image.
|
Use these GIF options for the output image.
|
||||||
|
|
||||||
The first entry in the palette is reserved for transparency.
|
The first entry in the palette is reserved for transparency.
|
||||||
@@ -378,6 +394,8 @@ await sharp('in.gif', { animated: true })
|
|||||||
|
|
||||||
|
|
||||||
## jp2
|
## jp2
|
||||||
|
> jp2([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Use these JP2 options for output image.
|
Use these JP2 options for output image.
|
||||||
|
|
||||||
Requires libvips compiled with support for OpenJPEG.
|
Requires libvips compiled with support for OpenJPEG.
|
||||||
@@ -420,6 +438,8 @@ const data = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## tiff
|
## tiff
|
||||||
|
> tiff([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Use these TIFF options for output image.
|
Use these TIFF options for output image.
|
||||||
|
|
||||||
The `density` can be set in pixels/inch via [withMetadata](#withmetadata)
|
The `density` can be set in pixels/inch via [withMetadata](#withmetadata)
|
||||||
@@ -461,10 +481,9 @@ sharp('input.svg')
|
|||||||
|
|
||||||
|
|
||||||
## avif
|
## avif
|
||||||
Use these AVIF options for output image.
|
> avif([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Whilst it is possible to create AVIF images smaller than 16x16 pixels,
|
Use these AVIF options for output image.
|
||||||
most web browsers do not display these properly.
|
|
||||||
|
|
||||||
AVIF image sequences are not supported.
|
AVIF image sequences are not supported.
|
||||||
|
|
||||||
@@ -498,6 +517,8 @@ const data = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## heif
|
## heif
|
||||||
|
> heif(options) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Use these HEIF options for output image.
|
Use these HEIF options for output image.
|
||||||
|
|
||||||
Support for patent-encumbered HEIC images using `hevc` compression requires the use of a
|
Support for patent-encumbered HEIC images using `hevc` compression requires the use of a
|
||||||
@@ -512,9 +533,9 @@ globally-installed libvips compiled with support for libheif, libde265 and x265.
|
|||||||
|
|
||||||
| Param | Type | Default | Description |
|
| Param | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| [options] | <code>Object</code> | | output options |
|
| options | <code>Object</code> | | output options |
|
||||||
|
| options.compression | <code>string</code> | | compression format: av1, hevc |
|
||||||
| [options.quality] | <code>number</code> | <code>50</code> | quality, integer 1-100 |
|
| [options.quality] | <code>number</code> | <code>50</code> | quality, integer 1-100 |
|
||||||
| [options.compression] | <code>string</code> | <code>"'av1'"</code> | compression format: av1, hevc |
|
|
||||||
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression |
|
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression |
|
||||||
| [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 9 (slowest) |
|
| [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 9 (slowest) |
|
||||||
| [options.chromaSubsampling] | <code>string</code> | <code>"'4:4:4'"</code> | set to '4:2:0' to use chroma subsampling |
|
| [options.chromaSubsampling] | <code>string</code> | <code>"'4:4:4'"</code> | set to '4:2:0' to use chroma subsampling |
|
||||||
@@ -528,6 +549,8 @@ const data = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## jxl
|
## jxl
|
||||||
|
> jxl([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Use these JPEG-XL (JXL) options for output image.
|
Use these JPEG-XL (JXL) options for output image.
|
||||||
|
|
||||||
This feature is experimental, please do not use in production systems.
|
This feature is experimental, please do not use in production systems.
|
||||||
@@ -557,6 +580,8 @@ Image metadata (EXIF, XMP) is unsupported.
|
|||||||
|
|
||||||
|
|
||||||
## raw
|
## raw
|
||||||
|
> raw([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Force output to be raw, uncompressed pixel data.
|
Force output to be raw, uncompressed pixel data.
|
||||||
Pixel ordering is left-to-right, top-to-bottom, without padding.
|
Pixel ordering is left-to-right, top-to-bottom, without padding.
|
||||||
Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
|
Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
|
||||||
@@ -592,6 +617,8 @@ const data = await sharp('input.png')
|
|||||||
|
|
||||||
|
|
||||||
## tile
|
## tile
|
||||||
|
> tile([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Use tile-based deep zoom (image pyramid) output.
|
Use tile-based deep zoom (image pyramid) output.
|
||||||
|
|
||||||
Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
|
Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
|
||||||
@@ -653,6 +680,8 @@ readableStream
|
|||||||
|
|
||||||
|
|
||||||
## timeout
|
## timeout
|
||||||
|
> timeout(options) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Set a timeout for processing, in seconds.
|
Set a timeout for processing, in seconds.
|
||||||
Use a value of zero to continue processing indefinitely, the default behaviour.
|
Use a value of zero to continue processing indefinitely, the default behaviour.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
## resize
|
## resize
|
||||||
|
> resize([width], [height], [options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Resize image to `width`, `height` or `width x height`.
|
Resize image to `width`, `height` or `width x height`.
|
||||||
|
|
||||||
When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
|
When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
|
||||||
@@ -10,7 +12,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.png">
|
<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">
|
||||||
|
|
||||||
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`.
|
||||||
@@ -146,6 +148,8 @@ const scaleByHalf = await sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## extend
|
## extend
|
||||||
|
> extend(extend) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Extend / pad / extrude one or more edges of the image with either
|
Extend / pad / extrude one or more edges of the image with either
|
||||||
the provided background colour or pixels derived from the image.
|
the provided background colour or pixels derived from the image.
|
||||||
This operation will always occur after resizing and extraction, if any.
|
This operation will always occur after resizing and extraction, if any.
|
||||||
@@ -204,11 +208,13 @@ sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## extract
|
## extract
|
||||||
|
> extract(options) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Extract/crop a region of the image.
|
Extract/crop a region of the image.
|
||||||
|
|
||||||
- Use `extract` before `resize` for pre-resize extraction.
|
- Use `extract` before `resize` for pre-resize extraction.
|
||||||
- Use `extract` after `resize` for post-resize extraction.
|
- Use `extract` after `resize` for post-resize extraction.
|
||||||
- Use `extract` before and after for both.
|
- Use `extract` twice and `resize` once for extract-then-resize-then-extract in a fixed operation order.
|
||||||
|
|
||||||
|
|
||||||
**Throws**:
|
**Throws**:
|
||||||
@@ -245,6 +251,8 @@ sharp(input)
|
|||||||
|
|
||||||
|
|
||||||
## trim
|
## trim
|
||||||
|
> trim(trim) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.
|
Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.
|
||||||
|
|
||||||
Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
|
Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
## versions
|
## versions
|
||||||
|
> versions
|
||||||
|
|
||||||
An Object containing the version numbers of sharp, libvips and its dependencies.
|
An Object containing the version numbers of sharp, libvips and its dependencies.
|
||||||
|
|
||||||
|
|
||||||
@@ -9,6 +11,8 @@ console.log(sharp.versions);
|
|||||||
|
|
||||||
|
|
||||||
## interpolators
|
## interpolators
|
||||||
|
> interpolators : <code>enum</code>
|
||||||
|
|
||||||
An Object containing the available interpolators and their proper values
|
An Object containing the available interpolators and their proper values
|
||||||
|
|
||||||
|
|
||||||
@@ -27,6 +31,8 @@ An Object containing the available interpolators and their proper values
|
|||||||
|
|
||||||
|
|
||||||
## format
|
## format
|
||||||
|
> format ⇒ <code>Object</code>
|
||||||
|
|
||||||
An Object containing nested boolean values representing the available input and output formats/methods.
|
An Object containing nested boolean values representing the available input and output formats/methods.
|
||||||
|
|
||||||
|
|
||||||
@@ -36,18 +42,9 @@ console.log(sharp.format);
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## vendor
|
|
||||||
An Object containing the platform and architecture
|
|
||||||
of the current and installed vendored binaries.
|
|
||||||
|
|
||||||
|
|
||||||
**Example**
|
|
||||||
```js
|
|
||||||
console.log(sharp.vendor);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## queue
|
## queue
|
||||||
|
> queue
|
||||||
|
|
||||||
An EventEmitter that emits a `change` event when a task is either:
|
An EventEmitter that emits a `change` event when a task is either:
|
||||||
- queued, waiting for _libuv_ to provide a worker thread
|
- queued, waiting for _libuv_ to provide a worker thread
|
||||||
- complete
|
- complete
|
||||||
@@ -62,6 +59,8 @@ sharp.queue.on('change', function(queueLength) {
|
|||||||
|
|
||||||
|
|
||||||
## cache
|
## cache
|
||||||
|
> cache([options]) ⇒ <code>Object</code>
|
||||||
|
|
||||||
Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
|
Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
|
||||||
Existing entries in the cache will be trimmed after any change in limits.
|
Existing entries in the cache will be trimmed after any change in limits.
|
||||||
This method always returns cache statistics,
|
This method always returns cache statistics,
|
||||||
@@ -89,6 +88,8 @@ sharp.cache(false);
|
|||||||
|
|
||||||
|
|
||||||
## concurrency
|
## concurrency
|
||||||
|
> concurrency([concurrency]) ⇒ <code>number</code>
|
||||||
|
|
||||||
Gets or, when a concurrency is provided, sets
|
Gets or, when a concurrency is provided, sets
|
||||||
the maximum number of threads _libvips_ should use to process _each image_.
|
the maximum number of threads _libvips_ should use to process _each image_.
|
||||||
These are from a thread pool managed by glib,
|
These are from a thread pool managed by glib,
|
||||||
@@ -132,6 +133,8 @@ sharp.concurrency(0); // 4
|
|||||||
|
|
||||||
|
|
||||||
## counters
|
## counters
|
||||||
|
> counters() ⇒ <code>Object</code>
|
||||||
|
|
||||||
Provides access to internal task counters.
|
Provides access to internal task counters.
|
||||||
- queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
|
- queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
|
||||||
- process is the number of resize tasks currently being processed.
|
- process is the number of resize tasks currently being processed.
|
||||||
@@ -144,6 +147,8 @@ const counters = sharp.counters(); // { queue: 2, process: 4 }
|
|||||||
|
|
||||||
|
|
||||||
## simd
|
## simd
|
||||||
|
> simd([simd]) ⇒ <code>boolean</code>
|
||||||
|
|
||||||
Get and set use of SIMD vector unit instructions.
|
Get and set use of SIMD vector unit instructions.
|
||||||
Requires libvips to have been compiled with liborc support.
|
Requires libvips to have been compiled with liborc support.
|
||||||
|
|
||||||
@@ -169,6 +174,8 @@ const simd = sharp.simd(false);
|
|||||||
|
|
||||||
|
|
||||||
## block
|
## block
|
||||||
|
> block(options)
|
||||||
|
|
||||||
Block libvips operations at runtime.
|
Block libvips operations at runtime.
|
||||||
|
|
||||||
This is in addition to the `VIPS_BLOCK_UNTRUSTED` environment variable,
|
This is in addition to the `VIPS_BLOCK_UNTRUSTED` environment variable,
|
||||||
@@ -191,6 +198,8 @@ sharp.block({
|
|||||||
|
|
||||||
|
|
||||||
## unblock
|
## unblock
|
||||||
|
> unblock(options)
|
||||||
|
|
||||||
Unblock libvips operations at runtime.
|
Unblock libvips operations at runtime.
|
||||||
|
|
||||||
This is useful for defining a list of allowed operations.
|
This is useful for defining a list of allowed operations.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const jsdoc2md = require('jsdoc-to-markdown');
|
|||||||
});
|
});
|
||||||
|
|
||||||
const cleanMarkdown = markdown
|
const cleanMarkdown = markdown
|
||||||
.replace(/(## [A-Za-z0-9]+)[^\n]*/g, '$1') // simplify headings to match those of documentationjs, ensures existing URLs work
|
.replace(/(## )([A-Za-z0-9]+)([^\n]*)/g, '$1$2\n> $2$3\n') // simplify headings to match those of documentationjs, ensures existing URLs work
|
||||||
.replace(/<a name="[A-Za-z0-9+]+"><\/a>/g, '') // remove anchors, let docute add these (at markdown to HTML render time)
|
.replace(/<a name="[A-Za-z0-9+]+"><\/a>/g, '') // remove anchors, let docute add these (at markdown to HTML render time)
|
||||||
.replace(/\*\*Kind\*\*: global[^\n]+/g, '') // remove all "global" Kind labels (requires JSDoc refactoring)
|
.replace(/\*\*Kind\*\*: global[^\n]+/g, '') // remove all "global" Kind labels (requires JSDoc refactoring)
|
||||||
.trim();
|
.trim();
|
||||||
|
|||||||
@@ -1,8 +1,63 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v0.33 - *gauge*
|
||||||
|
|
||||||
|
Requires libvips v8.14.5
|
||||||
|
|
||||||
|
### v0.33.0 - TBD
|
||||||
|
|
||||||
|
* Drop support for Node.js 14 and 16, now requires Node.js >= 18.17.0
|
||||||
|
|
||||||
|
* Remove `sharp.vendor`.
|
||||||
|
|
||||||
|
* Make `compression` option of `heif` mandatory to help reduce HEIF vs HEIC confusion.
|
||||||
|
[#3740](https://github.com/lovell/sharp/issues/3740)
|
||||||
|
|
||||||
|
* Ensure correct interpretation of 16-bit raw input.
|
||||||
|
[#3808](https://github.com/lovell/sharp/issues/3808)
|
||||||
|
|
||||||
## v0.32 - *flow*
|
## v0.32 - *flow*
|
||||||
|
|
||||||
Requires libvips v8.14.3
|
Requires libvips v8.14.5
|
||||||
|
|
||||||
|
### v0.32.6 - 18th September 2023
|
||||||
|
|
||||||
|
* Upgrade to libvips v8.14.5 for upstream bug fixes.
|
||||||
|
|
||||||
|
* Ensure composite tile images are fully decoded (regression in 0.32.0).
|
||||||
|
[#3767](https://github.com/lovell/sharp/issues/3767)
|
||||||
|
|
||||||
|
* Ensure `withMetadata` can add ICC profiles to RGB16 output.
|
||||||
|
[#3773](https://github.com/lovell/sharp/issues/3773)
|
||||||
|
|
||||||
|
* Ensure `withMetadata` does not reduce 16-bit images to 8-bit (regression in 0.32.5).
|
||||||
|
[#3773](https://github.com/lovell/sharp/issues/3773)
|
||||||
|
|
||||||
|
* TypeScript: Add definitions for block and unblock.
|
||||||
|
[#3799](https://github.com/lovell/sharp/pull/3799)
|
||||||
|
[@ldrick](https://github.com/ldrick)
|
||||||
|
|
||||||
|
### v0.32.5 - 15th August 2023
|
||||||
|
|
||||||
|
* Upgrade to libvips v8.14.4 for upstream bug fixes.
|
||||||
|
|
||||||
|
* TypeScript: Add missing `WebpPresetEnum` to definitions.
|
||||||
|
[#3748](https://github.com/lovell/sharp/pull/3748)
|
||||||
|
[@pilotso11](https://github.com/pilotso11)
|
||||||
|
|
||||||
|
* Ensure compilation using musl v1.2.4.
|
||||||
|
[#3755](https://github.com/lovell/sharp/pull/3755)
|
||||||
|
[@kleisauke](https://github.com/kleisauke)
|
||||||
|
|
||||||
|
* Ensure resize with a `fit` of `inside` respects 90/270 degree rotation.
|
||||||
|
[#3756](https://github.com/lovell/sharp/issues/3756)
|
||||||
|
|
||||||
|
* TypeScript: Ensure `minSize` property of `WebpOptions` is boolean.
|
||||||
|
[#3758](https://github.com/lovell/sharp/pull/3758)
|
||||||
|
[@sho-xizz](https://github.com/sho-xizz)
|
||||||
|
|
||||||
|
* Ensure `withMetadata` adds default sRGB profile.
|
||||||
|
[#3761](https://github.com/lovell/sharp/issues/3761)
|
||||||
|
|
||||||
### v0.32.4 - 21st July 2023
|
### v0.32.4 - 21st July 2023
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
61
docs/image/api-resize-fit.svg
Normal file
61
docs/image/api-resize-fit.svg
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="998" height="243" viewBox="0 0 998 243">
|
||||||
|
<defs>
|
||||||
|
<g id="placeholder">
|
||||||
|
<rect width="180" height="128" fill="#64bed8"/>
|
||||||
|
<circle cx="61.1" cy="36.8" r="19.3" fill="#ffefa9"/>
|
||||||
|
<circle cx="61.1" cy="36.8" r="18.1" fill="#fdda42"/>
|
||||||
|
<path d="m67.2 34.7 15.2 46L90 57.9l30.4 38 7.8-15.2 7.5 15.4H44z" fill="#6a696f"/>
|
||||||
|
<path d="m82.4 80.7-15.2-46-.3 69h22.9z" fill="#474749"/>
|
||||||
|
<path d="m90.1 58 12.2 15 18.2 23-13.9.1z" fill="#474749"/>
|
||||||
|
<path d="M135.8 96H131l-2.8-15.3z" fill="#474749"/>
|
||||||
|
<path d="M35.2 96h107.1c0 1.7-1.4 3.2-3.2 3.2H38.4a3.2 3.2 0 0 1-3.2-3.2z" fill="#b9c861"/>
|
||||||
|
<path d="m67.2 34.7-.1 31-6.2-3-5.3 2.7z" fill="#fff"/>
|
||||||
|
<path d="m67.2 34.7 7.6 23-7.7 8z" fill="#b3b1b4"/>
|
||||||
|
<rect width="30.8" height="7.7" x="71.1" y="27.2" rx="2.8" ry="4.1" fill="#fff"/>
|
||||||
|
<rect width="30.8" height="7.7" x="82.2" y="34.8" rx="2.8" ry="4.1" fill="#fff"/>
|
||||||
|
<rect width="30.8" height="7.7" x="36.2" y="19.6" rx="2.8" ry="4.1" fill="#fff"/>
|
||||||
|
<path d="m89.6 72.8-7.2 7.9L90 57.9l10 23z" fill="#fff"/>
|
||||||
|
<path d="m90.1 58 10 23 2.2-8z" fill="#b3b1b4"/>
|
||||||
|
<path d="M131.2 85.2 137 68l9 17.2-8 6z" fill="#8da128"/>
|
||||||
|
<rect width="109.4" height="6.8" x="33.9" y="99.1" rx="13.2" ry="11.4" fill="#22b0d6"/>
|
||||||
|
<path d="m137 68-5.8 17.2 6.8 6.1.3-13.7z" fill="#727d2e"/>
|
||||||
|
<rect width="83.3" height="6.8" x="50.8" y="103.6" rx="10" ry="11.4" fill="#22b0d6"/>
|
||||||
|
<rect width=".7" height="18.4" x="138" y="77.6" fill="#585657"/>
|
||||||
|
<rect width=".5" height="5.2" x="2" y="-161.3" fill="#585657" transform="rotate(120)"/>
|
||||||
|
<rect width=".5" height="5.3" x="5.5" y="-163.3" fill="#585657" transform="rotate(120)"/>
|
||||||
|
<rect width=".5" height="4.8" x="-142.4" y="77.7" fill="#585657" transform="rotate(240)"/>
|
||||||
|
<rect width=".5" height="5.1" x="-146" y="75.6" fill="#585657" transform="rotate(240)"/>
|
||||||
|
</g>
|
||||||
|
<pattern id="img" height="100%" width="100%" viewBox="0 0 180 128">
|
||||||
|
<use xlink:href="#placeholder"/>
|
||||||
|
</pattern>
|
||||||
|
<pattern id="img-fill" width="100%" height="100%" viewBox="0 0 180 128" preserveAspectRatio="none">
|
||||||
|
<use xlink:href="#placeholder"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect x="0" y="0" width="998" height="243" fill="#ddd"/>
|
||||||
|
<g id="cover">
|
||||||
|
<rect x="22" y="28" width="180" height="132" fill="url(#img)"/>
|
||||||
|
<rect x="48" y="30" width="128" height="128" fill="none" stroke="#000" stroke-width="4"/>
|
||||||
|
<text x="112" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold">cover</text>
|
||||||
|
</g>
|
||||||
|
<g id="contain">
|
||||||
|
<rect x="240" y="30" width="128" height="128" fill="url(#img)" stroke="#000" stroke-width="4"/>
|
||||||
|
<text x="304" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold" fill="#555">contain</text>
|
||||||
|
</g>
|
||||||
|
<g id="fill">
|
||||||
|
<rect x="432" y="30" width="128" height="128" fill="url(#img-fill)" stroke="#000" stroke-width="4"/>
|
||||||
|
<text x="496" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold">fill</text>
|
||||||
|
</g>
|
||||||
|
<g id="inside">
|
||||||
|
<rect x="624" y="48" width="128" height="92" fill="url(#img)" stroke="#000" stroke-width="4"/>
|
||||||
|
<rect x="624" y="30" width="128" height="128" fill="none" stroke="#000" stroke-width="4" stroke-dasharray="12 4" stroke-dashoffset="6"/>
|
||||||
|
<text x="688" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold" fill="#555">inside</text>
|
||||||
|
</g>
|
||||||
|
<g id="outside">
|
||||||
|
<rect x="792" y="30" width="176" height="128" fill="url(#img)" stroke="#000" stroke-width="4"/>
|
||||||
|
<rect x="816" y="30" width="128" height="128" fill="none" stroke="#000" stroke-width="4" stroke-dasharray="12 4" stroke-dashoffset="-2"/>
|
||||||
|
<text x="880" y="85%" dominant-baseline="middle" text-anchor="middle" font-family="sans" font-size="32" font-weight="bold">outside</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
202
docs/install.md
202
docs/install.md
@@ -8,9 +8,13 @@ npm install sharp
|
|||||||
yarn add sharp
|
yarn add sharp
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm add sharp
|
||||||
|
```
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
* Node.js >= 14.15.0
|
* Node.js >= 18.17.0
|
||||||
|
|
||||||
## Prebuilt binaries
|
## Prebuilt binaries
|
||||||
|
|
||||||
@@ -18,98 +22,19 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
|
|||||||
|
|
||||||
* macOS x64 (>= 10.13)
|
* macOS x64 (>= 10.13)
|
||||||
* macOS ARM64
|
* macOS ARM64
|
||||||
* Linux x64 (glibc >= 2.17, musl >= 1.1.24, CPU with SSE4.2)
|
* Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
|
||||||
* Linux ARM64 (glibc >= 2.17, musl >= 1.1.24)
|
* Linux ARM64 (glibc >= 2.26, musl >= 1.2.2)
|
||||||
|
* Linux ARM (glibc >= 2.28)
|
||||||
* Windows x64
|
* Windows x64
|
||||||
* Windows x86
|
* Windows x86
|
||||||
|
|
||||||
A ~7MB tarball containing libvips and its most commonly used dependencies
|
|
||||||
is downloaded via HTTPS, verified via Subresource Integrity
|
|
||||||
and decompressed into `node_modules/sharp/vendor` during `npm install`.
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
The following platforms have prebuilt libvips but not sharp:
|
|
||||||
|
|
||||||
* Linux ARMv7 (glibc >= 2.28)
|
|
||||||
* Linux ARMv6 (glibc >= 2.28)
|
|
||||||
* Windows ARM64
|
|
||||||
|
|
||||||
The following platforms require compilation of both libvips and sharp from source:
|
|
||||||
|
|
||||||
* Linux x86
|
|
||||||
* Linux ARMv7 (glibc <= 2.27, musl)
|
|
||||||
* Linux ARMv6 (glibc <= 2.27, musl)
|
|
||||||
* Linux PowerPC
|
|
||||||
* FreeBSD
|
|
||||||
* OpenBSD
|
|
||||||
|
|
||||||
## Common problems
|
|
||||||
|
|
||||||
The architecture and platform of Node.js used for `npm install`
|
|
||||||
must be the same as the architecture and platform of Node.js used at runtime.
|
|
||||||
See the [cross-platform](#cross-platform) section if this is not the case.
|
|
||||||
|
|
||||||
When using npm v6 or earlier, the `npm install --unsafe-perm` flag must be used when installing as `root` or a `sudo` user.
|
|
||||||
|
|
||||||
When using npm v7 or later, the user running `npm install` must own the directory it is run in.
|
|
||||||
|
|
||||||
The `npm install --ignore-scripts=false` flag must be used when `npm` has been configured to ignore installation scripts.
|
|
||||||
|
|
||||||
Check the output of running `npm install --verbose --foreground-scripts sharp` for useful error messages.
|
|
||||||
|
|
||||||
## Apple M1
|
|
||||||
|
|
||||||
Prebuilt sharp and libvips binaries have been provided for macOS on ARM64 since sharp v0.29.0.
|
|
||||||
|
|
||||||
## Cross-platform
|
|
||||||
|
|
||||||
At `npm install` time, prebuilt binaries are automatically selected for the
|
|
||||||
current OS platform and CPU architecture, where available.
|
|
||||||
|
|
||||||
The target platform and/or architecture can be manually selected using the following flags.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install --platform=... --arch=... --arm-version=... sharp
|
|
||||||
```
|
|
||||||
|
|
||||||
* `--platform`: one of `linux`, `darwin` or `win32`.
|
|
||||||
* `--arch`: one of `x64`, `ia32`, `arm` or `arm64`.
|
|
||||||
* `--arm-version`: one of `6`, `7` or `8` (`arm` defaults to `6`, `arm64` defaults to `8`).
|
|
||||||
* `--libc`: one of `glibc` or `musl`. This option only works with platform `linux`, defaults to `glibc`
|
|
||||||
* `--sharp-install-force`: skip version compatibility and Subresource Integrity checks.
|
|
||||||
|
|
||||||
These values can also be set via environment variables,
|
|
||||||
`npm_config_platform`, `npm_config_arch`, `npm_config_arm_version`, `npm_config_libc`
|
|
||||||
and `SHARP_INSTALL_FORCE` respectively.
|
|
||||||
|
|
||||||
For example, if the target machine has a 64-bit ARM CPU and is running Alpine Linux,
|
|
||||||
use the following flags:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install --arch=arm64 --platform=linux --libc=musl sharp
|
|
||||||
```
|
|
||||||
|
|
||||||
If the current machine is Alpine Linux and the target machine is Debian Linux on x64 cpu,
|
|
||||||
use the following flags:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install --arch=x64 --platform=linux --libc=glibc sharp
|
|
||||||
```
|
|
||||||
|
|
||||||
Multiple platforms and architectures can be supported within the same installation tree.
|
|
||||||
The following example for macOS installs x64 binaries then adds (via a rebuild) arm64 binaries:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install --platform=darwin --arch=x64 sharp
|
|
||||||
npm rebuild --platform=darwin --arch=arm64 sharp
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom libvips
|
## Custom libvips
|
||||||
|
|
||||||
To use a custom, globally-installed version of libvips instead of the provided binaries,
|
To use a custom, globally-installed version of libvips instead of the provided binaries,
|
||||||
make sure it is at least the version listed under `config.libvips` in the `package.json` file
|
make sure it is at least the version listed under `engines.libvips` in the `package.json` file
|
||||||
and that it can be located using `pkg-config --modversion vips-cpp`.
|
and that it can be located using `pkg-config --modversion vips-cpp`.
|
||||||
|
|
||||||
For help compiling libvips and its dependencies, please see
|
For help compiling libvips and its dependencies, please see
|
||||||
@@ -122,8 +47,7 @@ and on macOS when running Node.js under Rosetta.
|
|||||||
|
|
||||||
This module will be compiled from source at `npm install` time when:
|
This module will be compiled from source at `npm install` time when:
|
||||||
|
|
||||||
* a globally-installed libvips is detected (set the `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable to skip this),
|
* a globally-installed libvips is detected (set the `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable to skip this), or
|
||||||
* prebuilt sharp binaries do not exist for the current platform, or
|
|
||||||
* when the `npm install --build-from-source` flag is used.
|
* when the `npm install --build-from-source` flag is used.
|
||||||
|
|
||||||
Building from source requires:
|
Building from source requires:
|
||||||
@@ -131,83 +55,11 @@ Building from source requires:
|
|||||||
* C++11 compiler
|
* C++11 compiler
|
||||||
* [node-gyp](https://github.com/nodejs/node-gyp#installation) and its dependencies
|
* [node-gyp](https://github.com/nodejs/node-gyp#installation) and its dependencies
|
||||||
|
|
||||||
## Custom prebuilt binaries
|
If `node-gyp` cannot be found, try adding it to `devDependencies`.
|
||||||
|
|
||||||
This is an advanced approach that most people will not require.
|
For cross-compiling, the `--platform`, `--arch` and `--libc` npm flags
|
||||||
|
(or the `npm_config_platform`, `npm_config_arch` and `npm_config_libc` environment variables)
|
||||||
### Prebuilt sharp binaries
|
can be used to configure the target environment.
|
||||||
|
|
||||||
To install the prebuilt sharp binaries from a custom URL,
|
|
||||||
set the `sharp_binary_host` npm config option
|
|
||||||
or the `npm_config_sharp_binary_host` environment variable.
|
|
||||||
|
|
||||||
To install the prebuilt sharp binaries from a directory on the local filesystem,
|
|
||||||
set the `sharp_local_prebuilds` npm config option
|
|
||||||
or the `npm_config_sharp_local_prebuilds` environment variable.
|
|
||||||
|
|
||||||
URL example:
|
|
||||||
if `sharp_binary_host` is set to `https://hostname/path`
|
|
||||||
and the sharp version is `1.2.3` then the resultant URL will be
|
|
||||||
`https://hostname/path/v1.2.3/sharp-v1.2.3-napi-v5-platform-arch.tar.gz`.
|
|
||||||
|
|
||||||
Filename example:
|
|
||||||
if `sharp_local_prebuilds` is set to `/path`
|
|
||||||
and the sharp version is `1.2.3` then the resultant filename will be
|
|
||||||
`/path/sharp-v1.2.3-napi-v5-platform-arch.tar.gz`.
|
|
||||||
|
|
||||||
### Prebuilt libvips binaries
|
|
||||||
|
|
||||||
To install the prebuilt libvips binaries from a custom URL,
|
|
||||||
set the `sharp_libvips_binary_host` npm config option
|
|
||||||
or the `npm_config_sharp_libvips_binary_host` environment variable.
|
|
||||||
|
|
||||||
To install the prebuilt libvips binaries from a directory on the local filesystem,
|
|
||||||
set the `sharp_libvips_local_prebuilds` npm config option
|
|
||||||
or the `npm_config_sharp_libvips_local_prebuilds` environment variable.
|
|
||||||
|
|
||||||
The version subpath and filename are appended to these.
|
|
||||||
|
|
||||||
URL example:
|
|
||||||
if `sharp_libvips_binary_host` is set to `https://hostname/path`
|
|
||||||
and the libvips version is `4.5.6` then the resultant URL will be
|
|
||||||
`https://hostname/path/v4.5.6/libvips-4.5.6-platform-arch.tar.br`.
|
|
||||||
|
|
||||||
Filename example:
|
|
||||||
if `sharp_libvips_local_prebuilds` is set to `/path`
|
|
||||||
and the libvips version is `4.5.6` then the resultant filename will be
|
|
||||||
`/path/v4.5.6/libvips-4.5.6-platform-arch.tar.br`.
|
|
||||||
|
|
||||||
See the Chinese mirror below for a further example.
|
|
||||||
|
|
||||||
If these binaries are modified, new integrity hashes can be provided
|
|
||||||
at install time via `npm_package_config_integrity_platform_arch`
|
|
||||||
environment variables, for example set
|
|
||||||
`npm_package_config_integrity_linux_x64` to `sha512-abc...`.
|
|
||||||
|
|
||||||
The integrity hash of a file can be generated via:
|
|
||||||
```sh
|
|
||||||
sha512sum libvips-x.y.z-platform-arch.tar.br | cut -f1 -d' ' | xxd -r -p | base64 -w 0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Chinese mirror
|
|
||||||
|
|
||||||
A mirror site based in China, provided by Alibaba, contains binaries for both sharp and libvips.
|
|
||||||
|
|
||||||
To use this either set the following configuration:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm config set sharp_binary_host "https://npmmirror.com/mirrors/sharp"
|
|
||||||
npm config set sharp_libvips_binary_host "https://npmmirror.com/mirrors/sharp-libvips"
|
|
||||||
npm install sharp
|
|
||||||
```
|
|
||||||
|
|
||||||
or set the following environment variables:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm_config_sharp_binary_host="https://npmmirror.com/mirrors/sharp" \
|
|
||||||
npm_config_sharp_libvips_binary_host="https://npmmirror.com/mirrors/sharp-libvips" \
|
|
||||||
npm install sharp
|
|
||||||
```
|
|
||||||
|
|
||||||
## FreeBSD
|
## FreeBSD
|
||||||
|
|
||||||
@@ -242,15 +94,18 @@ unaffected.
|
|||||||
|
|
||||||
The `node_modules` directory of the
|
The `node_modules` directory of the
|
||||||
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-package.html)
|
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-package.html)
|
||||||
must include binaries for the Linux x64 platform.
|
must include binaries for either the linux-x64 or linux-arm64 platforms
|
||||||
|
depending on the chosen architecture.
|
||||||
|
|
||||||
When building your deployment package on machines other than Linux x64 (glibc),
|
When building your deployment package on a machine that differs from the target architecture,
|
||||||
run the following additional command after `npm install`:
|
you will need to install either `@sharpen/sharp-linux-x64` or `@sharpen/sharp-linux-arm64` package.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install
|
npm install --force @sharpen/sharp-linux-x64
|
||||||
rm -rf node_modules/sharp
|
```
|
||||||
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux --libc=glibc sharp
|
|
||||||
|
```sh
|
||||||
|
npm install --force @sharpen/sharp-linux-arm64
|
||||||
```
|
```
|
||||||
|
|
||||||
To get the best performance select the largest memory available.
|
To get the best performance select the largest memory available.
|
||||||
@@ -302,7 +157,7 @@ custom:
|
|||||||
- sharp
|
- sharp
|
||||||
packagerOptions:
|
packagerOptions:
|
||||||
scripts:
|
scripts:
|
||||||
- npm install --arch=x64 --platform=linux sharp
|
- npm install --force @sharpen/sharp-linux-x64
|
||||||
```
|
```
|
||||||
|
|
||||||
## TypeScript
|
## TypeScript
|
||||||
@@ -358,14 +213,7 @@ Module did not self-register
|
|||||||
|
|
||||||
### Canvas and Windows
|
### Canvas and Windows
|
||||||
|
|
||||||
The prebuilt binaries provided by `canvas` for Windows
|
If both `canvas` and `sharp` modules are used in the same Windows process, the following error may occur:
|
||||||
from v2.7.0 onwards depend on the Visual C++ Runtime (MSVCRT).
|
|
||||||
These conflict with the binaries provided by sharp,
|
|
||||||
which depend on the more modern Universal C Runtime (UCRT).
|
|
||||||
|
|
||||||
See [Automattic/node-canvas#2155](https://github.com/Automattic/node-canvas/issues/2155).
|
|
||||||
|
|
||||||
If both modules are used in the same Windows process, the following error will occur:
|
|
||||||
```
|
```
|
||||||
The specified procedure could not be found.
|
The specified procedure could not be found.
|
||||||
```
|
```
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -40,7 +40,7 @@ for (const match of matches) {
|
|||||||
].forEach((section) => {
|
].forEach((section) => {
|
||||||
const contents = fs.readFileSync(path.join(__dirname, '..', `api-${section}.md`), 'utf8');
|
const contents = fs.readFileSync(path.join(__dirname, '..', `api-${section}.md`), 'utf8');
|
||||||
const matches = contents.matchAll(
|
const matches = contents.matchAll(
|
||||||
/## (?<title>[A-Za-z]+)\n(?<firstparagraph>.+?)\n\n.+?(?<parameters>\| Param .+?\n\n)?\*\*Example/gs
|
/## (?<title>[A-Za-z]+)\n[^\n]+\n(?<firstparagraph>.+?)\n\n.+?(?<parameters>\| Param .+?\n\n)?\*\*Example/gs
|
||||||
);
|
);
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
const { title, firstparagraph, parameters } = match.groups;
|
const { title, firstparagraph, parameters } = match.groups;
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const libvips = require('../lib/libvips');
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!(libvips.useGlobalLibvips() || libvips.hasVendoredLibvips())) {
|
|
||||||
process.exitCode = 1;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
process.exitCode = 1;
|
|
||||||
}
|
|
||||||
27
install/check.js
Normal file
27
install/check.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2013 Lovell Fuller and others.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { useGlobalLibvips, globalLibvipsVersion, log, gypRebuild } = require('../lib/libvips');
|
||||||
|
|
||||||
|
const buildFromSource = (msg) => {
|
||||||
|
log(msg);
|
||||||
|
log('Attempting to build from source via node-gyp');
|
||||||
|
try {
|
||||||
|
require('node-gyp');
|
||||||
|
} catch (err) {
|
||||||
|
log('You might need to install node-gyp');
|
||||||
|
}
|
||||||
|
log('See https://sharp.pixelplumbing.com/install#building-from-source');
|
||||||
|
const status = gypRebuild();
|
||||||
|
if (status !== 0) {
|
||||||
|
process.exit(status);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (useGlobalLibvips()) {
|
||||||
|
buildFromSource(`Detected globally-installed libvips v${globalLibvipsVersion()}`);
|
||||||
|
} else if (process.env.npm_config_build_from_source) {
|
||||||
|
buildFromSource('Detected --build-from-source flag');
|
||||||
|
}
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const libvips = require('../lib/libvips');
|
|
||||||
const platform = require('../lib/platform');
|
|
||||||
|
|
||||||
const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
|
|
||||||
|
|
||||||
const platformAndArch = platform();
|
|
||||||
|
|
||||||
if (platformAndArch.startsWith('win32')) {
|
|
||||||
const buildReleaseDir = path.join(__dirname, '..', 'build', 'Release');
|
|
||||||
libvips.log(`Creating ${buildReleaseDir}`);
|
|
||||||
try {
|
|
||||||
libvips.mkdirSync(buildReleaseDir);
|
|
||||||
} catch (err) {}
|
|
||||||
const vendorLibDir = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch, 'lib');
|
|
||||||
libvips.log(`Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
|
|
||||||
try {
|
|
||||||
fs
|
|
||||||
.readdirSync(vendorLibDir)
|
|
||||||
.filter(function (filename) {
|
|
||||||
return /\.dll$/.test(filename);
|
|
||||||
})
|
|
||||||
.forEach(function (filename) {
|
|
||||||
fs.copyFileSync(
|
|
||||||
path.join(vendorLibDir, filename),
|
|
||||||
path.join(buildReleaseDir, filename)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
libvips.log(err);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const os = require('os');
|
|
||||||
const path = require('path');
|
|
||||||
const stream = require('stream');
|
|
||||||
const zlib = require('zlib');
|
|
||||||
const { createHash } = require('crypto');
|
|
||||||
|
|
||||||
const detectLibc = require('detect-libc');
|
|
||||||
const semverCoerce = require('semver/functions/coerce');
|
|
||||||
const semverLessThan = require('semver/functions/lt');
|
|
||||||
const semverSatisfies = require('semver/functions/satisfies');
|
|
||||||
const simpleGet = require('simple-get');
|
|
||||||
const tarFs = require('tar-fs');
|
|
||||||
|
|
||||||
const agent = require('../lib/agent');
|
|
||||||
const libvips = require('../lib/libvips');
|
|
||||||
const platform = require('../lib/platform');
|
|
||||||
|
|
||||||
const minimumGlibcVersionByArch = {
|
|
||||||
arm: '2.28',
|
|
||||||
arm64: '2.17',
|
|
||||||
x64: '2.17'
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasSharpPrebuild = [
|
|
||||||
'darwin-x64',
|
|
||||||
'darwin-arm64',
|
|
||||||
'linux-arm64',
|
|
||||||
'linux-x64',
|
|
||||||
'linuxmusl-x64',
|
|
||||||
'linuxmusl-arm64',
|
|
||||||
'win32-ia32',
|
|
||||||
'win32-x64'
|
|
||||||
];
|
|
||||||
|
|
||||||
const { minimumLibvipsVersion, minimumLibvipsVersionLabelled } = libvips;
|
|
||||||
const localLibvipsDir = process.env.npm_config_sharp_libvips_local_prebuilds || '';
|
|
||||||
const distHost = process.env.npm_config_sharp_libvips_binary_host || 'https://github.com/lovell/sharp-libvips/releases/download';
|
|
||||||
const distBaseUrl = process.env.npm_config_sharp_dist_base_url || process.env.SHARP_DIST_BASE_URL || `${distHost}/v${minimumLibvipsVersionLabelled}/`;
|
|
||||||
const installationForced = !!(process.env.npm_config_sharp_install_force || process.env.SHARP_INSTALL_FORCE);
|
|
||||||
|
|
||||||
const fail = function (err) {
|
|
||||||
libvips.log(err);
|
|
||||||
if (err.code === 'EACCES') {
|
|
||||||
libvips.log('Are you trying to install as a root or sudo user?');
|
|
||||||
libvips.log('- For npm <= v6, try again with the "--unsafe-perm" flag');
|
|
||||||
libvips.log('- For npm >= v8, the user must own the directory "npm install" is run in');
|
|
||||||
}
|
|
||||||
libvips.log('Please see https://sharp.pixelplumbing.com/install for required dependencies');
|
|
||||||
process.exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleError = function (err) {
|
|
||||||
if (installationForced) {
|
|
||||||
libvips.log(`Installation warning: ${err.message}`);
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const verifyIntegrity = function (platformAndArch) {
|
|
||||||
const expected = libvips.integrity(platformAndArch);
|
|
||||||
if (installationForced || !expected) {
|
|
||||||
libvips.log(`Integrity check skipped for ${platformAndArch}`);
|
|
||||||
return new stream.PassThrough();
|
|
||||||
}
|
|
||||||
const hash = createHash('sha512');
|
|
||||||
return new stream.Transform({
|
|
||||||
transform: function (chunk, _encoding, done) {
|
|
||||||
hash.update(chunk);
|
|
||||||
done(null, chunk);
|
|
||||||
},
|
|
||||||
flush: function (done) {
|
|
||||||
const digest = `sha512-${hash.digest('base64')}`;
|
|
||||||
if (expected !== digest) {
|
|
||||||
try {
|
|
||||||
libvips.removeVendoredLibvips();
|
|
||||||
} catch (err) {
|
|
||||||
libvips.log(err.message);
|
|
||||||
}
|
|
||||||
libvips.log(`Integrity expected: ${expected}`);
|
|
||||||
libvips.log(`Integrity received: ${digest}`);
|
|
||||||
done(new Error(`Integrity check failed for ${platformAndArch}`));
|
|
||||||
} else {
|
|
||||||
libvips.log(`Integrity check passed for ${platformAndArch}`);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const extractTarball = function (tarPath, platformAndArch) {
|
|
||||||
const versionedVendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch);
|
|
||||||
libvips.mkdirSync(versionedVendorPath);
|
|
||||||
|
|
||||||
const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;
|
|
||||||
const ignore = function (name) {
|
|
||||||
return ignoreVendorInclude && name.includes('include/');
|
|
||||||
};
|
|
||||||
|
|
||||||
stream.pipeline(
|
|
||||||
fs.createReadStream(tarPath),
|
|
||||||
verifyIntegrity(platformAndArch),
|
|
||||||
new zlib.BrotliDecompress(),
|
|
||||||
tarFs.extract(versionedVendorPath, { ignore }),
|
|
||||||
function (err) {
|
|
||||||
if (err) {
|
|
||||||
if (/unexpected end of file/.test(err.message)) {
|
|
||||||
fail(new Error(`Please delete ${tarPath} as it is not a valid tarball`));
|
|
||||||
}
|
|
||||||
fail(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const useGlobalLibvips = libvips.useGlobalLibvips();
|
|
||||||
|
|
||||||
if (useGlobalLibvips) {
|
|
||||||
const globalLibvipsVersion = libvips.globalLibvipsVersion();
|
|
||||||
libvips.log(`Detected globally-installed libvips v${globalLibvipsVersion}`);
|
|
||||||
libvips.log('Building from source via node-gyp');
|
|
||||||
process.exit(1);
|
|
||||||
} else if (libvips.hasVendoredLibvips()) {
|
|
||||||
libvips.log(`Using existing vendored libvips v${minimumLibvipsVersion}`);
|
|
||||||
} else {
|
|
||||||
// Is this arch/platform supported?
|
|
||||||
const arch = process.env.npm_config_arch || process.arch;
|
|
||||||
const platformAndArch = platform();
|
|
||||||
if (arch === 'ia32' && !platformAndArch.startsWith('win32')) {
|
|
||||||
throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
||||||
}
|
|
||||||
if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
|
|
||||||
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
||||||
}
|
|
||||||
// Linux libc version check
|
|
||||||
const libcVersionRaw = detectLibc.versionSync();
|
|
||||||
if (libcVersionRaw) {
|
|
||||||
const libcFamily = detectLibc.familySync();
|
|
||||||
const libcVersion = semverCoerce(libcVersionRaw).version;
|
|
||||||
if (libcFamily === detectLibc.GLIBC && minimumGlibcVersionByArch[arch]) {
|
|
||||||
if (semverLessThan(libcVersion, semverCoerce(minimumGlibcVersionByArch[arch]).version)) {
|
|
||||||
handleError(new Error(`Use with glibc ${libcVersionRaw} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (libcFamily === detectLibc.MUSL) {
|
|
||||||
if (semverLessThan(libcVersion, '1.1.24')) {
|
|
||||||
handleError(new Error(`Use with musl ${libcVersionRaw} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Node.js minimum version check
|
|
||||||
const supportedNodeVersion = process.env.npm_package_engines_node || require('../package.json').engines.node;
|
|
||||||
if (!semverSatisfies(process.versions.node, supportedNodeVersion)) {
|
|
||||||
handleError(new Error(`Expected Node.js version ${supportedNodeVersion} but found ${process.versions.node}`));
|
|
||||||
}
|
|
||||||
// Download to per-process temporary file
|
|
||||||
const tarFilename = ['libvips', minimumLibvipsVersionLabelled, platformAndArch].join('-') + '.tar.br';
|
|
||||||
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
|
|
||||||
if (fs.existsSync(tarPathCache)) {
|
|
||||||
libvips.log(`Using cached ${tarPathCache}`);
|
|
||||||
extractTarball(tarPathCache, platformAndArch);
|
|
||||||
} else if (localLibvipsDir) {
|
|
||||||
// If localLibvipsDir is given try to use binaries from local directory
|
|
||||||
const tarPathLocal = path.join(path.resolve(localLibvipsDir), `v${minimumLibvipsVersionLabelled}`, tarFilename);
|
|
||||||
libvips.log(`Using local libvips from ${tarPathLocal}`);
|
|
||||||
extractTarball(tarPathLocal, platformAndArch);
|
|
||||||
} else {
|
|
||||||
const url = distBaseUrl + tarFilename;
|
|
||||||
libvips.log(`Downloading ${url}`);
|
|
||||||
simpleGet({ url: url, agent: agent(libvips.log) }, function (err, response) {
|
|
||||||
if (err) {
|
|
||||||
fail(err);
|
|
||||||
} else if (response.statusCode === 404) {
|
|
||||||
fail(new Error(`Prebuilt libvips ${minimumLibvipsVersion} binaries are not yet available for ${platformAndArch}`));
|
|
||||||
} else if (response.statusCode !== 200) {
|
|
||||||
fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
|
|
||||||
} else {
|
|
||||||
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
|
|
||||||
const tmpFileStream = fs.createWriteStream(tarPathTemp);
|
|
||||||
response
|
|
||||||
.on('error', function (err) {
|
|
||||||
tmpFileStream.destroy(err);
|
|
||||||
})
|
|
||||||
.on('close', function () {
|
|
||||||
if (!response.complete) {
|
|
||||||
tmpFileStream.destroy(new Error('Download incomplete (connection was terminated)'));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.pipe(tmpFileStream);
|
|
||||||
tmpFileStream
|
|
||||||
.on('error', function (err) {
|
|
||||||
// Clean up temporary file
|
|
||||||
try {
|
|
||||||
fs.unlinkSync(tarPathTemp);
|
|
||||||
} catch (e) {}
|
|
||||||
fail(err);
|
|
||||||
})
|
|
||||||
.on('close', function () {
|
|
||||||
try {
|
|
||||||
// Attempt to rename
|
|
||||||
fs.renameSync(tarPathTemp, tarPathCache);
|
|
||||||
} catch (err) {
|
|
||||||
// Fall back to copy and unlink
|
|
||||||
fs.copyFileSync(tarPathTemp, tarPathCache);
|
|
||||||
fs.unlinkSync(tarPathTemp);
|
|
||||||
}
|
|
||||||
extractTarball(tarPathCache, platformAndArch);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
fail(err);
|
|
||||||
}
|
|
||||||
44
lib/agent.js
44
lib/agent.js
@@ -1,44 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const url = require('url');
|
|
||||||
const tunnelAgent = require('tunnel-agent');
|
|
||||||
|
|
||||||
const is = require('./is');
|
|
||||||
|
|
||||||
const proxies = [
|
|
||||||
'HTTPS_PROXY',
|
|
||||||
'https_proxy',
|
|
||||||
'HTTP_PROXY',
|
|
||||||
'http_proxy',
|
|
||||||
'npm_config_https_proxy',
|
|
||||||
'npm_config_proxy'
|
|
||||||
];
|
|
||||||
|
|
||||||
function env (key) {
|
|
||||||
return process.env[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function (log) {
|
|
||||||
try {
|
|
||||||
const proxy = new url.URL(proxies.map(env).find(is.string));
|
|
||||||
const tunnel = proxy.protocol === 'https:'
|
|
||||||
? tunnelAgent.httpsOverHttps
|
|
||||||
: tunnelAgent.httpsOverHttp;
|
|
||||||
const proxyAuth = proxy.username && proxy.password
|
|
||||||
? `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`
|
|
||||||
: null;
|
|
||||||
log(`Via proxy ${proxy.protocol}//${proxy.hostname}:${proxy.port} ${proxyAuth ? 'with' : 'no'} credentials`);
|
|
||||||
return tunnel({
|
|
||||||
proxy: {
|
|
||||||
port: Number(proxy.port),
|
|
||||||
host: proxy.hostname,
|
|
||||||
proxyAuth
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -3,11 +3,10 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const util = require('util');
|
const util = require('node:util');
|
||||||
const stream = require('stream');
|
const stream = require('node:stream');
|
||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
|
|
||||||
require('./libvips').hasVendoredLibvips();
|
|
||||||
require('./sharp');
|
require('./sharp');
|
||||||
|
|
||||||
// Use NODE_DEBUG=sharp to enable libvips warnings
|
// Use NODE_DEBUG=sharp to enable libvips warnings
|
||||||
|
|||||||
66
lib/index.d.ts
vendored
66
lib/index.d.ts
vendored
@@ -91,12 +91,6 @@ declare namespace sharp {
|
|||||||
zlib?: string | undefined;
|
zlib?: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** An Object containing the platform and architecture of the current and installed vendored binaries. */
|
|
||||||
const vendor: {
|
|
||||||
current: string;
|
|
||||||
installed: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/** An Object containing the available interpolators and their proper values */
|
/** An Object containing the available interpolators and their proper values */
|
||||||
const interpolators: Interpolators;
|
const interpolators: Interpolators;
|
||||||
|
|
||||||
@@ -139,6 +133,52 @@ declare namespace sharp {
|
|||||||
*/
|
*/
|
||||||
function simd(enable?: boolean): boolean;
|
function simd(enable?: boolean): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block libvips operations at runtime.
|
||||||
|
*
|
||||||
|
* This is in addition to the `VIPS_BLOCK_UNTRUSTED` environment variable,
|
||||||
|
* which when set will block all "untrusted" operations.
|
||||||
|
*
|
||||||
|
* @since 0.32.4
|
||||||
|
*
|
||||||
|
* @example <caption>Block all TIFF input.</caption>
|
||||||
|
* sharp.block({
|
||||||
|
* operation: ['VipsForeignLoadTiff']
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {Array<string>} options.operation - List of libvips low-level operation names to block.
|
||||||
|
*/
|
||||||
|
function block(options: { operation: string[] }): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblock libvips operations at runtime.
|
||||||
|
*
|
||||||
|
* This is useful for defining a list of allowed operations.
|
||||||
|
*
|
||||||
|
* @since 0.32.4
|
||||||
|
*
|
||||||
|
* @example <caption>Block all input except WebP from the filesystem.</caption>
|
||||||
|
* sharp.block({
|
||||||
|
* operation: ['VipsForeignLoad']
|
||||||
|
* });
|
||||||
|
* sharp.unblock({
|
||||||
|
* operation: ['VipsForeignLoadWebpFile']
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @example <caption>Block all input except JPEG and PNG from a Buffer or Stream.</caption>
|
||||||
|
* sharp.block({
|
||||||
|
* operation: ['VipsForeignLoad']
|
||||||
|
* });
|
||||||
|
* sharp.unblock({
|
||||||
|
* operation: ['VipsForeignLoadJpegBuffer', 'VipsForeignLoadPngBuffer']
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {Array<string>} options.operation - List of libvips low-level operation names to unblock.
|
||||||
|
*/
|
||||||
|
function unblock(options: { operation: string[] }): void;
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
const gravity: GravityEnum;
|
const gravity: GravityEnum;
|
||||||
@@ -659,7 +699,6 @@ declare namespace sharp {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Use these AVIF options for output image.
|
* Use these AVIF options for output image.
|
||||||
* Whilst it is possible to create AVIF images smaller than 16x16 pixels, most web browsers do not display these properly.
|
|
||||||
* @param options Output options.
|
* @param options Output options.
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
* @returns A sharp instance that can be used to chain operations
|
* @returns A sharp instance that can be used to chain operations
|
||||||
@@ -1124,9 +1163,11 @@ declare namespace sharp {
|
|||||||
/** 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) */
|
||||||
minSize?: number;
|
minSize?: boolean;
|
||||||
/** Allow mixture of lossy and lossless animation frames (slow) (optional, default false) */
|
/** Allow mixture of lossy and lossless animation frames (slow) (optional, default false) */
|
||||||
mixed?: boolean;
|
mixed?: boolean;
|
||||||
|
/** Preset options: one of default, photo, picture, drawing, icon, text (optional, default 'default') */
|
||||||
|
preset?: keyof PresetEnum | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AvifOptions extends OutputOptions {
|
interface AvifOptions extends OutputOptions {
|
||||||
@@ -1476,6 +1517,15 @@ declare namespace sharp {
|
|||||||
lanczos3: 'lanczos3';
|
lanczos3: 'lanczos3';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PresetEnum {
|
||||||
|
default: 'default';
|
||||||
|
picture: 'picture';
|
||||||
|
photo: 'photo';
|
||||||
|
drawing: 'drawing';
|
||||||
|
icon: 'icon';
|
||||||
|
text: 'text';
|
||||||
|
}
|
||||||
|
|
||||||
interface BoolEnum {
|
interface BoolEnum {
|
||||||
and: 'and';
|
and: 'and';
|
||||||
or: 'or';
|
or: 'or';
|
||||||
|
|||||||
@@ -432,6 +432,7 @@ function _isStreamInput () {
|
|||||||
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
||||||
* - `pageHeight`: Number of pixels high each page in a multi-page image will be.
|
* - `pageHeight`: Number of pixels high each page in a multi-page image will be.
|
||||||
|
* - `paletteBitDepth`: Bit depth of palette-based image (GIF, PNG).
|
||||||
* - `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
* - `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
|
||||||
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
|
||||||
* - `pagePrimary`: Number of the primary page in a HEIF image
|
* - `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
|
|||||||
28
lib/is.js
28
lib/is.js
@@ -138,18 +138,18 @@ const invalidParameterError = function (name, expected, actual) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
defined: defined,
|
defined,
|
||||||
object: object,
|
object,
|
||||||
plainObject: plainObject,
|
plainObject,
|
||||||
fn: fn,
|
fn,
|
||||||
bool: bool,
|
bool,
|
||||||
buffer: buffer,
|
buffer,
|
||||||
typedArray: typedArray,
|
typedArray,
|
||||||
arrayBuffer: arrayBuffer,
|
arrayBuffer,
|
||||||
string: string,
|
string,
|
||||||
number: number,
|
number,
|
||||||
integer: integer,
|
integer,
|
||||||
inRange: inRange,
|
inRange,
|
||||||
inArray: inArray,
|
inArray,
|
||||||
invalidParameterError: invalidParameterError
|
invalidParameterError
|
||||||
};
|
};
|
||||||
|
|||||||
131
lib/libvips.js
131
lib/libvips.js
@@ -3,53 +3,30 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const fs = require('fs');
|
const { spawnSync } = require('node:child_process');
|
||||||
const os = require('os');
|
|
||||||
const path = require('path');
|
|
||||||
const spawnSync = require('child_process').spawnSync;
|
|
||||||
const semverCoerce = require('semver/functions/coerce');
|
const semverCoerce = require('semver/functions/coerce');
|
||||||
const semverGreaterThanOrEqualTo = require('semver/functions/gte');
|
const semverGreaterThanOrEqualTo = require('semver/functions/gte');
|
||||||
|
const detectLibc = require('detect-libc');
|
||||||
|
|
||||||
const platform = require('./platform');
|
const { engines } = require('../package.json');
|
||||||
const { config } = require('../package.json');
|
|
||||||
|
|
||||||
const env = process.env;
|
const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || /* istanbul ignore next */
|
||||||
const minimumLibvipsVersionLabelled = env.npm_package_config_libvips || /* istanbul ignore next */
|
engines.libvips;
|
||||||
config.libvips;
|
|
||||||
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
|
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
|
||||||
|
|
||||||
|
const prebuiltPlatforms = [
|
||||||
|
'darwin-arm64', 'darwin-x64',
|
||||||
|
'linux-arm', 'linux-arm64', 'linux-x64',
|
||||||
|
'linuxmusl-arm64', 'linuxmusl-x64',
|
||||||
|
'win32-ia32', 'win32-x64'
|
||||||
|
];
|
||||||
|
|
||||||
const spawnSyncOptions = {
|
const spawnSyncOptions = {
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
shell: true
|
shell: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
|
const log = (item) => {
|
||||||
|
|
||||||
const mkdirSync = function (dirPath) {
|
|
||||||
try {
|
|
||||||
fs.mkdirSync(dirPath, { recursive: true });
|
|
||||||
} catch (err) {
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (err.code !== 'EEXIST') {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const cachePath = function () {
|
|
||||||
const npmCachePath = env.npm_config_cache || /* istanbul ignore next */
|
|
||||||
(env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
|
|
||||||
mkdirSync(npmCachePath);
|
|
||||||
const libvipsCachePath = path.join(npmCachePath, '_libvips');
|
|
||||||
mkdirSync(libvipsCachePath);
|
|
||||||
return libvipsCachePath;
|
|
||||||
};
|
|
||||||
|
|
||||||
const integrity = function (platformAndArch) {
|
|
||||||
return env[`npm_package_config_integrity_${platformAndArch.replace('-', '_')}`] || config.integrity[platformAndArch];
|
|
||||||
};
|
|
||||||
|
|
||||||
const log = function (item) {
|
|
||||||
if (item instanceof Error) {
|
if (item instanceof Error) {
|
||||||
console.error(`sharp: Installation error: ${item.message}`);
|
console.error(`sharp: Installation error: ${item.message}`);
|
||||||
} else {
|
} else {
|
||||||
@@ -57,7 +34,43 @@ const log = function (item) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isRosetta = function () {
|
/* istanbul ignore next */
|
||||||
|
const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
|
||||||
|
|
||||||
|
const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
const buildPlatformArch = () => {
|
||||||
|
/* eslint camelcase: ["error", { allow: ["^npm_config_"] }] */
|
||||||
|
const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
|
||||||
|
return `${npm_config_platform || process.platform}${npm_config_libc || runtimeLibc()}-${npm_config_arch || process.arch}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildSharpLibvipsIncludeDir = () => {
|
||||||
|
try {
|
||||||
|
return require('@sharpen/sharp-libvips-dev/include');
|
||||||
|
} catch {}
|
||||||
|
/* istanbul ignore next */
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildSharpLibvipsCPlusPlusDir = () => {
|
||||||
|
try {
|
||||||
|
return require('@sharpen/sharp-libvips-dev/cplusplus');
|
||||||
|
} catch {}
|
||||||
|
/* istanbul ignore next */
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildSharpLibvipsLibDir = () => {
|
||||||
|
try {
|
||||||
|
return require(`@sharpen/sharp-libvips-${buildPlatformArch()}/lib`);
|
||||||
|
} catch {}
|
||||||
|
/* istanbul ignore next */
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const isRosetta = () => {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (process.platform === 'darwin' && process.arch === 'x64') {
|
if (process.platform === 'darwin' && process.arch === 'x64') {
|
||||||
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
|
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
|
||||||
@@ -66,12 +79,19 @@ const isRosetta = function () {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const globalLibvipsVersion = function () {
|
/* istanbul ignore next */
|
||||||
|
const gypRebuild = () =>
|
||||||
|
spawnSync('node-gyp rebuild', {
|
||||||
|
...spawnSyncOptions,
|
||||||
|
stdio: 'inherit'
|
||||||
|
}).status;
|
||||||
|
|
||||||
|
const globalLibvipsVersion = () => {
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
|
const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
|
||||||
...spawnSyncOptions,
|
...spawnSyncOptions,
|
||||||
env: {
|
env: {
|
||||||
...env,
|
...process.env,
|
||||||
PKG_CONFIG_PATH: pkgConfigPath()
|
PKG_CONFIG_PATH: pkgConfigPath()
|
||||||
}
|
}
|
||||||
}).stdout;
|
}).stdout;
|
||||||
@@ -82,17 +102,8 @@ const globalLibvipsVersion = function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasVendoredLibvips = function () {
|
|
||||||
return fs.existsSync(vendorPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
const removeVendoredLibvips = function () {
|
const pkgConfigPath = () => {
|
||||||
fs.rmSync(vendorPath, { recursive: true, maxRetries: 3, force: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
|
||||||
const pkgConfigPath = function () {
|
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
const brewPkgConfigPath = spawnSync(
|
const brewPkgConfigPath = spawnSync(
|
||||||
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
|
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
|
||||||
@@ -100,7 +111,7 @@ const pkgConfigPath = function () {
|
|||||||
).stdout || '';
|
).stdout || '';
|
||||||
return [
|
return [
|
||||||
brewPkgConfigPath.trim(),
|
brewPkgConfigPath.trim(),
|
||||||
env.PKG_CONFIG_PATH,
|
process.env.PKG_CONFIG_PATH,
|
||||||
'/usr/local/lib/pkgconfig',
|
'/usr/local/lib/pkgconfig',
|
||||||
'/usr/lib/pkgconfig',
|
'/usr/lib/pkgconfig',
|
||||||
'/usr/local/libdata/pkgconfig',
|
'/usr/local/libdata/pkgconfig',
|
||||||
@@ -111,8 +122,9 @@ const pkgConfigPath = function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const useGlobalLibvips = function () {
|
const useGlobalLibvips = () => {
|
||||||
if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
|
if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
|
||||||
|
log('Detected SHARP_IGNORE_GLOBAL_LIBVIPS, skipping search for globally-installed libvips');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
@@ -127,14 +139,15 @@ const useGlobalLibvips = function () {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
minimumLibvipsVersion,
|
minimumLibvipsVersion,
|
||||||
minimumLibvipsVersionLabelled,
|
prebuiltPlatforms,
|
||||||
cachePath,
|
buildPlatformArch,
|
||||||
integrity,
|
buildSharpLibvipsIncludeDir,
|
||||||
|
buildSharpLibvipsCPlusPlusDir,
|
||||||
|
buildSharpLibvipsLibDir,
|
||||||
|
runtimePlatformArch,
|
||||||
log,
|
log,
|
||||||
|
gypRebuild,
|
||||||
globalLibvipsVersion,
|
globalLibvipsVersion,
|
||||||
hasVendoredLibvips,
|
|
||||||
removeVendoredLibvips,
|
|
||||||
pkgConfigPath,
|
pkgConfigPath,
|
||||||
useGlobalLibvips,
|
useGlobalLibvips
|
||||||
mkdirSync
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const is = require('./is');
|
|||||||
* If no angle is provided, it is determined from the EXIF data.
|
* If no angle is provided, it is determined from the EXIF data.
|
||||||
* Mirroring is supported and may infer the use of a flip operation.
|
* Mirroring is supported and may infer the use of a flip operation.
|
||||||
*
|
*
|
||||||
* The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
|
* The use of `rotate` without an angle will remove the EXIF `Orientation` tag, if any.
|
||||||
*
|
*
|
||||||
* Only one rotation can occur per pipeline.
|
* Only one rotation can occur per pipeline.
|
||||||
* Previous calls to `rotate` in the same pipeline will be ignored.
|
* Previous calls to `rotate` in the same pipeline will be ignored.
|
||||||
@@ -83,8 +83,6 @@ function rotate (angle, options) {
|
|||||||
* Mirror the image vertically (up-down) about the x-axis.
|
* Mirror the image vertically (up-down) about the x-axis.
|
||||||
* This always occurs before rotation, if any.
|
* This always occurs before rotation, if any.
|
||||||
*
|
*
|
||||||
* The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
|
||||||
*
|
|
||||||
* This operation does not work correctly with multi-page images.
|
* This operation does not work correctly with multi-page images.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
@@ -102,8 +100,6 @@ function flip (flip) {
|
|||||||
* Mirror the image horizontally (left-right) about the y-axis.
|
* Mirror the image horizontally (left-right) about the y-axis.
|
||||||
* This always occurs before rotation, if any.
|
* This always occurs before rotation, if any.
|
||||||
*
|
*
|
||||||
* The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
|
||||||
*
|
|
||||||
* @example
|
* @example
|
||||||
* const output = await sharp(input).flop().toBuffer();
|
* const output = await sharp(input).flop().toBuffer();
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('node:path');
|
||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
const sharp = require('./sharp');
|
const sharp = require('./sharp');
|
||||||
|
|
||||||
@@ -162,8 +162,8 @@ function toBuffer (options, callback) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
* This will also convert to and add a web-friendly sRGB ICC profile unless a custom
|
* This will also convert to and add a web-friendly sRGB ICC profile if appropriate,
|
||||||
* output profile is provided.
|
* unless a custom output profile is provided.
|
||||||
*
|
*
|
||||||
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
||||||
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
||||||
@@ -867,9 +867,6 @@ function tiff (options) {
|
|||||||
/**
|
/**
|
||||||
* Use these AVIF options for output image.
|
* Use these AVIF options for output image.
|
||||||
*
|
*
|
||||||
* Whilst it is possible to create AVIF images smaller than 16x16 pixels,
|
|
||||||
* most web browsers do not display these properly.
|
|
||||||
*
|
|
||||||
* AVIF image sequences are not supported.
|
* AVIF image sequences are not supported.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
@@ -909,9 +906,9 @@ function avif (options) {
|
|||||||
*
|
*
|
||||||
* @since 0.23.0
|
* @since 0.23.0
|
||||||
*
|
*
|
||||||
* @param {Object} [options] - output options
|
* @param {Object} options - output options
|
||||||
|
* @param {string} options.compression - compression format: av1, hevc
|
||||||
* @param {number} [options.quality=50] - quality, integer 1-100
|
* @param {number} [options.quality=50] - quality, integer 1-100
|
||||||
* @param {string} [options.compression='av1'] - compression format: av1, hevc
|
|
||||||
* @param {boolean} [options.lossless=false] - use lossless compression
|
* @param {boolean} [options.lossless=false] - use lossless compression
|
||||||
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
|
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
|
||||||
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
|
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
|
||||||
@@ -920,6 +917,11 @@ function avif (options) {
|
|||||||
*/
|
*/
|
||||||
function heif (options) {
|
function heif (options) {
|
||||||
if (is.object(options)) {
|
if (is.object(options)) {
|
||||||
|
if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
|
||||||
|
this.options.heifCompression = options.compression;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
|
||||||
|
}
|
||||||
if (is.defined(options.quality)) {
|
if (is.defined(options.quality)) {
|
||||||
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||||
this.options.heifQuality = options.quality;
|
this.options.heifQuality = options.quality;
|
||||||
@@ -934,13 +936,6 @@ function heif (options) {
|
|||||||
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
|
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is.defined(options.compression)) {
|
|
||||||
if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
|
|
||||||
this.options.heifCompression = options.compression;
|
|
||||||
} else {
|
|
||||||
throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (is.defined(options.effort)) {
|
if (is.defined(options.effort)) {
|
||||||
if (is.integer(options.effort) && is.inRange(options.effort, 0, 9)) {
|
if (is.integer(options.effort) && is.inRange(options.effort, 0, 9)) {
|
||||||
this.options.heifEffort = options.effort;
|
this.options.heifEffort = options.effort;
|
||||||
@@ -955,6 +950,8 @@ function heif (options) {
|
|||||||
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
|
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('options', 'Object', options);
|
||||||
}
|
}
|
||||||
return this._updateFormatOut('heif', options);
|
return this._updateFormatOut('heif', options);
|
||||||
}
|
}
|
||||||
@@ -1046,6 +1043,7 @@ function jxl (options) {
|
|||||||
*
|
*
|
||||||
* @param {Object} [options] - output options
|
* @param {Object} [options] - output options
|
||||||
* @param {string} [options.depth='uchar'] - bit depth, one of: char, uchar (default), short, ushort, int, uint, float, complex, double, dpcomplex
|
* @param {string} [options.depth='uchar'] - bit depth, one of: char, uchar (default), short, ushort, int, uint, float, complex, double, dpcomplex
|
||||||
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
*/
|
*/
|
||||||
function raw (options) {
|
function raw (options) {
|
||||||
@@ -1369,7 +1367,7 @@ function _pipeline (callback) {
|
|||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
if (this.options.resolveWithObject) {
|
if (this.options.resolveWithObject) {
|
||||||
resolve({ data: data, info: info });
|
resolve({ data, info });
|
||||||
} else {
|
} else {
|
||||||
resolve(data);
|
resolve(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const detectLibc = require('detect-libc');
|
|
||||||
|
|
||||||
const env = process.env;
|
|
||||||
|
|
||||||
module.exports = function () {
|
|
||||||
const arch = env.npm_config_arch || process.arch;
|
|
||||||
const platform = env.npm_config_platform || process.platform;
|
|
||||||
const libc = process.env.npm_config_libc ||
|
|
||||||
/* istanbul ignore next */
|
|
||||||
(detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '');
|
|
||||||
const libcId = platform !== 'linux' || libc === detectLibc.GLIBC ? '' : libc;
|
|
||||||
|
|
||||||
const platformId = [`${platform}${libcId}`];
|
|
||||||
|
|
||||||
if (arch === 'arm') {
|
|
||||||
const fallback = process.versions.electron ? '7' : '6';
|
|
||||||
platformId.push(`armv${env.npm_config_arm_version || process.config.variables.arm_version || fallback}`);
|
|
||||||
} else if (arch === 'arm64') {
|
|
||||||
platformId.push(`arm64v${env.npm_config_arm_version || '8'}`);
|
|
||||||
} else {
|
|
||||||
platformId.push(arch);
|
|
||||||
}
|
|
||||||
|
|
||||||
return platformId.join('-');
|
|
||||||
};
|
|
||||||
@@ -126,7 +126,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.png">
|
* <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">
|
||||||
*
|
*
|
||||||
* 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`.
|
||||||
@@ -250,6 +250,9 @@ function resize (widthOrOptions, height, options) {
|
|||||||
if (isResizeExpected(this.options)) {
|
if (isResizeExpected(this.options)) {
|
||||||
this.options.debuglog('ignoring previous resize options');
|
this.options.debuglog('ignoring previous resize options');
|
||||||
}
|
}
|
||||||
|
if (this.options.widthPost !== -1) {
|
||||||
|
this.options.debuglog('operation order will be: extract, resize, extract');
|
||||||
|
}
|
||||||
if (is.defined(widthOrOptions)) {
|
if (is.defined(widthOrOptions)) {
|
||||||
if (is.object(widthOrOptions) && !is.defined(options)) {
|
if (is.object(widthOrOptions) && !is.defined(options)) {
|
||||||
options = widthOrOptions;
|
options = widthOrOptions;
|
||||||
@@ -437,7 +440,7 @@ function extend (extend) {
|
|||||||
*
|
*
|
||||||
* - Use `extract` before `resize` for pre-resize extraction.
|
* - Use `extract` before `resize` for pre-resize extraction.
|
||||||
* - Use `extract` after `resize` for post-resize extraction.
|
* - Use `extract` after `resize` for post-resize extraction.
|
||||||
* - Use `extract` before and after for both.
|
* - Use `extract` twice and `resize` once for extract-then-resize-then-extract in a fixed operation order.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* sharp(input)
|
* sharp(input)
|
||||||
|
|||||||
74
lib/sharp.js
74
lib/sharp.js
@@ -3,36 +3,58 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const platformAndArch = require('./platform')();
|
// Inspects the runtime environment and exports the relevant sharp.node binary
|
||||||
|
|
||||||
|
const { familySync, versionSync } = require('detect-libc');
|
||||||
|
|
||||||
|
const { runtimePlatformArch, prebuiltPlatforms, minimumLibvipsVersion } = require('./libvips');
|
||||||
|
const runtimePlatform = runtimePlatformArch();
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
try {
|
try {
|
||||||
module.exports = require(`../build/Release/sharp-${platformAndArch}.node`);
|
// Check for local build
|
||||||
} catch (err) {
|
module.exports = require(`../build/Release/sharp-${runtimePlatform}.node`);
|
||||||
// Bail early if bindings aren't available
|
} catch (errLocal) {
|
||||||
const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, '', 'Possible solutions:'];
|
try {
|
||||||
if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
|
// Check for runtime package
|
||||||
help.push('- Update Homebrew: "brew update && brew upgrade vips"');
|
module.exports = require(`@sharpen/sharp-${runtimePlatform}/sharp.node`);
|
||||||
} else {
|
} catch (errPackage) {
|
||||||
const [platform, arch] = platformAndArch.split('-');
|
const help = ['Could not load the "sharp" module at runtime'];
|
||||||
if (platform === 'linux' && /Module did not self-register/.test(err.message)) {
|
if (errLocal.code !== 'MODULE_NOT_FOUND') {
|
||||||
help.push('- Using worker threads? See https://sharp.pixelplumbing.com/install#worker-threads');
|
help.push(`${errLocal.code}: ${errLocal.message}`);
|
||||||
}
|
}
|
||||||
help.push(
|
if (errPackage.code !== 'MODULE_NOT_FOUND') {
|
||||||
'- Install with verbose logging and look for errors: "npm install --ignore-scripts=false --foreground-scripts --verbose sharp"',
|
help.push(`${errPackage.code}: ${errPackage.message}`);
|
||||||
`- Install for the current ${platformAndArch} runtime: "npm install --platform=${platform} --arch=${arch} sharp"`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
help.push(
|
|
||||||
'- Consult the installation documentation: https://sharp.pixelplumbing.com/install'
|
|
||||||
);
|
|
||||||
// Check loaded
|
|
||||||
if (process.platform === 'win32' || /symbol/.test(err.message)) {
|
|
||||||
const loadedModule = Object.keys(require.cache).find((i) => /[\\/]build[\\/]Release[\\/]sharp(.*)\.node$/.test(i));
|
|
||||||
if (loadedModule) {
|
|
||||||
const [, loadedPackage] = loadedModule.match(/node_modules[\\/]([^\\/]+)[\\/]/);
|
|
||||||
help.push(`- Ensure the version of sharp aligns with the ${loadedPackage} package: "npm ls sharp"`);
|
|
||||||
}
|
}
|
||||||
|
help.push('Possible solutions:');
|
||||||
|
// Common error messages
|
||||||
|
if (prebuiltPlatforms.includes(runtimePlatform)) {
|
||||||
|
help.push(`- Add an explicit dependency for the runtime platform: "npm install --force @sharpen/sharp-${runtimePlatform}"`);
|
||||||
|
} else {
|
||||||
|
help.push(`- The ${runtimePlatform} platform requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
||||||
|
}
|
||||||
|
if (runtimePlatform.startsWith('linux') && /symbol not found/i.test(errPackage)) {
|
||||||
|
try {
|
||||||
|
const { engines } = require(`@sharpen/sharp-libvips-${runtimePlatform}/package`);
|
||||||
|
const libcFound = `${familySync()} ${versionSync()}`;
|
||||||
|
const libcRequires = `${engines.musl ? 'musl' : 'glibc'} ${engines.musl || engines.glibc}`;
|
||||||
|
help.push(`- Update your OS: found ${libcFound}, requires ${libcRequires}`);
|
||||||
|
} catch (errEngines) {}
|
||||||
|
}
|
||||||
|
if (runtimePlatform.startsWith('darwin') && /Incompatible library version/.test(errLocal.message)) {
|
||||||
|
help.push('- Update Homebrew: "brew update && brew upgrade vips"');
|
||||||
|
}
|
||||||
|
if (errPackage.code === 'ERR_DLOPEN_DISABLED') {
|
||||||
|
help.push('- Run Node.js without using the --no-addons flag');
|
||||||
|
}
|
||||||
|
// Link to installation docs
|
||||||
|
if (runtimePlatform.startsWith('linux') && /Module did not self-register/.test(errLocal.message + errPackage.message)) {
|
||||||
|
help.push('- Using worker threads on Linux? See https://sharp.pixelplumbing.com/install#worker-threads');
|
||||||
|
} else if (runtimePlatform.startsWith('win32') && /The specified procedure could not be found/.test(errPackage.message)) {
|
||||||
|
help.push('- Using the canvas package on Windows? See https://sharp.pixelplumbing.com/install#canvas-and-windows');
|
||||||
|
} else {
|
||||||
|
help.push('- Consult the installation documentation: https://sharp.pixelplumbing.com/install');
|
||||||
|
}
|
||||||
|
throw new Error(help.join('\n'));
|
||||||
}
|
}
|
||||||
throw new Error(help.join('\n'));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,11 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const fs = require('fs');
|
const events = require('node:events');
|
||||||
const path = require('path');
|
|
||||||
const events = require('events');
|
|
||||||
const detectLibc = require('detect-libc');
|
const detectLibc = require('detect-libc');
|
||||||
|
|
||||||
const is = require('./is');
|
const is = require('./is');
|
||||||
const platformAndArch = require('./platform')();
|
const { runtimePlatformArch } = require('./libvips');
|
||||||
const sharp = require('./sharp');
|
const sharp = require('./sharp');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,25 +53,14 @@ let versions = {
|
|||||||
vips: sharp.libvipsVersion()
|
vips: sharp.libvipsVersion()
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
versions = require(`../vendor/${versions.vips}/${platformAndArch}/versions.json`);
|
versions = require(`@sharpen/sharp-${runtimePlatformArch()}/versions`);
|
||||||
} catch (_err) { /* ignore */ }
|
} catch (_) {
|
||||||
|
try {
|
||||||
|
versions = require(`@sharpen/sharp-libvips-${runtimePlatformArch()}/versions`);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
versions.sharp = require('../package.json').version;
|
versions.sharp = require('../package.json').version;
|
||||||
|
|
||||||
/**
|
|
||||||
* An Object containing the platform and architecture
|
|
||||||
* of the current and installed vendored binaries.
|
|
||||||
* @member
|
|
||||||
* @example
|
|
||||||
* console.log(sharp.vendor);
|
|
||||||
*/
|
|
||||||
const vendor = {
|
|
||||||
current: platformAndArch,
|
|
||||||
installed: []
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
vendor.installed = fs.readdirSync(path.join(__dirname, `../vendor/${versions.vips}`));
|
|
||||||
} catch (_err) { /* ignore */ }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
|
* Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
|
||||||
* Existing entries in the cache will be trimmed after any change in limits.
|
* Existing entries in the cache will be trimmed after any change in limits.
|
||||||
@@ -280,7 +267,6 @@ module.exports = function (Sharp) {
|
|||||||
Sharp.format = format;
|
Sharp.format = format;
|
||||||
Sharp.interpolators = interpolators;
|
Sharp.interpolators = interpolators;
|
||||||
Sharp.versions = versions;
|
Sharp.versions = versions;
|
||||||
Sharp.vendor = vendor;
|
|
||||||
Sharp.queue = queue;
|
Sharp.queue = queue;
|
||||||
Sharp.block = block;
|
Sharp.block = block;
|
||||||
Sharp.unblock = unblock;
|
Sharp.unblock = unblock;
|
||||||
|
|||||||
43
npm/darwin-arm64/package.json
Normal file
43
npm/darwin-arm64/package.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp-darwin-arm64",
|
||||||
|
"version": "0.0.1-alpha.3",
|
||||||
|
"description": "Prebuilt sharp for use with macOS ARM64",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/darwin-arm64"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@sharpen/sharp-libvips-darwin-arm64": "0.0.1-alpha.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-darwin-arm64.node",
|
||||||
|
"./package": "./package.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"yarn": ">=3.2.0",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"glibc": ">=2.26"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
}
|
||||||
43
npm/darwin-x64/package.json
Normal file
43
npm/darwin-x64/package.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp-darwin-x64",
|
||||||
|
"version": "0.0.1-alpha.3",
|
||||||
|
"description": "Prebuilt sharp for use with macOS x64",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/darwin-x64"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@sharpen/sharp-libvips-darwin-x64": "0.0.1-alpha.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-darwin-x64.node",
|
||||||
|
"./package": "./package.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"yarn": ">=3.2.0",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"glibc": ">=2.26"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
|
}
|
||||||
70
npm/from-github-release.js
Normal file
70
npm/from-github-release.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// 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 { writeFile, 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 url = `https://github.com/lovell/sharp/releases/download/v${version}/sharp-v${version}-napi-v9-${platform}.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, { 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 Windows-specific files
|
||||||
|
if (platform.startsWith('win32-')) {
|
||||||
|
const sharpLibvipsDir = path.join(require(`@sharpen/sharp-libvips-${platform}/lib`), '..');
|
||||||
|
await Promise.all(
|
||||||
|
['versions.json', 'THIRD-PARTY-NOTICES.md'].map(
|
||||||
|
filename => copyFile(path.join(sharpLibvipsDir, filename), path.join(dir, filename))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
26
npm/from-local-build.js
Normal file
26
npm/from-local-build.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2013 Lovell Fuller and others.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Populate contents of a single npm/sharpen-sharp-<build-platform> package
|
||||||
|
// with the local/CI build directory for local/CI prebuild testing
|
||||||
|
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const { buildPlatformArch } = require('../lib/libvips');
|
||||||
|
const platform = buildPlatformArch();
|
||||||
|
const dest = path.join(__dirname, platform);
|
||||||
|
|
||||||
|
// Use same config as prebuild to copy binary files
|
||||||
|
const release = path.join(__dirname, '..', 'build', 'Release');
|
||||||
|
const prebuildrc = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '.prebuildrc'), 'utf8'));
|
||||||
|
const include = new RegExp(prebuildrc['include-regex'], 'i');
|
||||||
|
fs.cpSync(release, path.join(dest, 'lib'), {
|
||||||
|
recursive: true,
|
||||||
|
filter: (file) => {
|
||||||
|
const name = path.basename(file);
|
||||||
|
return name === 'Release' || include.test(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
46
npm/linux-arm/package.json
Normal file
46
npm/linux-arm/package.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp-linux-arm",
|
||||||
|
"version": "0.0.1-alpha.3",
|
||||||
|
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/linux-arm"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@sharpen/sharp-libvips-linux-arm": "0.0.1-alpha.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-linux-arm.node",
|
||||||
|
"./package": "./package.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"yarn": ">=3.2.0",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"glibc": ">=2.28"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
]
|
||||||
|
}
|
||||||
46
npm/linux-arm64/package.json
Normal file
46
npm/linux-arm64/package.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp-linux-arm64",
|
||||||
|
"version": "0.0.1-alpha.3",
|
||||||
|
"description": "Prebuilt sharp for use with Linux (glibc) ARM64",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/linux-arm64"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@sharpen/sharp-libvips-linux-arm64": "0.0.1-alpha.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-linux-arm64.node",
|
||||||
|
"./package": "./package.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"yarn": ">=3.2.0",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"glibc": ">=2.26"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
}
|
||||||
46
npm/linux-x64/package.json
Normal file
46
npm/linux-x64/package.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp-linux-x64",
|
||||||
|
"version": "0.0.1-alpha.3",
|
||||||
|
"description": "Prebuilt sharp for use with Linux (glibc) x64",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/linux-x64"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@sharpen/sharp-libvips-linux-x64": "0.0.1-alpha.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-linux-x64.node",
|
||||||
|
"./package": "./package.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"yarn": ">=3.2.0",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"glibc": ">=2.26"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
|
}
|
||||||
46
npm/linuxmusl-arm64/package.json
Normal file
46
npm/linuxmusl-arm64/package.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp-linuxmusl-arm64",
|
||||||
|
"version": "0.0.1-alpha.3",
|
||||||
|
"description": "Prebuilt sharp for use with Linux (musl) ARM64",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/linuxmusl-arm64"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@sharpen/sharp-libvips-linuxmusl-arm64": "0.0.1-alpha.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-linuxmusl-arm64.node",
|
||||||
|
"./package": "./package.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"yarn": ">=3.2.0",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"musl": ">=1.2.2"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
}
|
||||||
46
npm/linuxmusl-x64/package.json
Normal file
46
npm/linuxmusl-x64/package.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp-linuxmusl-x64",
|
||||||
|
"version": "0.0.1-alpha.3",
|
||||||
|
"description": "Prebuilt sharp for use with Linux (musl) x64",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/linuxmusl-x64"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@sharpen/sharp-libvips-linuxmusl-x64": "0.0.1-alpha.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-linuxmusl-x64.node",
|
||||||
|
"./package": "./package.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"yarn": ">=3.2.0",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"musl": ">=1.2.2"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
|
}
|
||||||
16
npm/package.json
Normal file
16
npm/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp",
|
||||||
|
"version": "0.0.1-alpha.1",
|
||||||
|
"private": "true",
|
||||||
|
"workspaces": [
|
||||||
|
"darwin-x64",
|
||||||
|
"darwin-arm64",
|
||||||
|
"linux-arm",
|
||||||
|
"linux-arm64",
|
||||||
|
"linuxmusl-arm64",
|
||||||
|
"linuxmusl-x64",
|
||||||
|
"linux-x64",
|
||||||
|
"win32-ia32",
|
||||||
|
"win32-x64"
|
||||||
|
]
|
||||||
|
}
|
||||||
42
npm/win32-ia32/package.json
Normal file
42
npm/win32-ia32/package.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp-win32-ia32",
|
||||||
|
"version": "0.0.1-alpha.3",
|
||||||
|
"description": "Prebuilt sharp for use with Windows x86 (32-bit)",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/win32-ia32"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"versions.json",
|
||||||
|
"THIRD-PARTY-NOTICES.md"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-win32-ia32.node",
|
||||||
|
"./package": "./package.json",
|
||||||
|
"./versions": "./versions.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"yarn": ">=3.2.0",
|
||||||
|
"pnpm": ">=7.1.0"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
]
|
||||||
|
}
|
||||||
41
npm/win32-x64/package.json
Normal file
41
npm/win32-x64/package.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "@sharpen/sharp-win32-x64",
|
||||||
|
"version": "0.0.1-alpha.3",
|
||||||
|
"description": "Prebuilt sharp for use with Windows x64",
|
||||||
|
"homepage": "https://sharp.pixelplumbing.com",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/lovell/sharp.git",
|
||||||
|
"directory": "npm/win32-x64"
|
||||||
|
},
|
||||||
|
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"preferUnplugged": true,
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"versions.json",
|
||||||
|
"THIRD-PARTY-NOTICES.md"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
"./sharp.node": "./lib/sharp-win32-x64.node",
|
||||||
|
"./package": "./package.json"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"yarn": ">=3.2.0",
|
||||||
|
"pnpm": ">=7.1.0"
|
||||||
|
},
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
|
}
|
||||||
81
package.json
81
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.32.4",
|
"version": "0.33.0-alpha.3",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://github.com/lovell/sharp",
|
"homepage": "https://github.com/lovell/sharp",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@@ -89,29 +89,32 @@
|
|||||||
"Lachlan Newman <lachnewman007@gmail.com>"
|
"Lachlan Newman <lachnewman007@gmail.com>"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
|
"install": "node install/check",
|
||||||
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
|
"clean": "rm -rf 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",
|
||||||
"test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha",
|
"test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha",
|
||||||
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
|
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;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-github-release": "node npm/from-github-release",
|
||||||
"docs-build": "node docs/build && node docs/search-index/build",
|
"docs-build": "node docs/build && node docs/search-index/build",
|
||||||
"docs-serve": "cd docs && npx serve",
|
"docs-serve": "cd docs && npx serve",
|
||||||
"docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
|
"docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
|
||||||
},
|
},
|
||||||
|
"type": "commonjs",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"binding.gyp",
|
"binding.gyp",
|
||||||
"install/**",
|
"install",
|
||||||
"lib/**",
|
"lib",
|
||||||
"src/**"
|
"src"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/lovell/sharp"
|
"url": "git://github.com/lovell/sharp.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"jpeg",
|
"jpeg",
|
||||||
@@ -134,57 +137,57 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"detect-libc": "^2.0.2",
|
"detect-libc": "^2.0.2",
|
||||||
"node-addon-api": "^6.1.0",
|
"node-addon-api": "^7.0.0",
|
||||||
"prebuild-install": "^7.1.1",
|
"semver": "^7.5.4"
|
||||||
"semver": "^7.5.4",
|
},
|
||||||
"simple-get": "^4.0.1",
|
"optionalDependencies": {
|
||||||
"tar-fs": "^3.0.4",
|
"@sharpen/sharp-darwin-arm64": "0.0.1-alpha.3",
|
||||||
"tunnel-agent": "^0.6.0"
|
"@sharpen/sharp-darwin-x64": "0.0.1-alpha.3",
|
||||||
|
"@sharpen/sharp-linux-arm": "0.0.1-alpha.3",
|
||||||
|
"@sharpen/sharp-linux-arm64": "0.0.1-alpha.3",
|
||||||
|
"@sharpen/sharp-linux-x64": "0.0.1-alpha.3",
|
||||||
|
"@sharpen/sharp-linuxmusl-arm64": "0.0.1-alpha.3",
|
||||||
|
"@sharpen/sharp-linuxmusl-x64": "0.0.1-alpha.3",
|
||||||
|
"@sharpen/sharp-win32-ia32": "0.0.1-alpha.3",
|
||||||
|
"@sharpen/sharp-win32-x64": "0.0.1-alpha.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@sharpen/sharp-libvips-darwin-arm64": "0.0.1-alpha.1",
|
||||||
|
"@sharpen/sharp-libvips-darwin-x64": "0.0.1-alpha.1",
|
||||||
|
"@sharpen/sharp-libvips-dev": "0.0.1-alpha.1",
|
||||||
|
"@sharpen/sharp-libvips-linux-arm": "0.0.1-alpha.1",
|
||||||
|
"@sharpen/sharp-libvips-linux-arm64": "0.0.1-alpha.1",
|
||||||
|
"@sharpen/sharp-libvips-linux-x64": "0.0.1-alpha.1",
|
||||||
|
"@sharpen/sharp-libvips-linuxmusl-arm64": "0.0.1-alpha.1",
|
||||||
|
"@sharpen/sharp-libvips-linuxmusl-x64": "0.0.1-alpha.1",
|
||||||
|
"@sharpen/sharp-libvips-win32-ia32": "0.0.1-alpha.1",
|
||||||
|
"@sharpen/sharp-libvips-win32-x64": "0.0.1-alpha.1",
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"async": "^3.2.4",
|
"async": "^3.2.4",
|
||||||
"cc": "^3.0.1",
|
"cc": "^3.0.1",
|
||||||
"exif-reader": "^1.2.0",
|
"exif-reader": "^2.0.0",
|
||||||
"extract-zip": "^2.0.1",
|
"extract-zip": "^2.0.1",
|
||||||
"icc": "^3.0.0",
|
"icc": "^3.0.0",
|
||||||
"jsdoc-to-markdown": "^8.0.0",
|
"jsdoc-to-markdown": "^8.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.2.0",
|
||||||
"mock-fs": "^5.2.0",
|
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"prebuild": "lovell/prebuild#add-nodejs-20-drop-nodejs-10-and-12",
|
"prebuild": "^12.1.0",
|
||||||
"semistandard": "^16.0.1",
|
"semistandard": "^17.0.0",
|
||||||
"tsd": "^0.28.1"
|
"tar-fs": "^3.0.4",
|
||||||
|
"tsd": "^0.29.0"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"config": {
|
|
||||||
"libvips": "8.14.3",
|
|
||||||
"integrity": {
|
|
||||||
"darwin-arm64v8": "sha512-zeb7jQ/5ARZfBH9Uy5wlpN05bFpiIN0qN3gIIpfJhpN0rhGDnjJZQgK0W+pOmG1YiLL42BMCS0SHldb0xE33rA==",
|
|
||||||
"darwin-x64": "sha512-C3N6smxdfprfz58cjojv0aekYXDl6+f9SwpGpxPG5RrZnrDMn5NOXtUQOEQ8PZ3Hd9VzfkJTnW/s36EvcMPfYg==",
|
|
||||||
"linux-arm64v8": "sha512-hT6B+OswqVQH10Fggq3jpOdn+GhxNx+5bk+EMr3lY3RZy72PZ+n4ZHJDfYSxAymdiz5rCdzGxsRLMb9GgD4OSw==",
|
|
||||||
"linux-armv6": "sha512-cW9giVrBssHXFt07l+PgqGu7P7XRDv7oW8jC6iXGBcjG75N7rXz2CK0DyPclfnyoWH4IQ78dh5SkQWmb6X4tig==",
|
|
||||||
"linux-armv7": "sha512-hgqFt3UkZHK6D91JtYrYmT1niznh+N93Zxj2EWXgTLAdcS1D3QqaDPEg2EhInHbXqYvfOuQYAAXPxt7zVtKqcw==",
|
|
||||||
"linux-x64": "sha512-FKbMBbCcFcSugRtuiTsA6Cov+M2WQ8nzvmmJ5xYYpRg/rsrWvObFT+6x/YBpblur9uXGjGIehjXVZtB3VXc+pg==",
|
|
||||||
"linuxmusl-arm64v8": "sha512-RTf6mrFyLGWnyt0DH4nHeXv5oSZMSJWxTdTt4cjvJsgp2Husz3mNJLQJGeehCuqPCYj/liJ9NIczw8u71eHFng==",
|
|
||||||
"linuxmusl-x64": "sha512-y/8UOkHzKhi/5UM1/ONyPvpuhO11nPQmuJWfzqUKj8kSKnDasmxv3FN46yI0XY3xA2oFC8lQNFBnLudQsi3Nvw==",
|
|
||||||
"win32-arm64v8": "sha512-D3PiVL981S7V0bSUwW3OqDS48H9QRw2vqQhYIY3JcIEssOnjWxmJGaz0Y9Zb8TYF5DHnnD6g5kEhob5Y2PIVEw==",
|
|
||||||
"win32-ia32": "sha512-FuLIaSIYJGJAcxyKkG/3/uuTzputekKSCcRCpRHkQS9J8IwM+yHzQeJ5W2PyAvNdeGIEwlYq3wnCNcXe1UGXWA==",
|
|
||||||
"win32-x64": "sha512-VQg4aBqpEfybgV8bjnrjfvnosxQDII/23mouFUfKHCsH5kvvHV5tTuPsxm6qbl+SCVploDK/zK1qpjop8YEvtg=="
|
|
||||||
},
|
|
||||||
"runtime": "napi",
|
|
||||||
"target": 7
|
|
||||||
},
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.15.0"
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"libvips": ">=8.14.5"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
},
|
},
|
||||||
"binary": {
|
"binary": {
|
||||||
"napi_versions": [
|
"napi_versions": [
|
||||||
7
|
9
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"semistandard": {
|
"semistandard": {
|
||||||
|
|||||||
@@ -166,10 +166,10 @@ namespace sharp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// How many tasks are in the queue?
|
// How many tasks are in the queue?
|
||||||
volatile int counterQueue = 0;
|
std::atomic<int> counterQueue{0};
|
||||||
|
|
||||||
// How many tasks are being processed?
|
// How many tasks are being processed?
|
||||||
volatile int counterProcess = 0;
|
std::atomic<int> counterProcess{0};
|
||||||
|
|
||||||
// Filename extension checkers
|
// Filename extension checkers
|
||||||
static bool EndsWith(std::string const &str, std::string const &end) {
|
static bool EndsWith(std::string const &str, std::string const &end) {
|
||||||
@@ -363,12 +363,13 @@ namespace sharp {
|
|||||||
if (descriptor->isBuffer) {
|
if (descriptor->isBuffer) {
|
||||||
if (descriptor->rawChannels > 0) {
|
if (descriptor->rawChannels > 0) {
|
||||||
// Raw, uncompressed pixel data
|
// Raw, uncompressed pixel data
|
||||||
|
bool const is8bit = vips_band_format_is8bit(descriptor->rawDepth);
|
||||||
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
|
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
|
||||||
descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, descriptor->rawDepth);
|
descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, descriptor->rawDepth);
|
||||||
if (descriptor->rawChannels < 3) {
|
if (descriptor->rawChannels < 3) {
|
||||||
image.get_image()->Type = VIPS_INTERPRETATION_B_W;
|
image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_GREY16;
|
||||||
} else {
|
} else {
|
||||||
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
|
image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16;
|
||||||
}
|
}
|
||||||
if (descriptor->rawPremultiplied) {
|
if (descriptor->rawPremultiplied) {
|
||||||
image = image.unpremultiply();
|
image = image.unpremultiply();
|
||||||
@@ -964,12 +965,7 @@ namespace sharp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
|
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
|
||||||
Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction) {
|
Canvas canvas, bool withoutEnlargement, bool withoutReduction) {
|
||||||
if (swap && canvas != Canvas::IGNORE_ASPECT) {
|
|
||||||
// Swap input width and height when requested.
|
|
||||||
std::swap(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
double hshrink = 1.0;
|
double hshrink = 1.0;
|
||||||
double vshrink = 1.0;
|
double vshrink = 1.0;
|
||||||
|
|
||||||
|
|||||||
16
src/common.h
16
src/common.h
@@ -7,6 +7,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include <napi.h>
|
#include <napi.h>
|
||||||
#include <vips/vips8>
|
#include <vips/vips8>
|
||||||
@@ -15,8 +16,8 @@
|
|||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION < 8) || \
|
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 14) || \
|
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 14) || \
|
||||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 14 && VIPS_MICRO_VERSION < 3)
|
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 14 && VIPS_MICRO_VERSION < 5)
|
||||||
#error "libvips version 8.14.3+ is required - please see https://sharp.pixelplumbing.com/install"
|
#error "libvips version 8.14.5+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
||||||
@@ -161,10 +162,10 @@ namespace sharp {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// How many tasks are in the queue?
|
// How many tasks are in the queue?
|
||||||
extern volatile int counterQueue;
|
extern std::atomic<int> counterQueue;
|
||||||
|
|
||||||
// How many tasks are being processed?
|
// How many tasks are being processed?
|
||||||
extern volatile int counterProcess;
|
extern std::atomic<int> counterProcess;
|
||||||
|
|
||||||
// Filename extension checkers
|
// Filename extension checkers
|
||||||
bool IsJpeg(std::string const &str);
|
bool IsJpeg(std::string const &str);
|
||||||
@@ -362,13 +363,10 @@ namespace sharp {
|
|||||||
VImage EnsureAlpha(VImage image, double const value);
|
VImage EnsureAlpha(VImage image, double const value);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Calculate the shrink factor, taking into account auto-rotate, the canvas
|
Calculate the horizontal and vertical shrink factors, taking the canvas mode into account.
|
||||||
mode, and so on. The hshrink/vshrink are the amount to shrink the input
|
|
||||||
image axes by in order for the output axes (ie. after rotation) to match
|
|
||||||
the required thumbnail width/height and canvas mode.
|
|
||||||
*/
|
*/
|
||||||
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
|
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
|
||||||
Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction);
|
Canvas canvas, bool withoutEnlargement, bool withoutReduction);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Ensure decoding remains sequential.
|
Ensure decoding remains sequential.
|
||||||
|
|||||||
@@ -1,151 +0,0 @@
|
|||||||
/* Object part of the VSource and VTarget class
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 1991-2001 The National Gallery
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
||||||
02110-1301 USA
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif /*HAVE_CONFIG_H*/
|
|
||||||
|
|
||||||
#include <vips/vips8>
|
|
||||||
|
|
||||||
#include <vips/debug.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define VIPS_DEBUG
|
|
||||||
#define VIPS_DEBUG_VERBOSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_START
|
|
||||||
|
|
||||||
VSource
|
|
||||||
VSource::new_from_descriptor( int descriptor )
|
|
||||||
{
|
|
||||||
VipsSource *input;
|
|
||||||
|
|
||||||
if( !(input = vips_source_new_from_descriptor( descriptor )) )
|
|
||||||
throw VError();
|
|
||||||
|
|
||||||
VSource out( input );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VSource
|
|
||||||
VSource::new_from_file( const char *filename )
|
|
||||||
{
|
|
||||||
VipsSource *input;
|
|
||||||
|
|
||||||
if( !(input = vips_source_new_from_file( filename )) )
|
|
||||||
throw VError();
|
|
||||||
|
|
||||||
VSource out( input );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VSource
|
|
||||||
VSource::new_from_blob( VipsBlob *blob )
|
|
||||||
{
|
|
||||||
VipsSource *input;
|
|
||||||
|
|
||||||
if( !(input = vips_source_new_from_blob( blob )) )
|
|
||||||
throw VError();
|
|
||||||
|
|
||||||
VSource out( input );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VSource
|
|
||||||
VSource::new_from_memory( const void *data,
|
|
||||||
size_t size )
|
|
||||||
{
|
|
||||||
VipsSource *input;
|
|
||||||
|
|
||||||
if( !(input = vips_source_new_from_memory( data, size )) )
|
|
||||||
throw VError();
|
|
||||||
|
|
||||||
VSource out( input );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VSource
|
|
||||||
VSource::new_from_options( const char *options )
|
|
||||||
{
|
|
||||||
VipsSource *input;
|
|
||||||
|
|
||||||
if( !(input = vips_source_new_from_options( options )) )
|
|
||||||
throw VError();
|
|
||||||
|
|
||||||
VSource out( input );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VTarget
|
|
||||||
VTarget::new_to_descriptor( int descriptor )
|
|
||||||
{
|
|
||||||
VipsTarget *output;
|
|
||||||
|
|
||||||
if( !(output = vips_target_new_to_descriptor( descriptor )) )
|
|
||||||
throw VError();
|
|
||||||
|
|
||||||
VTarget out( output );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VTarget
|
|
||||||
VTarget::new_to_file( const char *filename )
|
|
||||||
{
|
|
||||||
VipsTarget *output;
|
|
||||||
|
|
||||||
if( !(output = vips_target_new_to_file( filename )) )
|
|
||||||
throw VError();
|
|
||||||
|
|
||||||
VTarget out( output );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VTarget
|
|
||||||
VTarget::new_to_memory()
|
|
||||||
{
|
|
||||||
VipsTarget *output;
|
|
||||||
|
|
||||||
if( !(output = vips_target_new_to_memory()) )
|
|
||||||
throw VError();
|
|
||||||
|
|
||||||
VTarget out( output );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_END
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
// Code for error type
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 1991-2001 The National Gallery
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
||||||
02110-1301 USA
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif /*HAVE_CONFIG_H*/
|
|
||||||
|
|
||||||
#include <vips/vips8>
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_START
|
|
||||||
|
|
||||||
std::ostream &operator<<( std::ostream &file, const VError &err )
|
|
||||||
{
|
|
||||||
err.ostream_print( file );
|
|
||||||
return( file );
|
|
||||||
}
|
|
||||||
|
|
||||||
void VError::ostream_print( std::ostream &file ) const
|
|
||||||
{
|
|
||||||
file << _what;
|
|
||||||
}
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_END
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
|||||||
/* Object part of VInterpolate class
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 1991-2001 The National Gallery
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
||||||
02110-1301 USA
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif /*HAVE_CONFIG_H*/
|
|
||||||
|
|
||||||
#include <vips/vips8>
|
|
||||||
|
|
||||||
#include <vips/debug.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define VIPS_DEBUG
|
|
||||||
#define VIPS_DEBUG_VERBOSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_START
|
|
||||||
|
|
||||||
VInterpolate
|
|
||||||
VInterpolate::new_from_name( const char *name, VOption *options )
|
|
||||||
{
|
|
||||||
VipsInterpolate *interp;
|
|
||||||
|
|
||||||
if( !(interp = vips_interpolate_new( name )) ) {
|
|
||||||
delete options;
|
|
||||||
throw VError();
|
|
||||||
}
|
|
||||||
delete options;
|
|
||||||
|
|
||||||
VInterpolate out( interp );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_END
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// Object part of VRegion class
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include <config.h>
|
|
||||||
#endif /*HAVE_CONFIG_H*/
|
|
||||||
|
|
||||||
#include <vips/vips8>
|
|
||||||
|
|
||||||
#include <vips/debug.h>
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_START
|
|
||||||
|
|
||||||
VRegion
|
|
||||||
VRegion::new_from_image( VImage image )
|
|
||||||
{
|
|
||||||
VipsRegion *region;
|
|
||||||
|
|
||||||
if( !(region = vips_region_new( image.get_image() )) ) {
|
|
||||||
throw VError();
|
|
||||||
}
|
|
||||||
|
|
||||||
VRegion out( region );
|
|
||||||
|
|
||||||
return( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
VIPS_NAMESPACE_END
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|||||||
|
|
||||||
void Execute() {
|
void Execute() {
|
||||||
// Decrement queued task counter
|
// Decrement queued task counter
|
||||||
g_atomic_int_dec_and_test(&sharp::counterQueue);
|
sharp::counterQueue--;
|
||||||
|
|
||||||
vips::VImage image;
|
vips::VImage image;
|
||||||
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
|
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
|
||||||
@@ -281,7 +281,7 @@ Napi::Value metadata(const Napi::CallbackInfo& info) {
|
|||||||
worker->Queue();
|
worker->Queue();
|
||||||
|
|
||||||
// Increment queued task counter
|
// Increment queued task counter
|
||||||
g_atomic_int_inc(&sharp::counterQueue);
|
sharp::counterQueue++;
|
||||||
|
|
||||||
return info.Env().Undefined();
|
return info.Env().Undefined();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,18 +20,15 @@
|
|||||||
#include "operations.h"
|
#include "operations.h"
|
||||||
#include "pipeline.h"
|
#include "pipeline.h"
|
||||||
|
|
||||||
#if defined(WIN32)
|
#ifdef _WIN32
|
||||||
#define STAT64_STRUCT __stat64
|
#define STAT64_STRUCT __stat64
|
||||||
#define STAT64_FUNCTION _stat64
|
#define STAT64_FUNCTION _stat64
|
||||||
#elif defined(__APPLE__)
|
#elif defined(_LARGEFILE64_SOURCE)
|
||||||
#define STAT64_STRUCT stat
|
|
||||||
#define STAT64_FUNCTION stat
|
|
||||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
|
|
||||||
#define STAT64_STRUCT stat
|
|
||||||
#define STAT64_FUNCTION stat
|
|
||||||
#else
|
|
||||||
#define STAT64_STRUCT stat64
|
#define STAT64_STRUCT stat64
|
||||||
#define STAT64_FUNCTION stat64
|
#define STAT64_FUNCTION stat64
|
||||||
|
#else
|
||||||
|
#define STAT64_STRUCT stat
|
||||||
|
#define STAT64_FUNCTION stat
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class PipelineWorker : public Napi::AsyncWorker {
|
class PipelineWorker : public Napi::AsyncWorker {
|
||||||
@@ -47,9 +44,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// libuv worker
|
// libuv worker
|
||||||
void Execute() {
|
void Execute() {
|
||||||
// Decrement queued task counter
|
// Decrement queued task counter
|
||||||
g_atomic_int_dec_and_test(&sharp::counterQueue);
|
sharp::counterQueue--;
|
||||||
// Increment processing task counter
|
// Increment processing task counter
|
||||||
g_atomic_int_inc(&sharp::counterProcess);
|
sharp::counterProcess++;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Open input
|
// Open input
|
||||||
@@ -160,15 +157,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
int targetResizeWidth = baton->width;
|
int targetResizeWidth = baton->width;
|
||||||
int targetResizeHeight = baton->height;
|
int targetResizeHeight = baton->height;
|
||||||
|
|
||||||
// Swap input output width and height when rotating by 90 or 270 degrees
|
// When auto-rotating by 90 or 270 degrees, swap the target width and
|
||||||
bool swap = !baton->rotateBeforePreExtract &&
|
// height to ensure the behavior aligns with how it would have been if
|
||||||
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270 ||
|
// the rotation had taken place *before* resizing.
|
||||||
autoRotation == VIPS_ANGLE_D90 || autoRotation == VIPS_ANGLE_D270);
|
if (!baton->rotateBeforePreExtract &&
|
||||||
|
(autoRotation == VIPS_ANGLE_D90 || autoRotation == VIPS_ANGLE_D270)) {
|
||||||
|
std::swap(targetResizeWidth, targetResizeHeight);
|
||||||
|
}
|
||||||
|
|
||||||
// Shrink to pageHeight, so we work for multi-page images
|
// Shrink to pageHeight, so we work for multi-page images
|
||||||
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
||||||
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
||||||
baton->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
|
baton->canvas, baton->withoutEnlargement, baton->withoutReduction);
|
||||||
|
|
||||||
// The jpeg preload shrink.
|
// The jpeg preload shrink.
|
||||||
int jpegShrinkOnLoad = 1;
|
int jpegShrinkOnLoad = 1;
|
||||||
@@ -302,7 +302,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Shrink to pageHeight, so we work for multi-page images
|
// Shrink to pageHeight, so we work for multi-page images
|
||||||
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
|
||||||
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
|
||||||
baton->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
|
baton->canvas, baton->withoutEnlargement, baton->withoutReduction);
|
||||||
|
|
||||||
int targetHeight = static_cast<int>(std::rint(static_cast<double>(pageHeight) / vshrink));
|
int targetHeight = static_cast<int>(std::rint(static_cast<double>(pageHeight) / vshrink));
|
||||||
int targetPageHeight = targetHeight;
|
int targetPageHeight = targetHeight;
|
||||||
@@ -326,7 +326,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
try {
|
try {
|
||||||
image = image.icc_transform(processingProfile, VImage::option()
|
image = image.icc_transform(processingProfile, VImage::option()
|
||||||
->set("embedded", TRUE)
|
->set("embedded", TRUE)
|
||||||
->set("depth", image.interpretation() == VIPS_INTERPRETATION_RGB16 ? 16 : 8)
|
->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
|
||||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid embedded profile", nullptr);
|
sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid embedded profile", nullptr);
|
||||||
@@ -653,7 +653,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
if (across != 0 || down != 0) {
|
if (across != 0 || down != 0) {
|
||||||
int left;
|
int left;
|
||||||
int top;
|
int top;
|
||||||
compositeImage = compositeImage.replicate(across, down);
|
compositeImage = sharp::StaySequential(compositeImage, access).replicate(across, down);
|
||||||
if (composite->hasOffset) {
|
if (composite->hasOffset) {
|
||||||
std::tie(left, top) = sharp::CalculateCrop(
|
std::tie(left, top) = sharp::CalculateCrop(
|
||||||
compositeImage.width(), compositeImage.height(), image.width(), image.height(),
|
compositeImage.width(), compositeImage.height(), image.width(), image.height(),
|
||||||
@@ -763,6 +763,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
if (baton->withMetadata && sharp::HasProfile(image) && baton->withMetadataIcc.empty()) {
|
if (baton->withMetadata && sharp::HasProfile(image) && baton->withMetadataIcc.empty()) {
|
||||||
image = image.icc_transform("srgb", VImage::option()
|
image = image.icc_transform("srgb", VImage::option()
|
||||||
->set("embedded", TRUE)
|
->set("embedded", TRUE)
|
||||||
|
->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
|
||||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -788,12 +789,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply output ICC profile
|
// Apply output ICC profile
|
||||||
if (!baton->withMetadataIcc.empty()) {
|
if (baton->withMetadata) {
|
||||||
image = image.icc_transform(
|
image = image.icc_transform(
|
||||||
const_cast<char*>(baton->withMetadataIcc.data()),
|
baton->withMetadataIcc.empty() ? "srgb" : const_cast<char*>(baton->withMetadataIcc.data()),
|
||||||
VImage::option()
|
VImage::option()
|
||||||
->set("input_profile", processingProfile)
|
->set("input_profile", processingProfile)
|
||||||
->set("embedded", TRUE)
|
->set("embedded", TRUE)
|
||||||
|
->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
|
||||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
}
|
}
|
||||||
// Override EXIF Orientation tag
|
// Override EXIF Orientation tag
|
||||||
@@ -1287,8 +1289,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
delete baton;
|
delete baton;
|
||||||
|
|
||||||
// Decrement processing task counter
|
// Decrement processing task counter
|
||||||
g_atomic_int_dec_and_test(&sharp::counterProcess);
|
sharp::counterProcess--;
|
||||||
Napi::Number queueLength = Napi::Number::New(env, static_cast<double>(sharp::counterQueue));
|
Napi::Number queueLength = Napi::Number::New(env, static_cast<int>(sharp::counterQueue));
|
||||||
queueListener.MakeCallback(Receiver().Value(), { queueLength });
|
queueListener.MakeCallback(Receiver().Value(), { queueLength });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1705,8 +1707,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
worker->Queue();
|
worker->Queue();
|
||||||
|
|
||||||
// Increment queued task counter
|
// Increment queued task counter
|
||||||
g_atomic_int_inc(&sharp::counterQueue);
|
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<int>(++sharp::counterQueue));
|
||||||
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<double>(sharp::counterQueue));
|
|
||||||
queueListener.MakeCallback(info.This(), { queueLength });
|
queueListener.MakeCallback(info.This(), { queueLength });
|
||||||
|
|
||||||
return info.Env().Undefined();
|
return info.Env().Undefined();
|
||||||
|
|||||||
13
src/sharp.cc
13
src/sharp.cc
@@ -1,6 +1,8 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
// Copyright 2013 Lovell Fuller and others.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#include <mutex> // NOLINT(build/c++11)
|
||||||
|
|
||||||
#include <napi.h>
|
#include <napi.h>
|
||||||
#include <vips/vips8>
|
#include <vips/vips8>
|
||||||
|
|
||||||
@@ -10,14 +12,11 @@
|
|||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
|
|
||||||
static void* sharp_vips_init(void*) {
|
|
||||||
vips_init("sharp");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Napi::Object init(Napi::Env env, Napi::Object exports) {
|
Napi::Object init(Napi::Env env, Napi::Object exports) {
|
||||||
static GOnce sharp_vips_init_once = G_ONCE_INIT;
|
static std::once_flag sharp_vips_init_once;
|
||||||
g_once(&sharp_vips_init_once, static_cast<GThreadFunc>(sharp_vips_init), nullptr);
|
std::call_once(sharp_vips_init_once, []() {
|
||||||
|
vips_init("sharp");
|
||||||
|
});
|
||||||
|
|
||||||
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
|
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
|
||||||
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
|
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class StatsWorker : public Napi::AsyncWorker {
|
|||||||
|
|
||||||
void Execute() {
|
void Execute() {
|
||||||
// Decrement queued task counter
|
// Decrement queued task counter
|
||||||
g_atomic_int_dec_and_test(&sharp::counterQueue);
|
sharp::counterQueue--;
|
||||||
|
|
||||||
vips::VImage image;
|
vips::VImage image;
|
||||||
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
|
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
|
||||||
@@ -177,7 +177,7 @@ Napi::Value stats(const Napi::CallbackInfo& info) {
|
|||||||
worker->Queue();
|
worker->Queue();
|
||||||
|
|
||||||
// Increment queued task counter
|
// Increment queued task counter
|
||||||
g_atomic_int_inc(&sharp::counterQueue);
|
sharp::counterQueue++;
|
||||||
|
|
||||||
return info.Env().Undefined();
|
return info.Env().Undefined();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ Napi::Value concurrency(const Napi::CallbackInfo& info) {
|
|||||||
*/
|
*/
|
||||||
Napi::Value counters(const Napi::CallbackInfo& info) {
|
Napi::Value counters(const Napi::CallbackInfo& info) {
|
||||||
Napi::Object counters = Napi::Object::New(info.Env());
|
Napi::Object counters = Napi::Object::New(info.Env());
|
||||||
counters.Set("queue", sharp::counterQueue);
|
counters.Set("queue", static_cast<int>(sharp::counterQueue));
|
||||||
counters.Set("process", sharp::counterProcess);
|
counters.Set("process", static_cast<int>(sharp::counterProcess));
|
||||||
return counters;
|
return counters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ ARG BRANCH=main
|
|||||||
RUN apt-get -y update && apt-get install -y build-essential curl git
|
RUN apt-get -y update && apt-get install -y build-essential curl git
|
||||||
|
|
||||||
# Install latest Node.js LTS
|
# Install latest Node.js LTS
|
||||||
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
|
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
|
||||||
RUN apt-get install -y nodejs
|
RUN apt-get install -y nodejs
|
||||||
|
|
||||||
# Install benchmark dependencies
|
# Install benchmark dependencies
|
||||||
@@ -23,4 +23,9 @@ RUN cat /etc/os-release | grep VERSION=
|
|||||||
RUN node -v
|
RUN node -v
|
||||||
|
|
||||||
WORKDIR /tmp/sharp/test/bench
|
WORKDIR /tmp/sharp/test/bench
|
||||||
|
|
||||||
|
# Workaround for: https://github.com/emscripten-core/emscripten/pull/16917
|
||||||
|
# This could be removed once Squoosh is an optional dependency.
|
||||||
|
ENV NODE_OPTIONS="--no-experimental-fetch"
|
||||||
|
|
||||||
CMD [ "node", "perf" ]
|
CMD [ "node", "perf" ]
|
||||||
|
|||||||
@@ -9,19 +9,16 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@squoosh/cli": "0.7.3",
|
"@squoosh/cli": "0.7.3",
|
||||||
"@squoosh/lib": "0.4.0",
|
"@squoosh/lib": "0.5.3",
|
||||||
"async": "3.2.4",
|
"async": "3.2.4",
|
||||||
"benchmark": "2.1.4",
|
"benchmark": "2.1.4",
|
||||||
"gm": "1.25.0",
|
"gm": "1.25.0",
|
||||||
"imagemagick": "0.1.3",
|
"imagemagick": "0.1.3",
|
||||||
"jimp": "0.22.7"
|
"jimp": "0.22.10"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@tensorflow/tfjs-node": "4.2.0",
|
"@tensorflow/tfjs-node": "4.11.0",
|
||||||
"mapnik": "4.5.9"
|
"mapnik": "4.5.9"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0"
|
||||||
"engines": {
|
|
||||||
"node": "16"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { exec } = require('child_process');
|
const { exec, execSync } = require('child_process');
|
||||||
|
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const Benchmark = require('benchmark');
|
const Benchmark = require('benchmark');
|
||||||
@@ -40,8 +40,10 @@ const heightPng = 540;
|
|||||||
// Disable libvips cache to ensure tests are as fair as they can be
|
// Disable libvips cache to ensure tests are as fair as they can be
|
||||||
sharp.cache(false);
|
sharp.cache(false);
|
||||||
|
|
||||||
// Spawn one thread per CPU
|
// Spawn one thread per physical CPU core
|
||||||
sharp.concurrency(os.cpus().length);
|
const physicalCores = Number(execSync('lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l', { encoding: 'utf-8' }).trim());
|
||||||
|
console.log(`Detected ${physicalCores} physical cores`);
|
||||||
|
sharp.concurrency(physicalCores);
|
||||||
|
|
||||||
async.series({
|
async.series({
|
||||||
jpeg: function (callback) {
|
jpeg: function (callback) {
|
||||||
@@ -109,7 +111,7 @@ async.series({
|
|||||||
jpegSuite.add('squoosh-lib-buffer-buffer', {
|
jpegSuite.add('squoosh-lib-buffer-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
const pool = new squoosh.ImagePool();
|
const pool = new squoosh.ImagePool(os.cpus().length);
|
||||||
const image = pool.ingestImage(inputJpgBuffer);
|
const image = pool.ingestImage(inputJpgBuffer);
|
||||||
image.decoded
|
image.decoded
|
||||||
.then(function () {
|
.then(function () {
|
||||||
@@ -188,8 +190,8 @@ async.series({
|
|||||||
srcPath: fixtures.inputJpg,
|
srcPath: fixtures.inputJpg,
|
||||||
dstPath: outputJpg,
|
dstPath: outputJpg,
|
||||||
quality: 0.8,
|
quality: 0.8,
|
||||||
width: width,
|
width,
|
||||||
height: height,
|
height,
|
||||||
format: 'jpg',
|
format: 'jpg',
|
||||||
filter: 'Lanczos'
|
filter: 'Lanczos'
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
@@ -652,7 +654,7 @@ async.series({
|
|||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
image
|
image
|
||||||
.resize(width, heightPng)
|
.resize(width, heightPng, jimp.RESIZE_BICUBIC)
|
||||||
.deflateLevel(6)
|
.deflateLevel(6)
|
||||||
.filterType(0)
|
.filterType(0)
|
||||||
.getBuffer(jimp.MIME_PNG, function (err) {
|
.getBuffer(jimp.MIME_PNG, function (err) {
|
||||||
@@ -673,7 +675,7 @@ async.series({
|
|||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
image
|
image
|
||||||
.resize(width, heightPng)
|
.resize(width, heightPng, jimp.RESIZE_BICUBIC)
|
||||||
.deflateLevel(6)
|
.deflateLevel(6)
|
||||||
.filterType(0)
|
.filterType(0)
|
||||||
.write(outputPng, function (err) {
|
.write(outputPng, function (err) {
|
||||||
@@ -707,7 +709,7 @@ async.series({
|
|||||||
pngSuite.add('squoosh-lib-buffer-buffer', {
|
pngSuite.add('squoosh-lib-buffer-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
const pool = new squoosh.ImagePool();
|
const pool = new squoosh.ImagePool(os.cpus().length);
|
||||||
const image = pool.ingestImage(inputPngBuffer);
|
const image = pool.ingestImage(inputPngBuffer);
|
||||||
image.decoded
|
image.decoded
|
||||||
.then(function () {
|
.then(function () {
|
||||||
@@ -793,7 +795,7 @@ async.series({
|
|||||||
imagemagick.resize({
|
imagemagick.resize({
|
||||||
srcPath: fixtures.inputPngAlphaPremultiplicationLarge,
|
srcPath: fixtures.inputPngAlphaPremultiplicationLarge,
|
||||||
dstPath: outputPng,
|
dstPath: outputPng,
|
||||||
width: width,
|
width,
|
||||||
height: heightPng,
|
height: heightPng,
|
||||||
filter: 'Lanczos',
|
filter: 'Lanczos',
|
||||||
customArgs: [
|
customArgs: [
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
# Crop strategy accuracy
|
|
||||||
|
|
||||||
1. Download the [MSRA Salient Object Database](http://research.microsoft.com/en-us/um/people/jiansun/SalientObject/salient_object.htm) (101MB).
|
|
||||||
2. Extract each image and its median human-labelled salient region.
|
|
||||||
3. Generate a test report of percentage deviance of top and left edges for each crop strategy, plus a naive centre gravity crop as "control".
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone https://github.com/lovell/sharp.git
|
|
||||||
cd sharp/test/saliency
|
|
||||||
./download.sh
|
|
||||||
node report.js
|
|
||||||
python -m SimpleHTTPServer
|
|
||||||
```
|
|
||||||
|
|
||||||
The test report will then be available at
|
|
||||||
http://localhost:8000/report.html
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Fetch and parse the MSRA Salient Object Database 'Image set B'
|
|
||||||
# http://research.microsoft.com/en-us/um/people/jiansun/salientobject/salient_object.htm
|
|
||||||
|
|
||||||
if [ ! -d Image ]; then
|
|
||||||
if [ ! -f ImageB.zip ]; then
|
|
||||||
echo "Downloading 5000 images (101MB)"
|
|
||||||
curl -O http://research.microsoft.com/en-us/um/people/jiansun/salientobject/ImageSetB/ImageB.zip
|
|
||||||
fi
|
|
||||||
unzip ImageB.zip
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -d UserData ]; then
|
|
||||||
if [ ! -f UserDataB.zip ]; then
|
|
||||||
echo "Downloading human-labelled regions"
|
|
||||||
curl -O http://research.microsoft.com/en-us/um/people/jiansun/salientobject/ImageSetB/UserDataB.zip
|
|
||||||
fi
|
|
||||||
unzip UserDataB.zip
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f userData.json ]; then
|
|
||||||
echo "Processing human-labelled regions"
|
|
||||||
node userData.js
|
|
||||||
fi
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const request = require('request');
|
|
||||||
const tumblr = require('tumblr.js');
|
|
||||||
|
|
||||||
const client = tumblr.createClient({
|
|
||||||
consumer_key: '***',
|
|
||||||
consumer_secret: '***'
|
|
||||||
});
|
|
||||||
|
|
||||||
const fetchImages = function (offset) {
|
|
||||||
console.log(`Fetching offset ${offset}`);
|
|
||||||
client.posts('humanae', {
|
|
||||||
type: 'photo',
|
|
||||||
offset: offset
|
|
||||||
}, function (err, response) {
|
|
||||||
if (err) throw err;
|
|
||||||
if (response.posts.length > 0) {
|
|
||||||
response.posts.forEach((post) => {
|
|
||||||
const url = post.photos[0].alt_sizes
|
|
||||||
.filter((image) => image.width === 100)
|
|
||||||
.map((image) => image.url)[0];
|
|
||||||
const filename = `./images/${post.id}.jpg`;
|
|
||||||
try {
|
|
||||||
fs.statSync(filename);
|
|
||||||
} catch (err) {
|
|
||||||
if (err.code === 'ENOENT') {
|
|
||||||
request(url).pipe(fs.createWriteStream(filename));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fetchImages(offset + 20);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
fetchImages(0);
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "sharp-crop-strategy-attention-model-humanae",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"request": "^2.75.0",
|
|
||||||
"tumblr.js": "^1.1.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const childProcess = require('child_process');
|
|
||||||
|
|
||||||
const a = [];
|
|
||||||
const b = [];
|
|
||||||
|
|
||||||
fs.readdirSync('./images')
|
|
||||||
.filter((file) => file.endsWith('.jpg'))
|
|
||||||
.forEach((file) => {
|
|
||||||
// Extract one pixel, avoiding first DCT block, and return value of A and B channels
|
|
||||||
const command = `convert ./images/${file}[1x1+8+8] -colorspace lab -format "%[fx:u.g] %[fx:u.b]" info:`;
|
|
||||||
const result = childProcess.execSync(command, { encoding: 'utf8' });
|
|
||||||
const ab = result.split(' ');
|
|
||||||
a.push(ab[0]);
|
|
||||||
b.push(ab[1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
a.sort((v1, v2) => v1 - v2);
|
|
||||||
b.sort((v1, v2) => v1 - v2);
|
|
||||||
|
|
||||||
// Convert from 0..1 to -128..128
|
|
||||||
const convert = function (v) {
|
|
||||||
return Math.round(256 * (v - 0.5));
|
|
||||||
};
|
|
||||||
|
|
||||||
const threshold = Math.round(a.length / 100);
|
|
||||||
console.log(`Trimming lowest/highest ${threshold} for 98th percentile`);
|
|
||||||
|
|
||||||
// Ignore ~2% outliers
|
|
||||||
console.log(`a ${convert(a[threshold])} - ${convert(a[a.length - threshold])}`);
|
|
||||||
console.log(`b ${convert(b[threshold])} - ${convert(b[b.length - threshold])}`);
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/metrics-graphics/2.10.1/metricsgraphics.min.css" rel="stylesheet" type="text/css">
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.6/d3.min.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/metrics-graphics/2.10.1/metricsgraphics.min.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="accuracy"></div>
|
|
||||||
<script>
|
|
||||||
d3.json('report.json', function(err, data) {
|
|
||||||
MG.data_graphic({
|
|
||||||
title: 'Crop accuracy',
|
|
||||||
data: data,
|
|
||||||
target: '#accuracy',
|
|
||||||
width: 960,
|
|
||||||
height: 600,
|
|
||||||
x_accessor: 'accuracy',
|
|
||||||
x_label: '% Accuracy',
|
|
||||||
y_accessor: ['centre', 'entropy', 'attention'],
|
|
||||||
legend: ['Centre', 'Entropy', 'Attention']
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const os = require('os');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const async = require('async');
|
|
||||||
const sharp = require('../../');
|
|
||||||
|
|
||||||
const crops = {
|
|
||||||
entropy: sharp.strategy.entropy,
|
|
||||||
attention: sharp.strategy.attention
|
|
||||||
};
|
|
||||||
const concurrency = os.cpus().length;
|
|
||||||
|
|
||||||
const scores = {};
|
|
||||||
|
|
||||||
const incrementScore = function (accuracy, crop) {
|
|
||||||
if (typeof scores[accuracy] === 'undefined') {
|
|
||||||
scores[accuracy] = {};
|
|
||||||
}
|
|
||||||
if (typeof scores[accuracy][crop] === 'undefined') {
|
|
||||||
scores[accuracy][crop] = 0;
|
|
||||||
}
|
|
||||||
scores[accuracy][crop]++;
|
|
||||||
};
|
|
||||||
|
|
||||||
const userData = require('./userData.json');
|
|
||||||
const files = Object.keys(userData);
|
|
||||||
|
|
||||||
async.eachLimit(files, concurrency, function (file, done) {
|
|
||||||
const filename = path.join(__dirname, 'Image', file);
|
|
||||||
const salientWidth = userData[file].right - userData[file].left;
|
|
||||||
const salientHeight = userData[file].bottom - userData[file].top;
|
|
||||||
sharp(filename).metadata(function (err, metadata) {
|
|
||||||
if (err) console.log(err);
|
|
||||||
const marginWidth = metadata.width - salientWidth;
|
|
||||||
const marginHeight = metadata.height - salientHeight;
|
|
||||||
async.each(Object.keys(crops), function (crop, done) {
|
|
||||||
async.parallel([
|
|
||||||
// Left edge accuracy
|
|
||||||
function (done) {
|
|
||||||
if (marginWidth) {
|
|
||||||
sharp(filename).resize(salientWidth, metadata.height).crop(crops[crop]).toBuffer(function (err, data, info) {
|
|
||||||
const delta = Math.abs(userData[file].left + info.cropOffsetLeft);
|
|
||||||
const accuracy = Math.round(marginWidth / (marginWidth + delta) * 100);
|
|
||||||
incrementScore(accuracy, crop);
|
|
||||||
done(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Top edge accuracy
|
|
||||||
function (done) {
|
|
||||||
if (marginHeight) {
|
|
||||||
sharp(filename).resize(metadata.width, salientHeight).crop(crops[crop]).toBuffer(function (err, data, info) {
|
|
||||||
const delta = Math.abs(userData[file].top + info.cropOffsetTop);
|
|
||||||
const accuracy = Math.round(marginHeight / (marginHeight + delta) * 100);
|
|
||||||
incrementScore(accuracy, crop);
|
|
||||||
done(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
], done);
|
|
||||||
}, done);
|
|
||||||
});
|
|
||||||
}, function () {
|
|
||||||
const report = [];
|
|
||||||
Object.keys(scores).forEach(function (accuracy) {
|
|
||||||
report.push(
|
|
||||||
Object.assign({
|
|
||||||
accuracy: Number(accuracy)
|
|
||||||
}, scores[accuracy])
|
|
||||||
);
|
|
||||||
});
|
|
||||||
fs.writeFileSync('report.json', JSON.stringify(report, null, 2));
|
|
||||||
});
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const userDataDir = 'UserData';
|
|
||||||
|
|
||||||
const images = {};
|
|
||||||
|
|
||||||
const median = function (values) {
|
|
||||||
values.sort(function (a, b) {
|
|
||||||
return a - b;
|
|
||||||
});
|
|
||||||
const half = Math.floor(values.length / 2);
|
|
||||||
if (values.length % 2) {
|
|
||||||
return values[half];
|
|
||||||
} else {
|
|
||||||
return Math.floor((values[half - 1] + values[half]) / 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// List of files
|
|
||||||
fs.readdirSync(userDataDir).forEach(function (file) {
|
|
||||||
// Contents of file
|
|
||||||
const lines = fs.readFileSync(path.join(userDataDir, file), { encoding: 'utf-8' }).split(/\r\n/);
|
|
||||||
// First line = number of entries
|
|
||||||
const entries = parseInt(lines[0], 10);
|
|
||||||
// Verify number of entries
|
|
||||||
if (entries !== 500) {
|
|
||||||
throw new Error('Expecting 500 images in ' + file + ', found ' + entries);
|
|
||||||
}
|
|
||||||
// Keep track of which line we're on
|
|
||||||
let linePos = 2;
|
|
||||||
for (let i = 0; i < entries; i++) {
|
|
||||||
// Get data for current image
|
|
||||||
const filename = lines[linePos].replace(/\\/, path.sep);
|
|
||||||
linePos = linePos + 2;
|
|
||||||
const regions = lines[linePos].split('; ');
|
|
||||||
linePos = linePos + 2;
|
|
||||||
// Parse human-labelled regions for min/max coords
|
|
||||||
const lefts = [];
|
|
||||||
const tops = [];
|
|
||||||
const rights = [];
|
|
||||||
const bottoms = [];
|
|
||||||
regions.forEach(function (region) {
|
|
||||||
if (region.indexOf(' ') !== -1) {
|
|
||||||
const coords = region.split(' ');
|
|
||||||
lefts.push(parseInt(coords[0], 10));
|
|
||||||
tops.push(parseInt(coords[1], 10));
|
|
||||||
rights.push(parseInt(coords[2], 10));
|
|
||||||
bottoms.push(parseInt(coords[3], 10));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Add image
|
|
||||||
images[filename] = {
|
|
||||||
left: median(lefts),
|
|
||||||
top: median(tops),
|
|
||||||
right: median(rights),
|
|
||||||
bottom: median(bottoms)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verify number of images found
|
|
||||||
const imageCount = Object.keys(images).length;
|
|
||||||
if (imageCount === 5000) {
|
|
||||||
// Write output
|
|
||||||
fs.writeFileSync('userData.json', JSON.stringify(images, null, 2));
|
|
||||||
} else {
|
|
||||||
throw new Error('Expecting 5000 images, found ' + imageCount);
|
|
||||||
}
|
|
||||||
@@ -73,8 +73,6 @@ readableStream.pipe(transformer).pipe(writableStream);
|
|||||||
|
|
||||||
console.log(sharp.format);
|
console.log(sharp.format);
|
||||||
console.log(sharp.versions);
|
console.log(sharp.versions);
|
||||||
console.log(sharp.vendor.current);
|
|
||||||
console.log(sharp.vendor.installed.join(', '));
|
|
||||||
|
|
||||||
sharp.queue.on('change', (queueLength: number) => {
|
sharp.queue.on('change', (queueLength: number) => {
|
||||||
console.log(`Queue contains ${queueLength} task(s)`);
|
console.log(`Queue contains ${queueLength} task(s)`);
|
||||||
@@ -524,7 +522,7 @@ sharp('input.tiff').jxl({ lossless: true }).toFile('out.jxl');
|
|||||||
sharp('input.tiff').jxl({ effort: 7 }).toFile('out.jxl');
|
sharp('input.tiff').jxl({ effort: 7 }).toFile('out.jxl');
|
||||||
|
|
||||||
// Support `minSize` and `mixed` webp options
|
// Support `minSize` and `mixed` webp options
|
||||||
sharp('input.tiff').webp({ minSize: 1000, mixed: true }).toFile('out.gif');
|
sharp('input.tiff').webp({ minSize: true, mixed: true }).toFile('out.gif');
|
||||||
|
|
||||||
// 'failOn' input param
|
// 'failOn' input param
|
||||||
sharp('input.tiff', { failOn: 'none' });
|
sharp('input.tiff', { failOn: 'none' });
|
||||||
@@ -651,3 +649,13 @@ sharp(input).composite([
|
|||||||
unlimited: true,
|
unlimited: true,
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Support for webp preset in types
|
||||||
|
// https://github.com/lovell/sharp/issues/3747
|
||||||
|
sharp('input.tiff').webp({ preset: 'photo' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'picture' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'icon' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'drawing' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'text' }).toFile('out.webp');
|
||||||
|
sharp('input.tiff').webp({ preset: 'default' }).toFile('out.webp');
|
||||||
|
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const assert = require('assert');
|
|
||||||
const agent = require('../../lib/agent');
|
|
||||||
|
|
||||||
describe('HTTP agent', function () {
|
|
||||||
it('Without proxy', function () {
|
|
||||||
assert.strictEqual(null, agent());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('HTTPS proxy with auth from HTTPS_PROXY', function () {
|
|
||||||
process.env.HTTPS_PROXY = 'https://user:pass@secure:123';
|
|
||||||
let logMsg = '';
|
|
||||||
const proxy = agent(msg => { logMsg = msg; });
|
|
||||||
delete process.env.HTTPS_PROXY;
|
|
||||||
assert.strictEqual('object', typeof proxy);
|
|
||||||
assert.strictEqual('secure', proxy.options.proxy.host);
|
|
||||||
assert.strictEqual(123, proxy.options.proxy.port);
|
|
||||||
assert.strictEqual('user:pass', proxy.options.proxy.proxyAuth);
|
|
||||||
assert.strictEqual(443, proxy.defaultPort);
|
|
||||||
assert.strictEqual(logMsg, 'Via proxy https://secure:123 with credentials');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('HTTPS proxy with auth from HTTPS_PROXY using credentials containing special characters', function () {
|
|
||||||
process.env.HTTPS_PROXY = 'https://user,:pass=@secure:789';
|
|
||||||
let logMsg = '';
|
|
||||||
const proxy = agent(msg => { logMsg = msg; });
|
|
||||||
delete process.env.HTTPS_PROXY;
|
|
||||||
assert.strictEqual('object', typeof proxy);
|
|
||||||
assert.strictEqual('secure', proxy.options.proxy.host);
|
|
||||||
assert.strictEqual(789, proxy.options.proxy.port);
|
|
||||||
assert.strictEqual('user,:pass=', proxy.options.proxy.proxyAuth);
|
|
||||||
assert.strictEqual(443, proxy.defaultPort);
|
|
||||||
assert.strictEqual(logMsg, 'Via proxy https://secure:789 with credentials');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('HTTP proxy without auth from npm_config_proxy', function () {
|
|
||||||
process.env.npm_config_proxy = 'http://plaintext:456';
|
|
||||||
let logMsg = '';
|
|
||||||
const proxy = agent(msg => { logMsg = msg; });
|
|
||||||
delete process.env.npm_config_proxy;
|
|
||||||
assert.strictEqual('object', typeof proxy);
|
|
||||||
assert.strictEqual('plaintext', proxy.options.proxy.host);
|
|
||||||
assert.strictEqual(456, proxy.options.proxy.port);
|
|
||||||
assert.strictEqual(null, proxy.options.proxy.proxyAuth);
|
|
||||||
assert.strictEqual(443, proxy.defaultPort);
|
|
||||||
assert.strictEqual(logMsg, 'Via proxy http://plaintext:456 no credentials');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -269,7 +269,7 @@ describe('composite', () => {
|
|||||||
.resize(80)
|
.resize(80)
|
||||||
.composite([{
|
.composite([{
|
||||||
input: fixtures.inputPngWithTransparency16bit,
|
input: fixtures.inputPngWithTransparency16bit,
|
||||||
gravity: gravity
|
gravity
|
||||||
}])
|
}])
|
||||||
.toBuffer((err, data, info) => {
|
.toBuffer((err, data, info) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@@ -314,7 +314,7 @@ describe('composite', () => {
|
|||||||
.composite([{
|
.composite([{
|
||||||
input: fixtures.inputPngWithTransparency16bit,
|
input: fixtures.inputPngWithTransparency16bit,
|
||||||
tile: true,
|
tile: true,
|
||||||
gravity: gravity
|
gravity
|
||||||
}])
|
}])
|
||||||
.toBuffer((err, data, info) => {
|
.toBuffer((err, data, info) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@@ -472,4 +472,28 @@ describe('composite', () => {
|
|||||||
assert.strictEqual(b, 19);
|
assert.strictEqual(b, 19);
|
||||||
assert.strictEqual(a, 128);
|
assert.strictEqual(a, 128);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Ensure tiled overlay is fully decoded', async () => {
|
||||||
|
const tile = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 8, height: 513, channels: 3, background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.png({ compressionLevel: 0 })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { info } = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 8, height: 514, channels: 3, background: 'green'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.composite([{
|
||||||
|
input: tile,
|
||||||
|
tile: true
|
||||||
|
}])
|
||||||
|
.toBuffer({ resolveWithObject: true });
|
||||||
|
|
||||||
|
assert.strictEqual(info.width, 8);
|
||||||
|
assert.strictEqual(info.height, 514);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ describe('Extend', function () {
|
|||||||
sharp(fixtures.inputWebPAnimated, { pages: -1 })
|
sharp(fixtures.inputWebPAnimated, { pages: -1 })
|
||||||
.resize(120)
|
.resize(120)
|
||||||
.extend({
|
.extend({
|
||||||
extendWith: extendWith,
|
extendWith,
|
||||||
top: 40,
|
top: 40,
|
||||||
bottom: 40,
|
bottom: 40,
|
||||||
left: 40,
|
left: 40,
|
||||||
@@ -58,7 +58,7 @@ describe('Extend', function () {
|
|||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(120)
|
.resize(120)
|
||||||
.extend({
|
.extend({
|
||||||
extendWith: extendWith,
|
extendWith,
|
||||||
top: 10,
|
top: 10,
|
||||||
bottom: 10,
|
bottom: 10,
|
||||||
left: 10,
|
left: 10,
|
||||||
@@ -77,7 +77,7 @@ describe('Extend', function () {
|
|||||||
sharp(fixtures.inputPngWithTransparency16bit)
|
sharp(fixtures.inputPngWithTransparency16bit)
|
||||||
.resize(120)
|
.resize(120)
|
||||||
.extend({
|
.extend({
|
||||||
extendWith: extendWith,
|
extendWith,
|
||||||
top: 50,
|
top: 50,
|
||||||
left: 10,
|
left: 10,
|
||||||
right: 35,
|
right: 35,
|
||||||
@@ -94,7 +94,7 @@ describe('Extend', function () {
|
|||||||
it(`PNG with 2 channels (${extendWith})`, function (done) {
|
it(`PNG with 2 channels (${extendWith})`, function (done) {
|
||||||
sharp(fixtures.inputPngWithGreyAlpha)
|
sharp(fixtures.inputPngWithGreyAlpha)
|
||||||
.extend({
|
.extend({
|
||||||
extendWith: extendWith,
|
extendWith,
|
||||||
top: 50,
|
top: 50,
|
||||||
bottom: 50,
|
bottom: 50,
|
||||||
left: 80,
|
left: 80,
|
||||||
|
|||||||
@@ -319,5 +319,16 @@ describe('Partial image extraction', function () {
|
|||||||
s.extract(options);
|
s.extract(options);
|
||||||
assert.strictEqual(warningMessage, 'ignoring previous extract options');
|
assert.strictEqual(warningMessage, 'ignoring previous extract options');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Multiple extract+resize emits warning', () => {
|
||||||
|
let warningMessage = '';
|
||||||
|
const s = sharp();
|
||||||
|
s.on('warning', function (msg) { warningMessage = msg; });
|
||||||
|
const options = { top: 0, left: 0, width: 1, height: 1 };
|
||||||
|
s.extract(options).extract(options);
|
||||||
|
assert.strictEqual(warningMessage, '');
|
||||||
|
s.resize(1);
|
||||||
|
assert.strictEqual(warningMessage, 'operation order will be: extract, resize, extract');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,34 +8,34 @@ const assert = require('assert');
|
|||||||
const sharp = require('../../');
|
const sharp = require('../../');
|
||||||
|
|
||||||
describe('HEIF', () => {
|
describe('HEIF', () => {
|
||||||
it('called without options does not throw an error', () => {
|
it('called without options throws an error', () => {
|
||||||
assert.doesNotThrow(() => {
|
assert.throws(() => {
|
||||||
sharp().heif();
|
sharp().heif();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('valid quality does not throw an error', () => {
|
it('valid quality does not throw an error', () => {
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
sharp().heif({ quality: 80 });
|
sharp().heif({ compression: 'av1', quality: 80 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('invalid quality should throw an error', () => {
|
it('invalid quality should throw an error', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
sharp().heif({ quality: 101 });
|
sharp().heif({ compression: 'av1', quality: 101 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('non-numeric quality should throw an error', () => {
|
it('non-numeric quality should throw an error', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
sharp().heif({ quality: 'fail' });
|
sharp().heif({ compression: 'av1', quality: 'fail' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('valid lossless does not throw an error', () => {
|
it('valid lossless does not throw an error', () => {
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
sharp().heif({ lossless: true });
|
sharp().heif({ compression: 'av1', lossless: true });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('non-boolean lossless should throw an error', () => {
|
it('non-boolean lossless should throw an error', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
sharp().heif({ lossless: 'fail' });
|
sharp().heif({ compression: 'av1', lossless: 'fail' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('valid compression does not throw an error', () => {
|
it('valid compression does not throw an error', () => {
|
||||||
@@ -55,27 +55,27 @@ describe('HEIF', () => {
|
|||||||
});
|
});
|
||||||
it('valid effort does not throw an error', () => {
|
it('valid effort does not throw an error', () => {
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
sharp().heif({ effort: 6 });
|
sharp().heif({ compression: 'av1', effort: 6 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('out of range effort should throw an error', () => {
|
it('out of range effort should throw an error', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
sharp().heif({ effort: 10 });
|
sharp().heif({ compression: 'av1', effort: 10 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('invalid effort should throw an error', () => {
|
it('invalid effort should throw an error', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
sharp().heif({ effort: 'fail' });
|
sharp().heif({ compression: 'av1', effort: 'fail' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('invalid chromaSubsampling should throw an error', () => {
|
it('invalid chromaSubsampling should throw an error', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
sharp().heif({ chromaSubsampling: 'fail' });
|
sharp().heif({ compression: 'av1', chromaSubsampling: 'fail' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('valid chromaSubsampling does not throw an error', () => {
|
it('valid chromaSubsampling does not throw an error', () => {
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
sharp().heif({ chromaSubsampling: '4:4:4' });
|
sharp().heif({ compression: 'av1', chromaSubsampling: '4:4:4' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -914,7 +914,7 @@ describe('Input/output', function () {
|
|||||||
channels: 3,
|
channels: 3,
|
||||||
background: { r: 0, g: 255, b: 0 }
|
background: { r: 0, g: 255, b: 0 }
|
||||||
};
|
};
|
||||||
sharp({ create: create })
|
sharp({ create })
|
||||||
.jpeg()
|
.jpeg()
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@@ -932,7 +932,7 @@ describe('Input/output', function () {
|
|||||||
channels: 4,
|
channels: 4,
|
||||||
background: { r: 255, g: 0, b: 0, alpha: 128 }
|
background: { r: 255, g: 0, b: 0, alpha: 128 }
|
||||||
};
|
};
|
||||||
sharp({ create: create })
|
sharp({ create })
|
||||||
.png()
|
.png()
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@@ -951,7 +951,7 @@ describe('Input/output', function () {
|
|||||||
background: { r: 0, g: 0, b: 0 }
|
background: { r: 0, g: 0, b: 0 }
|
||||||
};
|
};
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp({ create: create });
|
sharp({ create });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('Missing background', function () {
|
it('Missing background', function () {
|
||||||
@@ -961,7 +961,7 @@ describe('Input/output', function () {
|
|||||||
channels: 3
|
channels: 3
|
||||||
};
|
};
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp({ create: create });
|
sharp({ create });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ describe('JPEG', function () {
|
|||||||
[-1, 88.2, 'test'].forEach(function (quality) {
|
[-1, 88.2, 'test'].forEach(function (quality) {
|
||||||
it(quality.toString(), function () {
|
it(quality.toString(), function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().jpeg({ quality: quality });
|
sharp().jpeg({ quality });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ const assert = require('assert');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const semver = require('semver');
|
const semver = require('semver');
|
||||||
const libvips = require('../../lib/libvips');
|
const libvips = require('../../lib/libvips');
|
||||||
const mockFS = require('mock-fs');
|
|
||||||
|
|
||||||
const originalPlatform = process.platform;
|
const originalPlatform = process.platform;
|
||||||
|
|
||||||
@@ -59,10 +58,6 @@ describe('libvips binaries', function () {
|
|||||||
assert.strictEqual('string', typeof minimumLibvipsVersion);
|
assert.strictEqual('string', typeof minimumLibvipsVersion);
|
||||||
assert.notStrictEqual(null, semver.valid(minimumLibvipsVersion));
|
assert.notStrictEqual(null, semver.valid(minimumLibvipsVersion));
|
||||||
});
|
});
|
||||||
it('hasVendoredLibvips returns a boolean', function () {
|
|
||||||
const hasVendoredLibvips = libvips.hasVendoredLibvips();
|
|
||||||
assert.strictEqual('boolean', typeof hasVendoredLibvips);
|
|
||||||
});
|
|
||||||
it('useGlobalLibvips can be ignored via an env var', function () {
|
it('useGlobalLibvips can be ignored via an env var', function () {
|
||||||
process.env.SHARP_IGNORE_GLOBAL_LIBVIPS = 1;
|
process.env.SHARP_IGNORE_GLOBAL_LIBVIPS = 1;
|
||||||
|
|
||||||
@@ -71,62 +66,48 @@ describe('libvips binaries', function () {
|
|||||||
|
|
||||||
delete process.env.SHARP_IGNORE_GLOBAL_LIBVIPS;
|
delete process.env.SHARP_IGNORE_GLOBAL_LIBVIPS;
|
||||||
});
|
});
|
||||||
it('cachePath returns a valid path ending with _libvips', function () {
|
});
|
||||||
const cachePath = libvips.cachePath();
|
|
||||||
assert.strictEqual('string', typeof cachePath);
|
describe('Build time platform detection', () => {
|
||||||
assert.strictEqual('_libvips', cachePath.substr(-8));
|
it('Can override platform with npm_config_platform and npm_config_libc', () => {
|
||||||
assert.strictEqual(true, fs.existsSync(cachePath));
|
process.env.npm_config_platform = 'testplatform';
|
||||||
|
process.env.npm_config_libc = 'testlibc';
|
||||||
|
const [platform] = libvips.buildPlatformArch().split('-');
|
||||||
|
assert.strictEqual(platform, 'testplatformtestlibc');
|
||||||
|
delete process.env.npm_config_platform;
|
||||||
|
delete process.env.npm_config_libc;
|
||||||
|
});
|
||||||
|
it('Can override arch with npm_config_arch', () => {
|
||||||
|
process.env.npm_config_arch = 'test';
|
||||||
|
const [, arch] = libvips.buildPlatformArch().split('-');
|
||||||
|
assert.strictEqual(arch, 'test');
|
||||||
|
delete process.env.npm_config_arch;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('integrity', function () {
|
describe('Build time directories', () => {
|
||||||
it('reads value from environment variable', function () {
|
it('sharp-libvips include', () => {
|
||||||
const prev = process.env.npm_package_config_integrity_platform_arch;
|
const dir = libvips.buildSharpLibvipsIncludeDir();
|
||||||
process.env.npm_package_config_integrity_platform_arch = 'sha512-test';
|
assert.strictEqual(fs.statSync(dir).isDirectory(), true);
|
||||||
|
|
||||||
const integrity = libvips.integrity('platform-arch');
|
|
||||||
assert.strictEqual('sha512-test', integrity);
|
|
||||||
|
|
||||||
process.env.npm_package_config_integrity_platform_arch = prev;
|
|
||||||
});
|
});
|
||||||
it('reads value from package.json', function () {
|
it('sharp-libvips cplusplus', () => {
|
||||||
const prev = process.env.npm_package_config_integrity_linux_x64;
|
const dir = libvips.buildSharpLibvipsCPlusPlusDir();
|
||||||
delete process.env.npm_package_config_integrity_linux_x64;
|
assert.strictEqual(fs.statSync(dir).isDirectory(), true);
|
||||||
|
});
|
||||||
const integrity = libvips.integrity('linux-x64');
|
it('sharp-libvips lib', () => {
|
||||||
assert.strictEqual('sha512-', integrity.substr(0, 7));
|
const dir = libvips.buildSharpLibvipsLibDir();
|
||||||
|
assert.strictEqual(fs.statSync(dir).isDirectory(), true);
|
||||||
process.env.npm_package_config_integrity_linux_x64 = prev;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('safe directory creation', function () {
|
describe('Runtime detection', () => {
|
||||||
before(function () {
|
it('platform', () => {
|
||||||
mockFS({
|
const [platform] = libvips.runtimePlatformArch().split('-');
|
||||||
exampleDirA: {
|
assert.strict(['darwin', 'linux', 'linuxmusl', 'win32'].includes(platform));
|
||||||
exampleDirB: {
|
|
||||||
exampleFile: 'Example test file'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
after(function () { mockFS.restore(); });
|
it('arch', () => {
|
||||||
|
const [, arch] = libvips.runtimePlatformArch().split('-');
|
||||||
it('mkdirSync creates a directory', function () {
|
assert.strict(['arm', 'arm64', 'ia32', 'x64'].includes(arch));
|
||||||
const dirPath = 'createdDir';
|
|
||||||
|
|
||||||
libvips.mkdirSync(dirPath);
|
|
||||||
assert.strictEqual(true, fs.existsSync(dirPath));
|
|
||||||
});
|
|
||||||
it('mkdirSync does not throw error or overwrite an existing dir', function () {
|
|
||||||
const dirPath = 'exampleDirA';
|
|
||||||
const nestedDirPath = 'exampleDirA/exampleDirB';
|
|
||||||
assert.strictEqual(true, fs.existsSync(dirPath));
|
|
||||||
|
|
||||||
libvips.mkdirSync(dirPath);
|
|
||||||
|
|
||||||
assert.strictEqual(true, fs.existsSync(dirPath));
|
|
||||||
assert.strictEqual(true, fs.existsSync(nestedDirPath));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual(true, metadata.exif instanceof Buffer);
|
assert.strictEqual(true, metadata.exif instanceof Buffer);
|
||||||
const exif = exifReader(metadata.exif);
|
const exif = exifReader(metadata.exif);
|
||||||
assert.strictEqual('object', typeof exif);
|
assert.strictEqual('object', typeof exif);
|
||||||
assert.strictEqual('object', typeof exif.image);
|
assert.strictEqual('object', typeof exif.Image);
|
||||||
assert.strictEqual('number', typeof exif.image.XResolution);
|
assert.strictEqual('number', typeof exif.Image.XResolution);
|
||||||
// ICC
|
// ICC
|
||||||
assert.strictEqual('object', typeof metadata.icc);
|
assert.strictEqual('object', typeof metadata.icc);
|
||||||
assert.strictEqual(true, metadata.icc instanceof Buffer);
|
assert.strictEqual(true, metadata.icc instanceof Buffer);
|
||||||
@@ -523,8 +523,8 @@ describe('Image metadata', function () {
|
|||||||
// EXIF
|
// EXIF
|
||||||
const exif = exifReader(metadata.exif);
|
const exif = exifReader(metadata.exif);
|
||||||
assert.strictEqual('object', typeof exif);
|
assert.strictEqual('object', typeof exif);
|
||||||
assert.strictEqual('object', typeof exif.image);
|
assert.strictEqual('object', typeof exif.Image);
|
||||||
assert.strictEqual('number', typeof exif.image.XResolution);
|
assert.strictEqual('number', typeof exif.Image.XResolution);
|
||||||
// ICC
|
// ICC
|
||||||
assert.strictEqual('object', typeof metadata.icc);
|
assert.strictEqual('object', typeof metadata.icc);
|
||||||
assert.strictEqual(true, metadata.icc instanceof Buffer);
|
assert.strictEqual(true, metadata.icc instanceof Buffer);
|
||||||
@@ -589,8 +589,8 @@ describe('Image metadata', function () {
|
|||||||
// EXIF
|
// EXIF
|
||||||
const exif = exifReader(metadata.exif);
|
const exif = exifReader(metadata.exif);
|
||||||
assert.strictEqual('object', typeof exif);
|
assert.strictEqual('object', typeof exif);
|
||||||
assert.strictEqual('object', typeof exif.image);
|
assert.strictEqual('object', typeof exif.Image);
|
||||||
assert.strictEqual('number', typeof exif.image.XResolution);
|
assert.strictEqual('number', typeof exif.Image.XResolution);
|
||||||
// ICC
|
// ICC
|
||||||
assert.strictEqual('object', typeof metadata.icc);
|
assert.strictEqual('object', typeof metadata.icc);
|
||||||
assert.strictEqual(true, metadata.icc instanceof Buffer);
|
assert.strictEqual(true, metadata.icc instanceof Buffer);
|
||||||
@@ -656,8 +656,8 @@ describe('Image metadata', function () {
|
|||||||
|
|
||||||
const { exif } = await sharp(data).metadata();
|
const { exif } = await sharp(data).metadata();
|
||||||
const parsedExif = exifReader(exif);
|
const parsedExif = exifReader(exif);
|
||||||
assert.strictEqual(parsedExif.image.Software, 'sharp');
|
assert.strictEqual(parsedExif.Image.Software, 'sharp');
|
||||||
assert.strictEqual(parsedExif.exif.ExposureTime, 0.2);
|
assert.strictEqual(parsedExif.Photo.ExposureTime, 0.2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Set density of JPEG', async () => {
|
it('Set density of JPEG', async () => {
|
||||||
@@ -781,6 +781,55 @@ describe('Image metadata', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('withMetadata adds default sRGB profile', async () => {
|
||||||
|
const data = await sharp(fixtures.inputJpg)
|
||||||
|
.resize(32, 24)
|
||||||
|
.withMetadata()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
const { colorSpace, deviceClass, intent } = icc.parse(metadata.icc);
|
||||||
|
assert.strictEqual(colorSpace, 'RGB');
|
||||||
|
assert.strictEqual(deviceClass, 'Monitor');
|
||||||
|
assert.strictEqual(intent, 'Perceptual');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withMetadata adds default sRGB profile to RGB16', async () => {
|
||||||
|
const data = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 8, height: 8, channels: 4, background: 'orange'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toColorspace('rgb16')
|
||||||
|
.png()
|
||||||
|
.withMetadata()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
assert.strictEqual(metadata.depth, 'ushort');
|
||||||
|
|
||||||
|
const { description } = icc.parse(metadata.icc);
|
||||||
|
assert.strictEqual(description, 'sRGB');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withMetadata adds P3 profile to 16-bit PNG', async () => {
|
||||||
|
const data = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 8, height: 8, channels: 4, background: 'orange'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toColorspace('rgb16')
|
||||||
|
.png()
|
||||||
|
.withMetadata({ icc: 'p3' })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const metadata = await sharp(data).metadata();
|
||||||
|
assert.strictEqual(metadata.depth, 'ushort');
|
||||||
|
|
||||||
|
const { description } = icc.parse(metadata.icc);
|
||||||
|
assert.strictEqual(description, 'sP3C');
|
||||||
|
});
|
||||||
|
|
||||||
it('File input with corrupt header fails gracefully', function (done) {
|
it('File input with corrupt header fails gracefully', function (done) {
|
||||||
sharp(fixtures.inputJpgWithCorruptHeader)
|
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||||
.metadata(function (err) {
|
.metadata(function (err) {
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
// Copyright 2013 Lovell Fuller and others.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const assert = require('assert');
|
|
||||||
const platform = require('../../lib/platform');
|
|
||||||
|
|
||||||
describe('Platform-detection', function () {
|
|
||||||
it('Can override arch with npm_config_arch', function () {
|
|
||||||
process.env.npm_config_arch = 'test';
|
|
||||||
assert.strictEqual('test', platform().split('-')[1]);
|
|
||||||
delete process.env.npm_config_arch;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can override platform with npm_config_platform', function () {
|
|
||||||
process.env.npm_config_platform = 'test';
|
|
||||||
assert.strictEqual('test', platform().split('-')[0]);
|
|
||||||
delete process.env.npm_config_platform;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can override ARM version via --arm-version', function () {
|
|
||||||
process.env.npm_config_arch = 'arm';
|
|
||||||
process.env.npm_config_arm_version = 'test';
|
|
||||||
assert.strictEqual('armvtest', platform().split('-')[1]);
|
|
||||||
delete process.env.npm_config_arm_version;
|
|
||||||
delete process.env.npm_config_arch;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can override ARM64 version via --arm-version', function () {
|
|
||||||
process.env.npm_config_arch = 'arm64';
|
|
||||||
process.env.npm_config_arm_version = 'test';
|
|
||||||
assert.strictEqual('arm64vtest', platform().split('-')[1]);
|
|
||||||
delete process.env.npm_config_arm_version;
|
|
||||||
delete process.env.npm_config_arch;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (process.config.variables.arm_version) {
|
|
||||||
it('Can detect ARM version via process.config', function () {
|
|
||||||
process.env.npm_config_arch = 'arm';
|
|
||||||
assert.strictEqual(`armv${process.config.variables.arm_version}`, platform().split('-')[1]);
|
|
||||||
delete process.env.npm_config_arch;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.config.variables.arm_version) {
|
|
||||||
it('Defaults to ARMv6 for 32-bit', function () {
|
|
||||||
process.env.npm_config_arch = 'arm';
|
|
||||||
assert.strictEqual('armv6', platform().split('-')[1]);
|
|
||||||
delete process.env.npm_config_arch;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('Defaults to ARMv8 for 64-bit', function () {
|
|
||||||
process.env.npm_config_arch = 'arm64';
|
|
||||||
assert.strictEqual('arm64v8', platform().split('-')[1]);
|
|
||||||
delete process.env.npm_config_arch;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can ensure version ARMv7 if electron version is present', function () {
|
|
||||||
process.env.npm_config_arch = 'arm';
|
|
||||||
process.versions.electron = 'test';
|
|
||||||
assert.strictEqual('armv7', platform().split('-')[1]);
|
|
||||||
delete process.env.npm_config_arch;
|
|
||||||
delete process.versions.electron;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Can override libc if platform is linux', function () {
|
|
||||||
process.env.npm_config_platform = 'linux';
|
|
||||||
process.env.npm_config_libc = 'test';
|
|
||||||
assert.strictEqual('linuxtest', platform().split('-')[0]);
|
|
||||||
delete process.env.npm_config_platform;
|
|
||||||
delete process.env.npm_config_libc;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Handles libc value "glibc" as default linux', function () {
|
|
||||||
process.env.npm_config_platform = 'linux';
|
|
||||||
process.env.npm_config_libc = 'glibc';
|
|
||||||
assert.strictEqual('linux', platform().split('-')[0]);
|
|
||||||
delete process.env.npm_config_platform;
|
|
||||||
delete process.env.npm_config_libc;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Discards libc value on non-linux platform', function () {
|
|
||||||
process.env.npm_config_platform = 'win32';
|
|
||||||
process.env.npm_config_libc = 'gnuwin32';
|
|
||||||
assert.strictEqual('win32', platform().split('-')[0]);
|
|
||||||
delete process.env.npm_config_platform;
|
|
||||||
delete process.env.npm_config_libc;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -284,4 +284,42 @@ describe('Raw pixel data', function () {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('16-bit roundtrip', () => {
|
||||||
|
it('grey', async () => {
|
||||||
|
const grey = 42000;
|
||||||
|
const png = await sharp(
|
||||||
|
Uint16Array.from([grey]),
|
||||||
|
{ raw: { width: 1, height: 1, channels: 1 } }
|
||||||
|
)
|
||||||
|
.toColourspace('grey16')
|
||||||
|
.png({ compressionLevel: 0 })
|
||||||
|
.toBuffer();
|
||||||
|
const raw = await sharp(png)
|
||||||
|
.toColourspace('grey16')
|
||||||
|
.raw({ depth: 'ushort' })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
assert.strictEqual(raw.readUint16LE(0), grey);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('RGB', async () => {
|
||||||
|
const rgb = [10946, 28657, 46368];
|
||||||
|
const png = await sharp(
|
||||||
|
Uint16Array.from(rgb),
|
||||||
|
{ raw: { width: 1, height: 1, channels: 3 } }
|
||||||
|
)
|
||||||
|
.toColourspace('rgb16')
|
||||||
|
.png({ compressionLevel: 0 })
|
||||||
|
.toBuffer();
|
||||||
|
const raw = await sharp(png)
|
||||||
|
.toColourspace('rgb16')
|
||||||
|
.raw({ depth: 'ushort' })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
assert.strictEqual(raw.readUint16LE(0), rgb[0]);
|
||||||
|
assert.strictEqual(raw.readUint16LE(2), rgb[1]);
|
||||||
|
assert.strictEqual(raw.readUint16LE(4), rgb[2]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -646,7 +646,7 @@ describe('Resize dimensions', function () {
|
|||||||
].forEach(function (kernel) {
|
].forEach(function (kernel) {
|
||||||
it(`kernel ${kernel}`, function (done) {
|
it(`kernel ${kernel}`, function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(320, null, { kernel: kernel })
|
.resize(320, null, { kernel })
|
||||||
.toBuffer(function (err, data, info) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
|||||||
@@ -192,6 +192,23 @@ describe('Rotation', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Auto-rotate by 270 degrees, rectangular output ignoring aspect ratio', function (done) {
|
||||||
|
sharp(fixtures.inputJpgWithLandscapeExif8)
|
||||||
|
.resize(320, 240, { fit: sharp.fit.fill })
|
||||||
|
.rotate()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
sharp(data).metadata(function (err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, metadata.width);
|
||||||
|
assert.strictEqual(240, metadata.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Rotate by 30 degrees, rectangular output ignoring aspect ratio', function (done) {
|
it('Rotate by 30 degrees, rectangular output ignoring aspect ratio', function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(320, 240, { fit: sharp.fit.fill })
|
.resize(320, 240, { fit: sharp.fit.fill })
|
||||||
@@ -489,4 +506,28 @@ describe('Rotation', function () {
|
|||||||
.timeout({ seconds: 5 })
|
.timeout({ seconds: 5 })
|
||||||
.toBuffer()
|
.toBuffer()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it('Rotate 90 then resize with inside fit', async () => {
|
||||||
|
const data = await sharp({ create: { width: 16, height: 8, channels: 3, background: 'red' } })
|
||||||
|
.rotate(90)
|
||||||
|
.resize({ width: 6, fit: 'inside' })
|
||||||
|
.png({ compressionLevel: 0 })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { width, height } = await sharp(data).metadata();
|
||||||
|
assert.strictEqual(width, 6);
|
||||||
|
assert.strictEqual(height, 12);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Resize with inside fit then rotate 90', async () => {
|
||||||
|
const data = await sharp({ create: { width: 16, height: 8, channels: 3, background: 'red' } })
|
||||||
|
.resize({ width: 6, fit: 'inside' })
|
||||||
|
.rotate(90)
|
||||||
|
.png({ compressionLevel: 0 })
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { width, height } = await sharp(data).metadata();
|
||||||
|
assert.strictEqual(width, 3);
|
||||||
|
assert.strictEqual(height, 6);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ describe('Text to image', function () {
|
|||||||
const text = sharp({
|
const text = sharp({
|
||||||
text: {
|
text: {
|
||||||
text: 'Hello, world!',
|
text: 'Hello, world!',
|
||||||
dpi: dpi
|
dpi
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
text.toFile(output, function (err, info) {
|
text.toFile(output, function (err, info) {
|
||||||
@@ -87,7 +87,7 @@ describe('Text to image', function () {
|
|||||||
text: {
|
text: {
|
||||||
text: '<span foreground="red" font="100">red</span><span font="50" background="cyan">blue</span>',
|
text: '<span foreground="red" font="100">red</span><span font="50" background="cyan">blue</span>',
|
||||||
rgba: true,
|
rgba: true,
|
||||||
dpi: dpi
|
dpi
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
text.toFile(output, function (err, info) {
|
text.toFile(output, function (err, info) {
|
||||||
@@ -146,7 +146,7 @@ describe('Text to image', function () {
|
|||||||
text: {
|
text: {
|
||||||
text: '<span background="cyan">cool</span>',
|
text: '<span background="cyan">cool</span>',
|
||||||
font: 'sans 30',
|
font: 'sans 30',
|
||||||
dpi: dpi,
|
dpi,
|
||||||
rgba: true
|
rgba: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ describe('Tile', function () {
|
|||||||
[1, 8192].forEach(function (size) {
|
[1, 8192].forEach(function (size) {
|
||||||
assert.doesNotThrow(function () {
|
assert.doesNotThrow(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
size: size
|
size
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -135,7 +135,7 @@ describe('Tile', function () {
|
|||||||
['zoinks', 1.1, -1, 0, 8193].forEach(function (size) {
|
['zoinks', 1.1, -1, 0, 8193].forEach(function (size) {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
size: size
|
size
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -146,7 +146,7 @@ describe('Tile', function () {
|
|||||||
assert.doesNotThrow(function () {
|
assert.doesNotThrow(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
size: 8192,
|
size: 8192,
|
||||||
overlap: overlap
|
overlap
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -156,7 +156,7 @@ describe('Tile', function () {
|
|||||||
['zoinks', 1.1, -1, 8193].forEach(function (overlap) {
|
['zoinks', 1.1, -1, 8193].forEach(function (overlap) {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
overlap: overlap
|
overlap
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -166,7 +166,7 @@ describe('Tile', function () {
|
|||||||
['fs', 'zip'].forEach(function (container) {
|
['fs', 'zip'].forEach(function (container) {
|
||||||
assert.doesNotThrow(function () {
|
assert.doesNotThrow(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
container: container
|
container
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -176,7 +176,7 @@ describe('Tile', function () {
|
|||||||
['zoinks', 1].forEach(function (container) {
|
['zoinks', 1].forEach(function (container) {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
container: container
|
container
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -186,7 +186,7 @@ describe('Tile', function () {
|
|||||||
['dz', 'google', 'zoomify'].forEach(function (layout) {
|
['dz', 'google', 'zoomify'].forEach(function (layout) {
|
||||||
assert.doesNotThrow(function () {
|
assert.doesNotThrow(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
layout: layout
|
layout
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -196,7 +196,7 @@ describe('Tile', function () {
|
|||||||
['zoinks', 1].forEach(function (layout) {
|
['zoinks', 1].forEach(function (layout) {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
layout: layout
|
layout
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -254,7 +254,7 @@ describe('Tile', function () {
|
|||||||
[90, 270, -90].forEach(function (angle) {
|
[90, 270, -90].forEach(function (angle) {
|
||||||
assert.doesNotThrow(function () {
|
assert.doesNotThrow(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
angle: angle
|
angle
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -264,7 +264,7 @@ describe('Tile', function () {
|
|||||||
['zoinks', 1.1, -1, 27].forEach(function (angle) {
|
['zoinks', 1.1, -1, 27].forEach(function (angle) {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().tile({
|
sharp().tile({
|
||||||
angle: angle
|
angle
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -146,14 +146,6 @@ describe('Utilities', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Vendor', function () {
|
|
||||||
it('Contains expected attributes', function () {
|
|
||||||
assert.strictEqual('object', typeof sharp.vendor);
|
|
||||||
assert.strictEqual('string', typeof sharp.vendor.current);
|
|
||||||
assert.strictEqual(true, Array.isArray(sharp.vendor.installed));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Block', () => {
|
describe('Block', () => {
|
||||||
it('Can block a named operation', () => {
|
it('Can block a named operation', () => {
|
||||||
sharp.block({ operation: ['test'] });
|
sharp.block({ operation: ['test'] });
|
||||||
|
|||||||
Reference in New Issue
Block a user