mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 13:46:19 +01:00
Compare commits
85 Commits
v0.34.4-rc
...
2291c0b864
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2291c0b864 | ||
|
|
ed6b7384d0 | ||
|
|
ef77388a73 | ||
|
|
66764b359b | ||
|
|
8561f0da1d | ||
|
|
0468c1be9f | ||
|
|
4b1680c312 | ||
|
|
2346722c0d | ||
|
|
a5e726002c | ||
|
|
d161e45e06 | ||
|
|
006d37b2d0 | ||
|
|
0d872bd13a | ||
|
|
1cf4b7f04d | ||
|
|
e50c0c2e04 | ||
|
|
3278a9a913 | ||
|
|
1b2f79335d | ||
|
|
937167933b | ||
|
|
dbcb7e60bd | ||
|
|
e1bad5470e | ||
|
|
1a2c1c8833 | ||
|
|
aaeded2b67 | ||
|
|
f6cdd36559 | ||
|
|
283c7d3f0c | ||
|
|
34c39fa194 | ||
|
|
7b4c476243 | ||
|
|
084a30f8bf | ||
|
|
3609c61a22 | ||
|
|
dc6820b49f | ||
|
|
f2a49d19c9 | ||
|
|
e062456868 | ||
|
|
6450c704a6 | ||
|
|
f7c95d1bf0 | ||
|
|
ef86a75560 | ||
|
|
6c1e840098 | ||
|
|
e1628d8ef5 | ||
|
|
4f9f8179a6 | ||
|
|
09d5aa8cfa | ||
|
|
040b73ca74 | ||
|
|
1f2f33d9a7 | ||
|
|
69b2c45615 | ||
|
|
9e4e184132 | ||
|
|
206eb4a89a | ||
|
|
c1c16ed3e6 | ||
|
|
b7fda60a85 | ||
|
|
1bbee519aa | ||
|
|
2324d75f7f | ||
|
|
5e72ad95fa | ||
|
|
6b922b30d5 | ||
|
|
54722dd582 | ||
|
|
adb6275ae9 | ||
|
|
f2978651f0 | ||
|
|
c446d743a2 | ||
|
|
3498eb63e3 | ||
|
|
3009957120 | ||
|
|
b36237ddcb | ||
|
|
a0af662d78 | ||
|
|
ee437832e2 | ||
|
|
529901177b | ||
|
|
4710092b2a | ||
|
|
ed1ac43e55 | ||
|
|
dfcbceee4b | ||
|
|
35d3f56c67 | ||
|
|
9f4bace03b | ||
|
|
b507831a11 | ||
|
|
905f69837e | ||
|
|
b0154ed83c | ||
|
|
93b814f849 | ||
|
|
6d4d44e2fa | ||
|
|
d8686e7c64 | ||
|
|
23a0e81d98 | ||
|
|
43b579c903 | ||
|
|
45f8717900 | ||
|
|
c270455007 | ||
|
|
1835288ab8 | ||
|
|
c1e33de33c | ||
|
|
0e4b648593 | ||
|
|
40be212bba | ||
|
|
8ceeda9ae9 | ||
|
|
16e248f93e | ||
|
|
b77c97067a | ||
|
|
660bbdb1c0 | ||
|
|
4164705113 | ||
|
|
c01e272db5 | ||
|
|
8607ff2f4a | ||
|
|
cd337e4de3 |
17
.cirrus.yml
17
.cirrus.yml
@@ -1,17 +0,0 @@
|
||||
freebsd_instance:
|
||||
image_family: freebsd-15-0-snap
|
||||
|
||||
task:
|
||||
name: FreeBSD
|
||||
env:
|
||||
IGNORE_OSVERSION: yes
|
||||
skip_notifications: true
|
||||
prerequisites_script:
|
||||
- pkg update -f
|
||||
- pkg upgrade -y
|
||||
- pkg install -y devel/git devel/pkgconf graphics/vips www/node20 www/npm
|
||||
- pkg-config --modversion vips-cpp
|
||||
install_script:
|
||||
- npm install --build-from-source
|
||||
test_script:
|
||||
- npx mocha --no-config --spec=test/unit/io.js --timeout=30000
|
||||
@@ -7,6 +7,4 @@ indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
max_line_length = 120
|
||||
|
||||
7
.github/CONTRIBUTING.md
vendored
7
.github/CONTRIBUTING.md
vendored
@@ -6,8 +6,6 @@ Hello, thank you for your interest in helping!
|
||||
|
||||
Please create a [new issue](https://github.com/lovell/sharp/issues/new) containing the steps to reproduce the problem.
|
||||
|
||||
If you're having installation problems, please include the output of running `npm install --verbose sharp`.
|
||||
|
||||
New bugs are assigned a `triage` label whilst under investigation.
|
||||
|
||||
## Submit a new feature request
|
||||
@@ -16,7 +14,7 @@ If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exist
|
||||
it's probably fastest to add a comment to it about your requirement.
|
||||
|
||||
Implementation is usually straightforward if libvips
|
||||
[already supports](https://www.libvips.org/API/current/func-list.html)
|
||||
[already supports](https://www.libvips.org/API/current/function-list.html)
|
||||
the feature you need.
|
||||
|
||||
## Submit a Pull Request to fix a bug
|
||||
@@ -27,12 +25,11 @@ Please select the `main` branch as the destination for your Pull Request so your
|
||||
|
||||
Please squash your changes into a single commit using a command like `git rebase -i upstream/main`.
|
||||
|
||||
To test C++ changes, you can compile the module using `npm install --build-from-source` and then run the tests using `npm test`.
|
||||
To test C++ changes, you can compile the module using `npm run build` and then run the tests using `npm test`.
|
||||
|
||||
## Submit a Pull Request with a new feature
|
||||
|
||||
Please add JavaScript [unit tests](https://github.com/lovell/sharp/tree/main/test/unit) to cover your new feature.
|
||||
A test coverage report for the JavaScript code is generated in the `coverage/lcov-report` directory.
|
||||
Please also update the [TypeScript definitions](https://github.com/lovell/sharp/tree/main/lib/index.d.ts), along with the [type definition tests](https://github.com/lovell/sharp/tree/main/test/types/sharp.test-d.ts).
|
||||
|
||||
Where possible, the functional tests use gradient-based perceptual hashes
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/installation.md
vendored
2
.github/ISSUE_TEMPLATE/installation.md
vendored
@@ -31,7 +31,7 @@ please open an issue against that package instead.
|
||||
|
||||
<!-- Please place an [x] in the relevant box to confirm. -->
|
||||
|
||||
- [ ] I am using Node.js with a version that satisfies `^18.17.0 || ^20.3.0 || >=21.0.0`
|
||||
- [ ] I am using Node.js with a version that satisfies `>=20.9.0`
|
||||
- [ ] I am using Deno
|
||||
- [ ] I am using Bun
|
||||
|
||||
|
||||
272
.github/workflows/ci.yml
vendored
272
.github/workflows/ci.yml
vendored
@@ -4,9 +4,23 @@ on:
|
||||
- pull_request
|
||||
permissions: {}
|
||||
jobs:
|
||||
lint:
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "24"
|
||||
- run: npm install --ignore-scripts
|
||||
- run: npm run lint-cpp
|
||||
- run: npm run lint-js
|
||||
- run: npm run lint-types
|
||||
build-native:
|
||||
permissions:
|
||||
contents: read
|
||||
needs: lint
|
||||
name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
container: ${{ matrix.container }}
|
||||
@@ -17,123 +31,124 @@ jobs:
|
||||
- os: ubuntu-24.04
|
||||
container: rockylinux:8
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: linux-x64
|
||||
package: true
|
||||
- os: ubuntu-24.04
|
||||
container: rockylinux:8
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
platform: linux-x64
|
||||
- os: ubuntu-24.04
|
||||
container: rockylinux:8
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: linux-x64
|
||||
- os: ubuntu-24.04
|
||||
container: node:18-alpine3.17
|
||||
nodejs_version_major: 18
|
||||
platform: linuxmusl-x64
|
||||
package: true
|
||||
container: rockylinux:8
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: linux-x64
|
||||
- os: ubuntu-24.04
|
||||
container: node:20-alpine3.18
|
||||
nodejs_version_major: 20
|
||||
platform: linuxmusl-x64
|
||||
package: true
|
||||
- os: ubuntu-24.04
|
||||
container: node:22-alpine3.20
|
||||
nodejs_version_major: 22
|
||||
platform: linuxmusl-x64
|
||||
- os: ubuntu-24.04
|
||||
container: node:24-alpine3.22
|
||||
nodejs_version_major: 24
|
||||
platform: linuxmusl-x64
|
||||
- os: ubuntu-24.04-arm
|
||||
container: arm64v8/rockylinux:8
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: linux-arm64
|
||||
package: true
|
||||
- os: ubuntu-24.04-arm
|
||||
container: arm64v8/rockylinux:8
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: linux-arm64
|
||||
- os: macos-13
|
||||
- os: ubuntu-24.04-arm
|
||||
container: arm64v8/rockylinux:8
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: linux-arm64
|
||||
- os: macos-15-intel
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
platform: darwin-x64
|
||||
package: true
|
||||
- os: macos-13
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: darwin-x64
|
||||
- os: macos-13
|
||||
package: true
|
||||
- os: macos-15-intel
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: darwin-x64
|
||||
- os: macos-14
|
||||
- os: macos-15-intel
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: darwin-x64
|
||||
- os: macos-15
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
platform: darwin-arm64
|
||||
package: true
|
||||
- os: macos-14
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: darwin-arm64
|
||||
- os: macos-14
|
||||
package: true
|
||||
- os: macos-15
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: darwin-arm64
|
||||
- os: macos-15
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: darwin-arm64
|
||||
- os: windows-2022
|
||||
nodejs_arch: x86
|
||||
nodejs_version: "18.18.2" # pinned to avoid 18.19.0 and npm 10
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: win32-ia32
|
||||
package: true
|
||||
- os: windows-2022
|
||||
nodejs_arch: x86
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
platform: win32-ia32
|
||||
- os: windows-2022
|
||||
nodejs_arch: x86
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version_major: 22
|
||||
platform: win32-ia32
|
||||
- os: windows-2022
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: win32-x64
|
||||
package: true
|
||||
- os: windows-2022
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: win32-x64
|
||||
- os: windows-2022
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version_major: 22
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: win32-x64
|
||||
- os: windows-11-arm
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: win32-arm64
|
||||
package: true
|
||||
- os: windows-11-arm
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: win32-arm64
|
||||
- os: windows-11-arm
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: win32-arm64
|
||||
steps:
|
||||
- name: Dependencies (Rocky Linux glibc)
|
||||
if: contains(matrix.container, 'rockylinux')
|
||||
@@ -145,24 +160,22 @@ jobs:
|
||||
run: apk add build-base git python3 font-noto --update-cache
|
||||
- name: Dependencies (Python 3.11 - macOS, Windows)
|
||||
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Dependencies (Node.js)
|
||||
if: "!contains(matrix.platform, 'linuxmusl')"
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ matrix.nodejs_version }}
|
||||
architecture: ${{ matrix.nodejs_arch }}
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install
|
||||
run: npm install --build-from-source
|
||||
- name: Test
|
||||
run: npm test
|
||||
- name: Populate npm package
|
||||
if: matrix.package
|
||||
- uses: actions/checkout@v6
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- run: npm run test-unit
|
||||
- if: matrix.package
|
||||
run: npm run package-from-local-build
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: matrix.package
|
||||
with:
|
||||
name: ${{ matrix.platform }}
|
||||
@@ -172,22 +185,25 @@ jobs:
|
||||
build-linuxmusl-arm64:
|
||||
permissions:
|
||||
contents: read
|
||||
needs: lint
|
||||
name: "build-linuxmusl-arm64 [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
|
||||
runs-on: ubuntu-24.04-arm
|
||||
container:
|
||||
image: ${{ matrix.container }}
|
||||
volumes:
|
||||
- /opt:/opt:rw,rshared
|
||||
- /opt:/__e/node20:ro,rshared
|
||||
- /opt:/__e/node24:ro,rshared
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- container: node:18-alpine3.17
|
||||
nodejs_version_major: 18
|
||||
package: true
|
||||
- container: node:20-alpine3.18
|
||||
- container: node:20-alpine3.20
|
||||
nodejs_version_major: 20
|
||||
package: true
|
||||
- container: node:22-alpine3.20
|
||||
nodejs_version_major: 22
|
||||
- container: node:24-alpine3.22
|
||||
nodejs_version_major: 24
|
||||
steps:
|
||||
- name: Allow Linux musl containers on ARM64 runners # https://github.com/actions/runner/issues/801#issuecomment-2394425757
|
||||
shell: sh
|
||||
@@ -198,15 +214,13 @@ jobs:
|
||||
ln -s /usr/bin/node /opt/bin/node
|
||||
- name: Dependencies
|
||||
run: apk add build-base git python3 font-noto --update-cache
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install
|
||||
run: npm install --build-from-source
|
||||
- name: Test
|
||||
run: npm test
|
||||
- name: Populate npm package
|
||||
if: matrix.package
|
||||
- uses: actions/checkout@v6
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- run: npm run test-unit
|
||||
- if: matrix.package
|
||||
run: npm run package-from-local-build
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: matrix.package
|
||||
with:
|
||||
name: linuxmusl-arm64
|
||||
@@ -216,6 +230,7 @@ jobs:
|
||||
build-qemu:
|
||||
permissions:
|
||||
contents: read
|
||||
needs: lint
|
||||
name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] [package]"
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
@@ -223,63 +238,93 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux-arm
|
||||
distro: bullseye
|
||||
run_on_arch: armv6
|
||||
base_image: "balenalib/rpi-raspbian:bullseye"
|
||||
nodejs_arch: armv6l
|
||||
nodejs_hostname: unofficial-builds.nodejs.org
|
||||
nodejs_version: "18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "20.9.0"
|
||||
nodejs_version_major: 20
|
||||
- platform: linux-s390x
|
||||
distro: bookworm
|
||||
run_on_arch: s390x
|
||||
base_image: "--platform=linux/s390x s390x/debian:bookworm"
|
||||
nodejs_arch: s390x
|
||||
nodejs_hostname: nodejs.org
|
||||
nodejs_version: "18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "20.9.0"
|
||||
nodejs_version_major: 20
|
||||
- platform: linux-ppc64
|
||||
distro: bookworm
|
||||
run_on_arch: ppc64le
|
||||
base_image: "--platform=linux/ppc64le ppc64le/debian:bookworm"
|
||||
nodejs_arch: ppc64le
|
||||
nodejs_hostname: nodejs.org
|
||||
nodejs_version: "18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "20.9.0"
|
||||
nodejs_version_major: 20
|
||||
- platform: linux-riscv64
|
||||
base_image: "--platform=linux/riscv64 riscv64/debian:trixie"
|
||||
compiler_flags: "-march=rv64gc"
|
||||
nodejs_arch: riscv64
|
||||
nodejs_hostname: unofficial-builds.nodejs.org
|
||||
nodejs_version: "20.19.5"
|
||||
nodejs_version_major: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: uraimo/run-on-arch-action@v3
|
||||
with:
|
||||
arch: ${{ matrix.run_on_arch }}
|
||||
distro: ${{ matrix.distro }}
|
||||
arch: none
|
||||
distro: none
|
||||
base_image: ${{ matrix.base_image }}
|
||||
env: |
|
||||
CFLAGS: "${{ matrix.compiler_flags }}"
|
||||
CXXFLAGS: "${{ matrix.compiler_flags }}"
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y curl g++ git libatomic1 make python3 xz-utils
|
||||
mkdir /opt/nodejs
|
||||
curl --silent https://${{ matrix.nodejs_hostname }}/download/release/v${{ matrix.nodejs_version}}/node-v${{ matrix.nodejs_version}}-linux-${{ matrix.nodejs_arch }}.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 --timeout=30000
|
||||
npm install
|
||||
npm run build
|
||||
node --test test/unit/io.js
|
||||
npm run package-from-local-build
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ matrix.platform }}
|
||||
path: npm/${{ matrix.platform }}
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
build-freebsd:
|
||||
permissions:
|
||||
contents: read
|
||||
needs: lint
|
||||
name: "build-freebsd"
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: vmactions/freebsd-vm@v1
|
||||
continue-on-error: true
|
||||
with:
|
||||
prepare: |
|
||||
pkg update -f
|
||||
pkg upgrade -y
|
||||
pkg install -y devel/git devel/pkgconf graphics/vips www/node22 www/npm
|
||||
pkg-config --modversion vips-cpp
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
node --test test/unit/io.js
|
||||
build-emscripten:
|
||||
permissions:
|
||||
contents: read
|
||||
needs: lint
|
||||
name: "build-wasm32 [package]"
|
||||
runs-on: ubuntu-24.04
|
||||
container: "emscripten/emsdk:4.0.11"
|
||||
container: "emscripten/emsdk:4.0.21"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Dependencies
|
||||
run: apt-get update && apt-get install -y pkg-config
|
||||
- name: Dependencies (Node.js)
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: "20"
|
||||
- name: Install
|
||||
run: emmake npm install --build-from-source
|
||||
- run: npm install
|
||||
- run: emmake npm run build
|
||||
- name: Verify emscripten versions match
|
||||
run: |
|
||||
EMSCRIPTEN_VERSION_LIBVIPS=$(node -p "require('@img/sharp-libvips-dev-wasm32/versions').emscripten")
|
||||
@@ -287,11 +332,9 @@ jobs:
|
||||
echo "libvips built with emscripten $EMSCRIPTEN_VERSION_LIBVIPS"
|
||||
echo "sharp built with emscripten $EMSCRIPTEN_VERSION_SHARP"
|
||||
test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP"
|
||||
- name: Test
|
||||
run: emmake npm test
|
||||
- name: Populate npm package
|
||||
run: emmake npm run package-from-local-build
|
||||
- uses: actions/upload-artifact@v4
|
||||
- run: emmake npm run test-unit
|
||||
- run: emmake npm run package-from-local-build
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: wasm32
|
||||
path: npm/wasm32
|
||||
@@ -300,6 +343,7 @@ jobs:
|
||||
release:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- build-native
|
||||
@@ -308,17 +352,15 @@ jobs:
|
||||
- build-emscripten
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
path: npm
|
||||
- name: Create npm workspace tarball
|
||||
run: tar -vcaf npm-workspace.tar.xz --directory npm --exclude=from-local-build.js .
|
||||
- uses: actions/setup-node@v4
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "24"
|
||||
node-version: '24'
|
||||
- name: Create release notes
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: npm run package-release-notes
|
||||
- name: Create GitHub release for tag
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
@@ -329,3 +371,9 @@ jobs:
|
||||
prerelease: ${{ contains(github.ref, '-rc') }}
|
||||
makeLatest: ${{ !contains(github.ref, '-rc') }}
|
||||
bodyFile: release-notes.md
|
||||
- name: Publish platform-specific npm packages
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: cd npm && npm publish --workspaces --tag=${{ contains(github.ref, '-rc') && 'next' || 'latest' }}
|
||||
- name: Publish sharp npm package
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: npm publish --tag=${{ contains(github.ref, '-rc') && 'next' || 'latest' }}
|
||||
|
||||
26
.github/workflows/npm.yml
vendored
26
.github/workflows/npm.yml
vendored
@@ -43,30 +43,30 @@ jobs:
|
||||
runtime: bun
|
||||
|
||||
- name: darwin-x64-node-npm
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
runtime: node
|
||||
package-manager: npm
|
||||
- name: darwin-x64-node-pnpm
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
runtime: node
|
||||
package-manager: pnpm
|
||||
- name: darwin-x64-node-yarn
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
runtime: node
|
||||
package-manager: yarn
|
||||
- name: darwin-x64-node-yarn-pnp
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
runtime: node
|
||||
package-manager: yarn-pnp
|
||||
- name: darwin-x64-node-yarn-v1
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
runtime: node
|
||||
package-manager: yarn-v1
|
||||
- name: darwin-x64-deno
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
runtime: deno
|
||||
- name: darwin-x64-bun
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
runtime: bun
|
||||
|
||||
- name: win32-x64-node-npm
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
if: ${{ matrix.runtime == 'node' }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
- name: Install pnpm
|
||||
@@ -106,9 +106,9 @@ jobs:
|
||||
version: 8
|
||||
- name: Install Deno
|
||||
if: ${{ matrix.runtime == 'deno' }}
|
||||
uses: denoland/setup-deno@v1
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
deno-version: v2.x
|
||||
- name: Install Bun
|
||||
if: ${{ matrix.runtime == 'bun' }}
|
||||
uses: oven-sh/setup-bun@v2
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
|
||||
- name: Version
|
||||
id: version
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
core.setOutput('semver', context.ref.replace('refs/tags/v',''))
|
||||
@@ -185,7 +185,9 @@ jobs:
|
||||
|
||||
- name: Run with Deno
|
||||
if: ${{ matrix.runtime == 'deno' }}
|
||||
run: deno run --allow-read --allow-ffi release.mjs
|
||||
run: |
|
||||
deno install
|
||||
deno run --allow-env --allow-ffi --allow-read --allow-sys release.mjs
|
||||
|
||||
- name: Run with Bun
|
||||
if: ${{ matrix.runtime == 'bun' }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
src/build
|
||||
src/node_modules
|
||||
node_modules
|
||||
/coverage
|
||||
npm/*/*
|
||||
!npm/*/package.json
|
||||
test/bench/node_modules
|
||||
@@ -9,7 +8,6 @@ test/fixtures/output*
|
||||
test/fixtures/vips-properties.xml
|
||||
test/leak/libvips.supp
|
||||
.DS_Store
|
||||
.nyc_output
|
||||
.vscode/
|
||||
package-lock.json
|
||||
.idea
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"parallel": true,
|
||||
"slow": 1000,
|
||||
"timeout": 30000,
|
||||
"require": "./test/beforeEach.js",
|
||||
"spec": "./test/unit/*.js"
|
||||
}
|
||||
@@ -8,7 +8,7 @@ smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions
|
||||
|
||||
It can be used with all JavaScript runtimes
|
||||
that provide support for Node-API v9, including
|
||||
Node.js (^18.17.0 or >= 20.3.0), Deno and Bun.
|
||||
Node.js (>= 20.9.0), Deno and Bun.
|
||||
|
||||
Resizing an image is typically 4x-5x faster than using the
|
||||
quickest ImageMagick and GraphicsMagick settings
|
||||
|
||||
26
biome.json
Normal file
26
biome.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": false,
|
||||
"useEditorconfig": true
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "single"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import starlight from '@astrojs/starlight';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import starlightAutoSidebar from 'starlight-auto-sidebar';
|
||||
|
||||
import { version } from '../package.json';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/starlight": "^0.34.6",
|
||||
"astro": "^5.11.1",
|
||||
"starlight-auto-sidebar": "^0.1.2"
|
||||
"@astrojs/starlight": "^0.37.1",
|
||||
"astro": "^5.16.6",
|
||||
"jsdoc-to-markdown": "^9.1.3",
|
||||
"starlight-auto-sidebar": "^0.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,3 +323,9 @@ GitHub: https://github.com/hans00
|
||||
|
||||
Name: Thibaut Patel
|
||||
GitHub: https://github.com/tpatel
|
||||
|
||||
Name: Maël Nison
|
||||
GitHub: https://github.com/arcanis
|
||||
|
||||
Name: Dmytro Tiapukhin
|
||||
GitHub: https://github.com/eddienubes
|
||||
|
||||
@@ -8,7 +8,7 @@ title: Channel manipulation
|
||||
|
||||
Remove alpha channels, 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).
|
||||
|
||||
|
||||
**Example**
|
||||
@@ -61,6 +61,8 @@ const rgba = await sharp(rgb)
|
||||
|
||||
Extract a single channel from a multi-channel image.
|
||||
|
||||
The output colourspace will be either `b-w` (8-bit) or `grey16` (16-bit).
|
||||
|
||||
|
||||
**Throws**:
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ as defined by [toColourspace](#tocolourspace).
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [colourspace] | <code>string</code> | pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://github.com/libvips/libvips/blob/41cff4e9d0838498487a00623462204eb10ee5b8/libvips/iofuncs/enumtypes.c#L774) |
|
||||
| [colourspace] | <code>string</code> | pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://www.libvips.org/API/current/enum.Interpretation.html) |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
@@ -123,7 +123,7 @@ By default output image will be web-friendly sRGB, with additional channels inte
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [colourspace] | <code>string</code> | output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/3c0bfdf74ce1dc37a6429bed47fa76f16e2cd70a/libvips/iofuncs/enumtypes.c#L777-L794) |
|
||||
| [colourspace] | <code>string</code> | output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html) |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
|
||||
@@ -21,7 +21,7 @@ The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
|
||||
`hard-light`, `soft-light`, `difference`, `exclusion`.
|
||||
|
||||
More information about blend modes can be found at
|
||||
https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
|
||||
https://www.libvips.org/API/current/enum.BlendMode.html
|
||||
and https://www.cairographics.org/operators/
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ and https://www.cairographics.org/operators/
|
||||
| [images[].raw.height] | <code>Number</code> | | |
|
||||
| [images[].raw.channels] | <code>Number</code> | | |
|
||||
| [images[].animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image. |
|
||||
| [images[].failOn] | <code>string</code> | <code>"'warning'"</code> | @see [constructor parameters](/api-constructor#parameters) |
|
||||
| [images[].limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | @see [constructor parameters](/api-constructor#parameters) |
|
||||
| [images[].failOn] | <code>string</code> | <code>"'warning'"</code> | @see [constructor parameters](/api-constructor/) |
|
||||
| [images[].limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | @see [constructor parameters](/api-constructor/) |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
|
||||
@@ -13,17 +13,17 @@ It does not take into consideration any operations to be applied to the output i
|
||||
such as resize or rotate.
|
||||
|
||||
Dimensions in the response will respect the `page` and `pages` properties of the
|
||||
[constructor parameters](/api-constructor#parameters).
|
||||
[constructor parameters](/api-constructor/).
|
||||
|
||||
A `Promise` is returned when `callback` is not provided.
|
||||
|
||||
- `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
|
||||
- `format`: Name of decoder used to parse image e.g. `jpeg`, `png`, `webp`, `gif`, `svg`, `heif`, `tiff`
|
||||
- `size`: Total size of image in bytes, for Stream and Buffer input only
|
||||
- `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
|
||||
- `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
|
||||
- `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
|
||||
- `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
|
||||
- `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
||||
- `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
|
||||
- `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/enum.BandFormat.html)
|
||||
- `density`: Number of pixels per inch (DPI), if present
|
||||
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||
@@ -50,6 +50,7 @@ A `Promise` is returned when `callback` is not provided.
|
||||
- `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
||||
- `formatMagick`: String containing format for images loaded via *magick
|
||||
- `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
|
||||
- `gainMap.image`: HDR gain map, if present, as compressed JPEG image.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ inputStream
|
||||
|
||||
|
||||
## sharpen
|
||||
> sharpen([options], [flat], [jagged]) ⇒ <code>Sharp</code>
|
||||
> sharpen([options]) ⇒ <code>Sharp</code>
|
||||
|
||||
Sharpen the image.
|
||||
|
||||
@@ -179,7 +179,7 @@ When used without parameters, performs a fast, mild sharpen of the output image.
|
||||
When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
|
||||
Fine-grained control over the level of sharpening in "flat" (m1) and "jagged" (m2) areas is available.
|
||||
|
||||
See [libvips sharpen](https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen) operation.
|
||||
See [libvips sharpen](https://www.libvips.org/API/current/method.Image.sharpen.html) operation.
|
||||
|
||||
|
||||
**Throws**:
|
||||
@@ -189,15 +189,13 @@ See [libvips sharpen](https://www.libvips.org/API/current/libvips-convolution.ht
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [options] | <code>Object</code> \| <code>number</code> | | if present, is an Object with attributes |
|
||||
| [options] | <code>Object</code> | | if present, is an Object with attributes |
|
||||
| [options.sigma] | <code>number</code> | | the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`, between 0.000001 and 10 |
|
||||
| [options.m1] | <code>number</code> | <code>1.0</code> | the level of sharpening to apply to "flat" areas, between 0 and 1000000 |
|
||||
| [options.m2] | <code>number</code> | <code>2.0</code> | the level of sharpening to apply to "jagged" areas, between 0 and 1000000 |
|
||||
| [options.x1] | <code>number</code> | <code>2.0</code> | threshold between "flat" and "jagged", between 0 and 1000000 |
|
||||
| [options.y2] | <code>number</code> | <code>10.0</code> | maximum amount of brightening, between 0 and 1000000 |
|
||||
| [options.y3] | <code>number</code> | <code>20.0</code> | maximum amount of darkening, between 0 and 1000000 |
|
||||
| [flat] | <code>number</code> | | (deprecated) see `options.m1`. |
|
||||
| [jagged] | <code>number</code> | | (deprecated) see `options.m2`. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
@@ -360,8 +358,6 @@ with all white pixel values made fully transparent.
|
||||
|
||||
Existing alpha channel values for non-white pixels remain unchanged.
|
||||
|
||||
This feature is experimental and the API may change.
|
||||
|
||||
|
||||
**Since**: 0.32.1
|
||||
**Example**
|
||||
|
||||
@@ -117,6 +117,38 @@ await sharp(pixelArray, { raw: { width, height, channels } })
|
||||
```
|
||||
|
||||
|
||||
## toUint8Array
|
||||
> toUint8Array() ⇒ <code>Promise.<{data: Uint8Array, info: Object}></code>
|
||||
|
||||
Write output to a `Uint8Array` backed by a transferable `ArrayBuffer`.
|
||||
JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
||||
|
||||
Use [toFormat](#toformat) or one of the format-specific functions such as [jpeg](#jpeg), [png](#png) etc. to set the output format.
|
||||
|
||||
If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
||||
|
||||
By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
See [keepExif](#keepexif) and similar methods for control over this.
|
||||
|
||||
Resolves with an `Object` containing:
|
||||
- `data` is the output image as a `Uint8Array` backed by a transferable `ArrayBuffer`.
|
||||
- `info` contains properties relating to the output image such as `width` and `height`.
|
||||
|
||||
|
||||
**Since**: v0.35.0
|
||||
**Example**
|
||||
```js
|
||||
const { data, info } = await sharp(input).toUint8Array();
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
const { data } = await sharp(input)
|
||||
.avif()
|
||||
.toUint8Array();
|
||||
const base64String = data.toBase64();
|
||||
```
|
||||
|
||||
|
||||
## keepExif
|
||||
> keepExif() ⇒ <code>Sharp</code>
|
||||
|
||||
@@ -201,7 +233,7 @@ const dataWithMergedExif = await sharp(inputWithExif)
|
||||
|
||||
Keep ICC profile from the input image in the output image.
|
||||
|
||||
Where necessary, will attempt to convert the output colour space to match the profile.
|
||||
When input and output colour spaces differ, use with [toColourspace](/api-colour/#tocolourspace) and optionally [pipelineColourspace](/api-colour/#pipelinecolourspace).
|
||||
|
||||
|
||||
**Since**: 0.33.0
|
||||
@@ -211,6 +243,14 @@ const outputWithIccProfile = await sharp(inputWithIccProfile)
|
||||
.keepIccProfile()
|
||||
.toBuffer();
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
const cmykOutputWithIccProfile = await sharp(cmykInputWithIccProfile)
|
||||
.pipelineColourspace('cmyk')
|
||||
.toColourspace('cmyk')
|
||||
.keepIccProfile()
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
|
||||
## withIccProfile
|
||||
@@ -242,6 +282,27 @@ const outputWithP3 = await sharp(input)
|
||||
```
|
||||
|
||||
|
||||
## withGainMap
|
||||
> withGainMap() ⇒ <code>Sharp</code>
|
||||
|
||||
If the input contains gain map metadata, use it to convert the main image to HDR (High Dynamic Range) before further processing.
|
||||
The input gain map is discarded.
|
||||
|
||||
If the output is JPEG, generate and attach a new ISO 21496-1 gain map.
|
||||
JPEG output options other than `quality` are ignored.
|
||||
|
||||
This feature is experimental and the API may change.
|
||||
|
||||
|
||||
**Since**: 0.35.0
|
||||
**Example**
|
||||
```js
|
||||
const outputWithGainMap = await sharp(inputWithGainMap)
|
||||
.withGainMap()
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
|
||||
## keepXmp
|
||||
> keepXmp() ⇒ <code>Sharp</code>
|
||||
|
||||
@@ -430,7 +491,7 @@ Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
||||
Set `palette` to `true` for slower, indexed PNG output.
|
||||
|
||||
For 16 bits per pixel output, convert to `rgb16` via
|
||||
[toColourspace](/api-colour#tocolourspace).
|
||||
[toColourspace](/api-colour/#tocolourspace).
|
||||
|
||||
|
||||
**Throws**:
|
||||
@@ -502,6 +563,7 @@ Use these WebP options for output image.
|
||||
| [options.delay] | <code>number</code> \| <code>Array.<number></code> | | delay(s) between animation frames (in milliseconds) |
|
||||
| [options.minSize] | <code>boolean</code> | <code>false</code> | prevent use of animation key frames to minimise file size (slow) |
|
||||
| [options.mixed] | <code>boolean</code> | <code>false</code> | allow mixture of lossy and lossless animation frames (slow) |
|
||||
| [options.exact] | <code>boolean</code> | <code>false</code> | preserve the colour data in transparent pixels |
|
||||
| [options.force] | <code>boolean</code> | <code>true</code> | force WebP output, otherwise attempt to use input format |
|
||||
|
||||
**Example**
|
||||
@@ -589,7 +651,7 @@ Use these JP2 options for output image.
|
||||
|
||||
Requires libvips compiled with support for OpenJPEG.
|
||||
The prebuilt binaries do not include this - see
|
||||
[installing a custom libvips](https://sharp.pixelplumbing.com/install#custom-libvips).
|
||||
[installing a custom libvips](/install#custom-libvips).
|
||||
|
||||
|
||||
**Throws**:
|
||||
@@ -646,6 +708,7 @@ instead of providing `xres` and `yres` in pixels/mm.
|
||||
| [options.quality] | <code>number</code> | <code>80</code> | quality, integer 1-100 |
|
||||
| [options.force] | <code>boolean</code> | <code>true</code> | force TIFF output, otherwise attempt to use input format |
|
||||
| [options.compression] | <code>string</code> | <code>"'jpeg'"</code> | compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k |
|
||||
| [options.bigtiff] | <code>boolean</code> | <code>false</code> | use BigTIFF variant (has no effect when compression is none) |
|
||||
| [options.predictor] | <code>string</code> | <code>"'horizontal'"</code> | compression predictor options: none, horizontal, float |
|
||||
| [options.pyramid] | <code>boolean</code> | <code>false</code> | write an image pyramid |
|
||||
| [options.tile] | <code>boolean</code> | <code>false</code> | write a tiled tiff |
|
||||
@@ -654,7 +717,7 @@ instead of providing `xres` and `yres` in pixels/mm.
|
||||
| [options.xres] | <code>number</code> | <code>1.0</code> | horizontal resolution in pixels/mm |
|
||||
| [options.yres] | <code>number</code> | <code>1.0</code> | vertical resolution in pixels/mm |
|
||||
| [options.resolutionUnit] | <code>string</code> | <code>"'inch'"</code> | resolution unit options: inch, cm |
|
||||
| [options.bitdepth] | <code>number</code> | <code>8</code> | reduce bitdepth to 1, 2 or 4 bit |
|
||||
| [options.bitdepth] | <code>number</code> | <code>0</code> | reduce bitdepth to 1, 2 or 4 bit |
|
||||
| [options.miniswhite] | <code>boolean</code> | <code>false</code> | write 1-bit images as miniswhite |
|
||||
|
||||
**Example**
|
||||
@@ -678,8 +741,7 @@ Use these AVIF options for output image.
|
||||
AVIF image sequences are not supported.
|
||||
Prebuilt binaries support a bitdepth of 8 only.
|
||||
|
||||
This feature is experimental on the Windows ARM64 platform
|
||||
and requires a CPU with ARM64v8.4 or later.
|
||||
When using Windows ARM64, this feature requires a CPU with ARM64v8.4 or later.
|
||||
|
||||
|
||||
**Throws**:
|
||||
@@ -696,6 +758,7 @@ and requires a CPU with ARM64v8.4 or later.
|
||||
| [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.bitdepth] | <code>number</code> | <code>8</code> | set bitdepth to 8, 10 or 12 bit |
|
||||
| [options.tune] | <code>string</code> | <code>"'iq'"</code> | tune output for a quality metric, one of 'iq' (default), 'ssim' (default when lossless) or 'psnr' |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
@@ -735,6 +798,7 @@ globally-installed libvips compiled with support for libheif, libde265 and x265.
|
||||
| [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.bitdepth] | <code>number</code> | <code>8</code> | set bitdepth to 8, 10 or 12 bit |
|
||||
| [options.tune] | <code>string</code> | <code>"'ssim'"</code> | tune output for a quality metric, one of 'ssim' (default), 'psnr' or 'iq' |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
@@ -753,7 +817,7 @@ This feature is experimental, please do not use in production systems.
|
||||
|
||||
Requires libvips compiled with support for libjxl.
|
||||
The prebuilt binaries do not include this - see
|
||||
[installing a custom libvips](https://sharp.pixelplumbing.com/install#custom-libvips).
|
||||
[installing a custom libvips](/install/#custom-libvips).
|
||||
|
||||
|
||||
**Throws**:
|
||||
|
||||
@@ -284,6 +284,7 @@ The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` pro
|
||||
| [options.background] | <code>string</code> \| <code>Object</code> | <code>"'top-left pixel'"</code> | Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel. |
|
||||
| [options.threshold] | <code>number</code> | <code>10</code> | Allowed difference from the above colour, a positive number. |
|
||||
| [options.lineArt] | <code>boolean</code> | <code>false</code> | Does the input more closely resemble line art (e.g. vector) rather than being photographic? |
|
||||
| [options.margin] | <code>number</code> | <code>0</code> | Leave a margin around trimmed content, value is in pixels. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
@@ -320,4 +321,13 @@ const output = await sharp(input)
|
||||
threshold: 42,
|
||||
})
|
||||
.toBuffer();
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
// Trim image leaving (up to) a 10 pixel margin around the trimmed content.
|
||||
const output = await sharp(input)
|
||||
.trim({
|
||||
margin: 10
|
||||
})
|
||||
.toBuffer();
|
||||
```
|
||||
@@ -114,7 +114,7 @@ e.g. libaom manages its own 4 threads when encoding AVIF images,
|
||||
and these are independent of the value set here.
|
||||
|
||||
:::note
|
||||
Further [control over performance](/performance) is available.
|
||||
Further [control over performance](/performance/) is available.
|
||||
:::
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
---
|
||||
title: v0.34.4 - TBD
|
||||
title: v0.34.4 - 17th September 2025
|
||||
slug: changelog/v0.34.4
|
||||
---
|
||||
|
||||
* Upgrade to libvips v8.17.2 for upstream bug fixes.
|
||||
|
||||
* Ensure TIFF `subifd` and OpenSlide `level` input options are respected (regression in 0.34.3).
|
||||
|
||||
* Ensure `autoOrient` occurs before non-90 angle rotation.
|
||||
[#4425](https://github.com/lovell/sharp/issues/4425)
|
||||
|
||||
* Ensure `autoOrient` removes existing metadata after shrink-on-load.
|
||||
[#4431](https://github.com/lovell/sharp/issues/4431)
|
||||
|
||||
* TypeScript: Ensure `KernelEnum` includes `linear`.
|
||||
[#4441](https://github.com/lovell/sharp/pull/4441)
|
||||
[@BayanBennett](https://github.com/BayanBennett)
|
||||
|
||||
* Ensure `unlimited` flag is passed upstream when reading TIFF images.
|
||||
[#4446](https://github.com/lovell/sharp/issues/4446)
|
||||
|
||||
* Support Electron memory cage when reading XMP metadata (regression in 0.34.3).
|
||||
[#4451](https://github.com/lovell/sharp/issues/4451)
|
||||
|
||||
* Add sharp-libvips rpath for yarn v5 support.
|
||||
[#4452](https://github.com/lovell/sharp/pull/4452)
|
||||
[@arcanis](https://github.com/arcanis)
|
||||
|
||||
21
docs/src/content/docs/changelog/v0.34.5.md
Normal file
21
docs/src/content/docs/changelog/v0.34.5.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: v0.34.5 - 6th November 2025
|
||||
slug: changelog/v0.34.5
|
||||
---
|
||||
|
||||
* Upgrade to libvips v8.17.3 for upstream bug fixes.
|
||||
|
||||
* Add experimental support for prebuilt Linux RISC-V 64-bit binaries.
|
||||
|
||||
* Support building from source with npm v12+, deprecate `--build-from-source` flag.
|
||||
[#4458](https://github.com/lovell/sharp/issues/4458)
|
||||
|
||||
* Add support for BigTIFF output.
|
||||
[#4459](https://github.com/lovell/sharp/pull/4459)
|
||||
[@throwbi](https://github.com/throwbi)
|
||||
|
||||
* Improve error messaging when only warnings issued.
|
||||
[#4465](https://github.com/lovell/sharp/issues/4465)
|
||||
|
||||
* Simplify ICC processing when retaining input profiles.
|
||||
[#4468](https://github.com/lovell/sharp/issues/4468)
|
||||
46
docs/src/content/docs/changelog/v0.35.0.md
Normal file
46
docs/src/content/docs/changelog/v0.35.0.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: v0.35.0 - TBC
|
||||
slug: changelog/v0.35.0
|
||||
---
|
||||
|
||||
* Breaking: Drop support for Node.js 18, now requires Node.js >= 20.9.0.
|
||||
|
||||
* Breaking: Remove `install` script from `package.json` file.
|
||||
Compiling from source is now opt-in via the `build` script.
|
||||
|
||||
* Breaking: AVIF output is now tuned using SSIMULACRA2-based `iq` quality metrics rather than `ssim`.
|
||||
|
||||
* Breaking: Remove deprecated `failOnError` constructor property.
|
||||
|
||||
* Breaking: Remove deprecated `paletteBitDepth` from `metadata` response.
|
||||
|
||||
* Breaking: Remove deprecated properties from `sharpen` operation.
|
||||
|
||||
* Breaking: Rename `format.jp2k` as `format.jp2` for API consistency.
|
||||
|
||||
* Upgrade to libvips v8.18.0 for upstream bug fixes.
|
||||
|
||||
* Ensure TIFF output `bitdepth` option is limited to 1, 2 or 4.
|
||||
|
||||
* Deprecate Windows 32-bit (win32-ia32) prebuilt binaries.
|
||||
|
||||
* Add AVIF/HEIF `tune` option for control over quality metrics.
|
||||
[#4227](https://github.com/lovell/sharp/issues/4227)
|
||||
|
||||
* Add `withGainMap` to process HDR JPEG images with embedded gain maps.
|
||||
[#4314](https://github.com/lovell/sharp/issues/4314)
|
||||
|
||||
* Add `toUint8Array` for output image as a `TypedArray` backed by a transferable `ArrayBuffer`.
|
||||
[#4355](https://github.com/lovell/sharp/issues/4355)
|
||||
|
||||
* TypeScript: Ensure `FormatEnum` keys match reality.
|
||||
[#4475](https://github.com/lovell/sharp/issues/4475)
|
||||
|
||||
* Add `margin` option to `trim` operation.
|
||||
[#4480](https://github.com/lovell/sharp/issues/4480)
|
||||
[@eddienubes](https://github.com/eddienubes)
|
||||
|
||||
* Ensure HEIF primary item is used as default page/frame.
|
||||
[#4487](https://github.com/lovell/sharp/issues/4487)
|
||||
|
||||
* Add WebP `exact` option for control over transparent pixel colour values.
|
||||
@@ -10,7 +10,7 @@ smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions
|
||||
|
||||
It can be used with all JavaScript runtimes
|
||||
that provide support for Node-API v9, including
|
||||
Node.js >= 18.17.0, Deno and Bun.
|
||||
Node.js >= 20.9.0, Deno and Bun.
|
||||
|
||||
Resizing an image is typically 4x-5x faster than using the
|
||||
quickest ImageMagick and GraphicsMagick settings
|
||||
@@ -25,7 +25,7 @@ rotation, extraction, compositing and gamma correction are available.
|
||||
Most modern macOS, Windows and Linux systems
|
||||
do not require any additional install or runtime dependencies.
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
npm install sharp
|
||||
```
|
||||
|
||||
|
||||
@@ -12,33 +12,30 @@ If a package manager lockfile must support multiple platforms,
|
||||
please see the [cross-platform](#cross-platform) section
|
||||
to help decide which package manager is appropriate.
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
npm install sharp
|
||||
```
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
pnpm add sharp
|
||||
```
|
||||
|
||||
When using `pnpm`, you may need to add `sharp` to
|
||||
[ignoredBuiltDependencies](https://pnpm.io/settings#ignoredbuiltdependencies)
|
||||
to silence warnings.
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
yarn add sharp
|
||||
```
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
bun add sharp
|
||||
```
|
||||
|
||||
```sh
|
||||
deno run --allow-ffi ...
|
||||
```sh frame="none"
|
||||
deno add --quiet npm:sharp
|
||||
deno run --allow-env --allow-ffi --allow-read --allow-sys ...
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Node-API v9 compatible runtime e.g. Node.js ^18.17.0 or >=20.3.0.
|
||||
* Node-API v9 compatible runtime e.g. Node.js >= 20.9.0.
|
||||
|
||||
## Prebuilt binaries
|
||||
|
||||
@@ -48,12 +45,13 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
|
||||
* macOS ARM64
|
||||
* Linux ARM (glibc >= 2.31)
|
||||
* Linux ARM64 (glibc >= 2.26, musl >= 1.2.2)
|
||||
* Linux RISC-V 64-bit (glibc >= 2.41)
|
||||
* Linux ppc64 (glibc >= 2.36)
|
||||
* Linux s390x (glibc >= 2.36)
|
||||
* Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
|
||||
* Windows x64
|
||||
* Windows x86
|
||||
* Windows ARM64 (experimental, CPU with ARMv8.4 required for all features)
|
||||
* Windows x86 (deprecated, Node.js 20 only)
|
||||
* Windows ARM64 (CPU with ARMv8.4 required for all features)
|
||||
|
||||
This provides support for the
|
||||
JPEG, PNG, WebP, AVIF (limited to 8-bit depth), TIFF, GIF and SVG (input) image formats.
|
||||
@@ -75,7 +73,7 @@ npm `package-lock.json` files shared by multiple platforms can cause installatio
|
||||
Provides limited support via `--os`, `--cpu` and `--libc` flags.
|
||||
|
||||
To support macOS with Intel x64 and ARM64 CPUs:
|
||||
```sh
|
||||
```sh frame="none"
|
||||
npm install --cpu=x64 --os=darwin sharp
|
||||
npm install --cpu=arm64 --os=darwin sharp
|
||||
```
|
||||
@@ -83,7 +81,7 @@ npm install --cpu=arm64 --os=darwin sharp
|
||||
When the cross-target is Linux, the C standard library must be specified.
|
||||
|
||||
To support glibc (e.g. Debian) and musl (e.g. Alpine) Linux with Intel x64 CPUs:
|
||||
```sh
|
||||
```sh frame="none"
|
||||
npm install --cpu=x64 --os=linux --libc=glibc sharp
|
||||
npm install --cpu=x64 --os=linux --libc=musl sharp
|
||||
```
|
||||
@@ -110,12 +108,13 @@ and on macOS when running Node.js under Rosetta.
|
||||
|
||||
## Building from source
|
||||
|
||||
This module will be compiled from source at `npm install` time when:
|
||||
```sh frame="none"
|
||||
npm install sharp
|
||||
npm explore sharp -- npm run build
|
||||
```
|
||||
|
||||
* a globally-installed libvips is detected, or
|
||||
* when the `npm install --build-from-source` flag is used.
|
||||
|
||||
The logic to detect a globally-installed libvips can be skipped by setting the
|
||||
The build process will search for a globally-installed libvips.
|
||||
This detection logic can be skipped by setting the
|
||||
`SHARP_IGNORE_GLOBAL_LIBVIPS` (never try to use it) or
|
||||
`SHARP_FORCE_GLOBAL_LIBVIPS` (always try to use it, even when missing or outdated)
|
||||
environment variables.
|
||||
@@ -126,21 +125,12 @@ Building from source requires:
|
||||
* [node-addon-api](https://www.npmjs.com/package/node-addon-api) version 7+
|
||||
* [node-gyp](https://github.com/nodejs/node-gyp#installation) version 9+ and its dependencies
|
||||
|
||||
There is an install-time check for these dependencies.
|
||||
If `node-addon-api` or `node-gyp` cannot be found, try adding them via:
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
npm install --save node-addon-api node-gyp
|
||||
```
|
||||
|
||||
When using `pnpm`, you may need to add `sharp` to
|
||||
[onlyBuiltDependencies](https://pnpm.io/settings#onlybuiltdependencies)
|
||||
to ensure the installation script can be run.
|
||||
|
||||
For cross-compiling, the `--platform`, `--arch` and `--libc` npm flags
|
||||
(or the `npm_config_platform`, `npm_config_arch` and `npm_config_libc` environment variables)
|
||||
can be used to configure the target environment.
|
||||
|
||||
## WebAssembly
|
||||
|
||||
Experimental support is provided for runtime environments that provide
|
||||
@@ -152,20 +142,19 @@ Native text rendering is unsupported.
|
||||
|
||||
[Tile-based output](/api-output#tile) is unsupported.
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
npm install --cpu=wasm32 sharp
|
||||
```
|
||||
|
||||
## FreeBSD
|
||||
|
||||
The `vips` package must be installed before `npm install` is run.
|
||||
The `vips` package must be installed before `npm install` is run,
|
||||
as well as the additional [building from source](#building-from-source) dependencies.
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
pkg install -y pkgconf vips
|
||||
```
|
||||
|
||||
```sh
|
||||
cd /usr/ports/graphics/vips/ && make install clean
|
||||
npm install sharp
|
||||
npm explore sharp -- npm run build
|
||||
```
|
||||
|
||||
## Linux memory allocator
|
||||
@@ -199,6 +188,12 @@ and how to configure it.
|
||||
Some package managers use symbolic links
|
||||
but AWS Lambda does not support these within deployment packages.
|
||||
|
||||
An alternative approach is to use a well-maintained, third-party Lambda Layer:
|
||||
|
||||
- [cbschuld/sharp-aws-lambda-layer](https://github.com/cbschuld/sharp-aws-lambda-layer)
|
||||
- [pH200/sharp-layer](https://github.com/pH200/sharp-layer)
|
||||
- [zoellner/sharp-heic-lambda-layer](https://github.com/zoellner/sharp-heic-lambda-layer)
|
||||
|
||||
To get the best performance select the largest memory available.
|
||||
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
|
||||
|
||||
@@ -213,7 +208,7 @@ Ensure sharp is excluded from bundling via the
|
||||
[externals](https://webpack.js.org/configuration/externals/)
|
||||
configuration.
|
||||
|
||||
```js
|
||||
```js frame="none"
|
||||
externals: {
|
||||
'sharp': 'commonjs sharp'
|
||||
}
|
||||
@@ -225,7 +220,7 @@ Ensure sharp is excluded from bundling via the
|
||||
[external](https://esbuild.github.io/api/#external)
|
||||
configuration.
|
||||
|
||||
```js
|
||||
```js frame="none"
|
||||
buildSync({
|
||||
entryPoints: ['app.js'],
|
||||
bundle: true,
|
||||
@@ -234,14 +229,14 @@ buildSync({
|
||||
})
|
||||
```
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
esbuild app.js --bundle --platform=node --external:sharp
|
||||
```
|
||||
|
||||
For `serverless-esbuild`, ensure platform-specific binaries are installed
|
||||
via the `serverless.yml` configuration.
|
||||
|
||||
```yaml
|
||||
```yaml frame="none"
|
||||
custom:
|
||||
esbuild:
|
||||
external:
|
||||
@@ -259,7 +254,7 @@ Ensure `sharp` is unpacked from the ASAR archive file using the
|
||||
[asarUnpack](https://www.electron.build/app-builder-lib.interface.platformspecificbuildoptions#asarunpack)
|
||||
option.
|
||||
|
||||
```json
|
||||
```json frame="none"
|
||||
{
|
||||
"build": {
|
||||
"asar": true,
|
||||
@@ -277,7 +272,7 @@ Ensure `sharp` is unpacked from the ASAR archive file using the
|
||||
[unpack](https://js.electronforge.io/interfaces/_electron_forge_maker_squirrel.InternalOptions.Options.html#asar)
|
||||
option.
|
||||
|
||||
```json
|
||||
```json frame="none"
|
||||
{
|
||||
"packagerConfig": {
|
||||
"asar": {
|
||||
@@ -297,7 +292,7 @@ Ensure `sharp` is excluded from bundling via the
|
||||
[build.rollupOptions](https://vitejs.dev/config/build-options.html)
|
||||
configuration.
|
||||
|
||||
```js
|
||||
```js frame="none"
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
|
||||
@@ -13,11 +13,12 @@ environment variable, which defaults to 4.
|
||||
When using more than 4 physical CPU cores, set this environment variable
|
||||
before the Node.js process starts to increase the thread pool size.
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
export UV_THREADPOOL_SIZE="$(lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l)"
|
||||
```
|
||||
|
||||
libvips uses a glib-managed thread pool to avoid the overhead of spawning new threads.
|
||||
libvips uses a shared thread pool to avoid the overhead of spawning new threads.
|
||||
The size of this thread pool will grow on demand and shrink when idle.
|
||||
|
||||
The default number of threads used to concurrently process each image is the same as the number of CPU cores,
|
||||
except when using glibc-based Linux without jemalloc, where the default is `1` to help reduce memory fragmentation.
|
||||
@@ -25,10 +26,10 @@ except when using glibc-based Linux without jemalloc, where the default is `1` t
|
||||
Use [`sharp.concurrency()`](/api-utility/#concurrency) to manage the number of threads per image.
|
||||
|
||||
To reduce memory fragmentation when using the default Linux glibc memory allocator, set the
|
||||
[`MALLOC_ARENA_MAX`](https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html)
|
||||
[`MALLOC_ARENA_MAX`](https://sourceware.org/glibc/manual/latest/html_node/Memory-Allocation-Tunables.html)
|
||||
environment variable before the Node.js process starts to reduce the number of memory pools.
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
export MALLOC_ARENA_MAX="2"
|
||||
```
|
||||
|
||||
@@ -130,7 +131,7 @@ Note: jimp does not support premultiply/unpremultiply.
|
||||
|
||||
Requires Docker.
|
||||
|
||||
```sh
|
||||
```sh frame="none"
|
||||
git clone https://github.com/lovell/sharp.git
|
||||
cd sharp/test/bench
|
||||
./run-with-docker.sh
|
||||
|
||||
38
install/build.js
Normal file
38
install/build.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const {
|
||||
useGlobalLibvips,
|
||||
globalLibvipsVersion,
|
||||
log,
|
||||
spawnRebuild,
|
||||
} = require('../lib/libvips');
|
||||
|
||||
log('Building from source');
|
||||
log('See https://sharp.pixelplumbing.com/install#building-from-source');
|
||||
|
||||
try {
|
||||
const addonApi = require('node-addon-api');
|
||||
log(`Found node-addon-api ${addonApi.version || ''}`);
|
||||
} catch (_err) {
|
||||
log('Please add node-addon-api to your dependencies');
|
||||
process.exit(1);
|
||||
}
|
||||
try {
|
||||
const gyp = require('node-gyp');
|
||||
log(`Found node-gyp ${gyp().version}`);
|
||||
} catch (_err) {
|
||||
log('Please add node-gyp to your dependencies');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (useGlobalLibvips(log)) {
|
||||
log(`Found globally-installed libvips v${globalLibvipsVersion()}`);
|
||||
}
|
||||
|
||||
const status = spawnRebuild();
|
||||
if (status !== 0) {
|
||||
process.exit(status);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
const { useGlobalLibvips, globalLibvipsVersion, log, spawnRebuild } = require('../lib/libvips');
|
||||
|
||||
const buildFromSource = (msg) => {
|
||||
log(msg);
|
||||
log('Attempting to build from source via node-gyp');
|
||||
try {
|
||||
const addonApi = require('node-addon-api');
|
||||
log(`Found node-addon-api ${addonApi.version || ''}`);
|
||||
} catch (err) {
|
||||
log('Please add node-addon-api to your dependencies');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const gyp = require('node-gyp');
|
||||
log(`Found node-gyp ${gyp().version}`);
|
||||
} catch (err) {
|
||||
log('Please add node-gyp to your dependencies');
|
||||
return;
|
||||
}
|
||||
log('See https://sharp.pixelplumbing.com/install#building-from-source');
|
||||
const status = spawnRebuild();
|
||||
if (status !== 0) {
|
||||
process.exit(status);
|
||||
}
|
||||
};
|
||||
|
||||
if (useGlobalLibvips(log)) {
|
||||
buildFromSource(`Detected globally-installed libvips v${globalLibvipsVersion()}`);
|
||||
} else if (process.env.npm_config_build_from_source) {
|
||||
buildFromSource('Detected --build-from-source flag');
|
||||
}
|
||||
} catch (err) {
|
||||
const summary = err.message.split(/\n/).slice(0, 1);
|
||||
console.log(`sharp: skipping install check: ${summary}`);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const is = require('./is');
|
||||
|
||||
@@ -18,7 +18,7 @@ const bool = {
|
||||
/**
|
||||
* Remove alpha channels, if any. This is a no-op if the image does not have an alpha channel.
|
||||
*
|
||||
* See also {@link /api-operation#flatten|flatten}.
|
||||
* See also {@link /api-operation/#flatten flatten}.
|
||||
*
|
||||
* @example
|
||||
* sharp('rgba.png')
|
||||
@@ -74,6 +74,8 @@ function ensureAlpha (alpha) {
|
||||
/**
|
||||
* Extract a single channel from a multi-channel image.
|
||||
*
|
||||
* The output colourspace will be either `b-w` (8-bit) or `grey16` (16-bit).
|
||||
*
|
||||
* @example
|
||||
* // green.jpg is a greyscale image containing the green channel of the input
|
||||
* await sharp(input)
|
||||
@@ -161,7 +163,7 @@ function bandbool (boolOp) {
|
||||
* @module Sharp
|
||||
* @private
|
||||
*/
|
||||
module.exports = function (Sharp) {
|
||||
module.exports = (Sharp) => {
|
||||
Object.assign(Sharp.prototype, {
|
||||
// Public instance functions
|
||||
removeAlpha,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const color = require('color');
|
||||
const color = require('@img/colour');
|
||||
const is = require('./is');
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ function grayscale (grayscale) {
|
||||
*
|
||||
* The input image will be converted to the provided colourspace at the start of the pipeline.
|
||||
* All operations will use this colourspace before converting to the output colourspace,
|
||||
* as defined by {@link #tocolourspace|toColourspace}.
|
||||
* as defined by {@link #tocolourspace toColourspace}.
|
||||
*
|
||||
* @since 0.29.0
|
||||
*
|
||||
@@ -80,7 +80,7 @@ function grayscale (grayscale) {
|
||||
* .toColourspace('srgb')
|
||||
* .toFile('16bpc-pipeline-to-8bpc-output.png')
|
||||
*
|
||||
* @param {string} [colourspace] - pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://github.com/libvips/libvips/blob/41cff4e9d0838498487a00623462204eb10ee5b8/libvips/iofuncs/enumtypes.c#L774)
|
||||
* @param {string} [colourspace] - pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
@@ -112,7 +112,7 @@ function pipelineColorspace (colorspace) {
|
||||
* .toColourspace('rgb16')
|
||||
* .toFile('16-bpp.png')
|
||||
*
|
||||
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/3c0bfdf74ce1dc37a6429bed47fa76f16e2cd70a/libvips/iofuncs/enumtypes.c#L777-L794)
|
||||
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
@@ -141,7 +141,10 @@ function toColorspace (colorspace) {
|
||||
* @throws {Error} Invalid value
|
||||
*/
|
||||
function _getBackgroundColourOption (value) {
|
||||
if (is.object(value) || is.string(value)) {
|
||||
if (
|
||||
is.object(value) ||
|
||||
(is.string(value) && value.length >= 3 && value.length <= 200)
|
||||
) {
|
||||
const colour = color(value);
|
||||
return [
|
||||
colour.red(),
|
||||
@@ -172,7 +175,7 @@ function _setBackgroundColourOption (key, value) {
|
||||
* @module Sharp
|
||||
* @private
|
||||
*/
|
||||
module.exports = function (Sharp) {
|
||||
module.exports = (Sharp) => {
|
||||
Object.assign(Sharp.prototype, {
|
||||
// Public
|
||||
tint,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const is = require('./is');
|
||||
|
||||
@@ -56,7 +56,7 @@ const blend = {
|
||||
* `hard-light`, `soft-light`, `difference`, `exclusion`.
|
||||
*
|
||||
* More information about blend modes can be found at
|
||||
* https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
|
||||
* https://www.libvips.org/API/current/enum.BlendMode.html
|
||||
* and https://www.cairographics.org/operators/
|
||||
*
|
||||
* @since 0.22.0
|
||||
@@ -123,8 +123,8 @@ const blend = {
|
||||
* @param {Number} [images[].raw.height]
|
||||
* @param {Number} [images[].raw.channels]
|
||||
* @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
|
||||
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor#parameters|constructor parameters}
|
||||
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
|
||||
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor/ constructor parameters}
|
||||
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor/ constructor parameters}
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
@@ -206,7 +206,7 @@ function composite (images) {
|
||||
* @module Sharp
|
||||
* @private
|
||||
*/
|
||||
module.exports = function (Sharp) {
|
||||
module.exports = (Sharp) => {
|
||||
Sharp.prototype.composite = composite;
|
||||
Sharp.blend = blend;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const util = require('node:util');
|
||||
const stream = require('node:stream');
|
||||
@@ -12,6 +12,10 @@ require('./sharp');
|
||||
// Use NODE_DEBUG=sharp to enable libvips warnings
|
||||
const debuglog = util.debuglog('sharp');
|
||||
|
||||
const queueListener = (queueLength) => {
|
||||
Sharp.queue.emit('change', queueLength);
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
|
||||
*
|
||||
@@ -205,6 +209,7 @@ const debuglog = util.debuglog('sharp');
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
const Sharp = function (input, options) {
|
||||
// biome-ignore lint/complexity/noArguments: constructor factory
|
||||
if (arguments.length === 1 && !is.defined(input)) {
|
||||
throw new Error('Invalid input');
|
||||
}
|
||||
@@ -273,6 +278,7 @@ const Sharp = function (input, options) {
|
||||
trimBackground: [],
|
||||
trimThreshold: -1,
|
||||
trimLineArt: false,
|
||||
trimMargin: 0,
|
||||
dilateWidth: 0,
|
||||
erodeWidth: 0,
|
||||
gamma: 0,
|
||||
@@ -301,6 +307,7 @@ const Sharp = function (input, options) {
|
||||
fileOut: '',
|
||||
formatOut: 'input',
|
||||
streamOut: false,
|
||||
typedArrayOut: false,
|
||||
keepMetadata: 0,
|
||||
withMetadataOrientation: -1,
|
||||
withMetadataDensity: 0,
|
||||
@@ -308,6 +315,7 @@ const Sharp = function (input, options) {
|
||||
withExif: {},
|
||||
withExifMerge: true,
|
||||
withXmp: '',
|
||||
withGainMap: false,
|
||||
resolveWithObject: false,
|
||||
loop: -1,
|
||||
delay: [],
|
||||
@@ -343,6 +351,7 @@ const Sharp = function (input, options) {
|
||||
webpEffort: 4,
|
||||
webpMinSize: false,
|
||||
webpMixed: false,
|
||||
webpExact: false,
|
||||
gifBitdepth: 8,
|
||||
gifEffort: 7,
|
||||
gifDither: 1,
|
||||
@@ -353,10 +362,11 @@ const Sharp = function (input, options) {
|
||||
gifProgressive: false,
|
||||
tiffQuality: 80,
|
||||
tiffCompression: 'jpeg',
|
||||
tiffBigtiff: false,
|
||||
tiffPredictor: 'horizontal',
|
||||
tiffPyramid: false,
|
||||
tiffMiniswhite: false,
|
||||
tiffBitdepth: 8,
|
||||
tiffBitdepth: 0,
|
||||
tiffTile: false,
|
||||
tiffTileHeight: 256,
|
||||
tiffTileWidth: 256,
|
||||
@@ -369,6 +379,7 @@ const Sharp = function (input, options) {
|
||||
heifEffort: 4,
|
||||
heifChromaSubsampling: '4:4:4',
|
||||
heifBitdepth: 8,
|
||||
heifTune: 'ssim',
|
||||
jxlDistance: 1,
|
||||
jxlDecodingTier: 0,
|
||||
jxlEffort: 7,
|
||||
@@ -396,9 +407,7 @@ const Sharp = function (input, options) {
|
||||
debuglog(warning);
|
||||
},
|
||||
// Function to notify of queue length changes
|
||||
queueListener: function (queueLength) {
|
||||
Sharp.queue.emit('change', queueLength);
|
||||
}
|
||||
queueListener
|
||||
};
|
||||
this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
|
||||
return this;
|
||||
|
||||
98
lib/index.d.ts
vendored
98
lib/index.d.ts
vendored
@@ -27,7 +27,7 @@
|
||||
|
||||
/// <reference types="node" />
|
||||
|
||||
import { Duplex } from 'stream';
|
||||
import type { Duplex } from 'node:stream';
|
||||
|
||||
//#region Constructor functions
|
||||
|
||||
@@ -259,7 +259,6 @@ declare namespace sharp {
|
||||
* Set the pipeline colourspace.
|
||||
* The input image will be converted to the provided colourspace at the start of the pipeline.
|
||||
* All operations will use this colourspace before converting to the output colourspace, as defined by toColourspace.
|
||||
* This feature is experimental and has not yet been fully-tested with all operations.
|
||||
*
|
||||
* @param colourspace pipeline colourspace e.g. rgb16, scrgb, lab, grey16 ...
|
||||
* @throws {Error} Invalid parameters
|
||||
@@ -470,21 +469,6 @@ declare namespace sharp {
|
||||
*/
|
||||
sharpen(options?: SharpenOptions): Sharp;
|
||||
|
||||
/**
|
||||
* Sharpen the image.
|
||||
* When used without parameters, performs a fast, mild sharpen of the output image.
|
||||
* When a sigma is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
|
||||
* Fine-grained control over the level of sharpening in "flat" (m1) and "jagged" (m2) areas is available.
|
||||
* @param sigma the sigma of the Gaussian mask, where sigma = 1 + radius / 2.
|
||||
* @param flat the level of sharpening to apply to "flat" areas. (optional, default 1.0)
|
||||
* @param jagged the level of sharpening to apply to "jagged" areas. (optional, default 2.0)
|
||||
* @throws {Error} Invalid parameters
|
||||
* @returns A sharp instance that can be used to chain operations
|
||||
*
|
||||
* @deprecated Use the object parameter `sharpen({sigma, m1, m2, x1, y2, y3})` instead
|
||||
*/
|
||||
sharpen(sigma?: number, flat?: number, jagged?: number): Sharp;
|
||||
|
||||
/**
|
||||
* Apply median filter. When used without parameters the default window is 3x3.
|
||||
* @param size square mask size: size x size (optional, default 3)
|
||||
@@ -693,6 +677,13 @@ declare namespace sharp {
|
||||
*/
|
||||
toBuffer(options: { resolveWithObject: true }): Promise<{ data: Buffer; info: OutputInfo }>;
|
||||
|
||||
/**
|
||||
* Write output to a Uint8Array backed by a transferable ArrayBuffer. JPEG, PNG, WebP, AVIF, TIFF, GIF and RAW output are supported.
|
||||
* By default, the format will match the input image, except SVG input which becomes PNG output.
|
||||
* @returns A promise that resolves with an object containing the Uint8Array data and an info object containing the output image format, size (bytes), width, height and channels
|
||||
*/
|
||||
toUint8Array(): Promise<{ data: Uint8Array; info: OutputInfo }>;
|
||||
|
||||
/**
|
||||
* Keep all EXIF metadata from the input image in the output image.
|
||||
* EXIF metadata is unsupported for TIFF output.
|
||||
@@ -849,7 +840,7 @@ declare namespace sharp {
|
||||
* @returns A sharp instance that can be used to chain operations
|
||||
*/
|
||||
toFormat(
|
||||
format: keyof FormatEnum | AvailableFormatInfo,
|
||||
format: keyof FormatEnum | AvailableFormatInfo | "avif",
|
||||
options?:
|
||||
| OutputOptions
|
||||
| JpegOptions
|
||||
@@ -860,6 +851,7 @@ declare namespace sharp {
|
||||
| JxlOptions
|
||||
| GifOptions
|
||||
| Jp2Options
|
||||
| RawOptions
|
||||
| TiffOptions,
|
||||
): Sharp;
|
||||
|
||||
@@ -902,7 +894,7 @@ declare namespace sharp {
|
||||
* - sharp.gravity: north, northeast, east, southeast, south, southwest, west, northwest, center or centre.
|
||||
* - sharp.strategy: cover only, dynamically crop using either the entropy or attention strategy. Some of these values are based on the object-position CSS property.
|
||||
*
|
||||
* The experimental strategy-based approach resizes so one dimension is at its target length then repeatedly ranks edge regions,
|
||||
* The strategy-based approach resizes so one dimension is at its target length then repeatedly ranks edge regions,
|
||||
* discarding the edge with the lowest score based on the selected strategy.
|
||||
* - entropy: focus on the region with the highest Shannon entropy.
|
||||
* - attention: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
|
||||
@@ -991,14 +983,6 @@ declare namespace sharp {
|
||||
* 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. (optional, default 'warning')
|
||||
*/
|
||||
failOn?: FailOnOptions | undefined;
|
||||
/**
|
||||
* By default halt processing and raise an error when loading invalid images.
|
||||
* Set this flag to false if you'd rather apply a "best effort" to decode images,
|
||||
* even if the data is corrupt or invalid. (optional, default true)
|
||||
*
|
||||
* @deprecated Use `failOn` instead
|
||||
*/
|
||||
failOnError?: boolean | undefined;
|
||||
/**
|
||||
* Do not process input images where the number of pixels (width x height) exceeds this limit.
|
||||
* Assumes image dimensions contained in the input metadata can be trusted.
|
||||
@@ -1027,11 +1011,11 @@ declare namespace sharp {
|
||||
openSlide?: OpenSlideInputOptions | undefined;
|
||||
/** JPEG 2000 specific input options */
|
||||
jp2?: Jp2InputOptions | undefined;
|
||||
/** Deprecated: use tiff.subifd instead */
|
||||
/** @deprecated Use {@link SharpOptions.tiff} instead */
|
||||
subifd?: number | undefined;
|
||||
/** Deprecated: use pdf.background instead */
|
||||
/** @deprecated Use {@link SharpOptions.pdf} instead */
|
||||
pdfBackground?: Colour | Color | undefined;
|
||||
/** Deprecated: use openSlide.level instead */
|
||||
/** @deprecated Use {@link SharpOptions.openSlide} instead */
|
||||
level?: number | undefined;
|
||||
/** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */
|
||||
animated?: boolean | undefined;
|
||||
@@ -1181,6 +1165,12 @@ declare namespace sharp {
|
||||
'IFD3'?: ExifDir;
|
||||
}
|
||||
|
||||
type HeifCompression = 'av1' | 'hevc';
|
||||
|
||||
type HeifTune = 'iq' | 'ssim' | 'psnr';
|
||||
|
||||
type Unit = 'inch' | 'cm';
|
||||
|
||||
interface WriteableMetadata {
|
||||
/** Number of pixels per inch (DPI) */
|
||||
density?: number | undefined;
|
||||
@@ -1259,7 +1249,7 @@ declare namespace sharp {
|
||||
/** Buffer containing raw TIFFTAG_PHOTOSHOP data, if present */
|
||||
tifftagPhotoshop?: Buffer | undefined;
|
||||
/** The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC) */
|
||||
compression?: 'av1' | 'hevc';
|
||||
compression?: HeifCompression | undefined;
|
||||
/** Default background colour, if present, for PNG (bKGD) and GIF images */
|
||||
background?: { r: number; g: number; b: number } | { gray: number };
|
||||
/** Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide */
|
||||
@@ -1267,11 +1257,13 @@ declare namespace sharp {
|
||||
/** Number of Sub Image File Directories in an OME-TIFF image */
|
||||
subifds?: number | undefined;
|
||||
/** The unit of resolution (density) */
|
||||
resolutionUnit?: 'inch' | 'cm' | undefined;
|
||||
resolutionUnit?: Unit | undefined;
|
||||
/** String containing format for images loaded via *magick */
|
||||
formatMagick?: string | undefined;
|
||||
/** Array of keyword/text pairs representing PNG text blocks, if present. */
|
||||
comments?: CommentsMetadata[] | undefined;
|
||||
/** HDR gain map, if present */
|
||||
gainMap?: GainMapMetadata | undefined;
|
||||
}
|
||||
|
||||
interface LevelMetadata {
|
||||
@@ -1284,16 +1276,21 @@ declare namespace sharp {
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface GainMapMetadata {
|
||||
/** JPEG image */
|
||||
image: Buffer;
|
||||
}
|
||||
|
||||
interface Stats {
|
||||
/** Array of channel statistics for each channel in the image. */
|
||||
channels: ChannelStats[];
|
||||
/** Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel */
|
||||
isOpaque: boolean;
|
||||
/** Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental) */
|
||||
/** Histogram-based estimation of greyscale entropy, discarding alpha channel if any */
|
||||
entropy: number;
|
||||
/** Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental) */
|
||||
/** Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any */
|
||||
sharpness: number;
|
||||
/** Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental) */
|
||||
/** Object containing most dominant sRGB colour based on a 4096-bin 3D histogram */
|
||||
dominant: { r: number; g: number; b: number };
|
||||
}
|
||||
|
||||
@@ -1399,11 +1396,13 @@ declare namespace sharp {
|
||||
/** Level of CPU effort to reduce file size, integer 0-6 (optional, default 4) */
|
||||
effort?: number | undefined;
|
||||
/** Prevent use of animation key frames to minimise file size (slow) (optional, default false) */
|
||||
minSize?: boolean;
|
||||
minSize?: boolean | undefined;
|
||||
/** Allow mixture of lossy and lossless animation frames (slow) (optional, default false) */
|
||||
mixed?: boolean;
|
||||
mixed?: boolean | undefined;
|
||||
/** Preset options: one of default, photo, picture, drawing, icon, text (optional, default 'default') */
|
||||
preset?: keyof PresetEnum | undefined;
|
||||
/** Preserve the colour data in transparent pixels (optional, default false) */
|
||||
exact?: boolean | undefined;
|
||||
}
|
||||
|
||||
interface AvifOptions extends OutputOptions {
|
||||
@@ -1417,13 +1416,15 @@ declare namespace sharp {
|
||||
chromaSubsampling?: string | undefined;
|
||||
/** Set bitdepth to 8, 10 or 12 bit (optional, default 8) */
|
||||
bitdepth?: 8 | 10 | 12 | undefined;
|
||||
/** Tune output for a quality metric, one of 'iq', 'ssim' or 'psnr' (optional, default 'iq') */
|
||||
tune?: HeifTune | undefined;
|
||||
}
|
||||
|
||||
interface HeifOptions extends OutputOptions {
|
||||
/** quality, integer 1-100 (optional, default 50) */
|
||||
quality?: number | undefined;
|
||||
/** compression format: av1, hevc (optional, default 'av1') */
|
||||
compression?: 'av1' | 'hevc' | undefined;
|
||||
compression?: HeifCompression | undefined;
|
||||
/** use lossless compression (optional, default false) */
|
||||
lossless?: boolean | undefined;
|
||||
/** Level of CPU effort to reduce file size, between 0 (fastest) and 9 (slowest) (optional, default 4) */
|
||||
@@ -1432,6 +1433,8 @@ declare namespace sharp {
|
||||
chromaSubsampling?: string | undefined;
|
||||
/** Set bitdepth to 8, 10 or 12 bit (optional, default 8) */
|
||||
bitdepth?: 8 | 10 | 12 | undefined;
|
||||
/** Tune output for a quality metric, one of 'ssim', 'psnr' or 'iq' (optional, default 'ssim') */
|
||||
tune?: HeifTune | undefined;
|
||||
}
|
||||
|
||||
interface GifOptions extends OutputOptions, AnimationOptions {
|
||||
@@ -1460,6 +1463,8 @@ declare namespace sharp {
|
||||
quality?: number | undefined;
|
||||
/** Compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k (optional, default 'jpeg') */
|
||||
compression?: string | undefined;
|
||||
/** Use BigTIFF variant (has no effect when compression is none) (optional, default false) */
|
||||
bigtiff?: boolean | undefined;
|
||||
/** Compression predictor options: none, horizontal, float (optional, default 'horizontal') */
|
||||
predictor?: string | undefined;
|
||||
/** Write an image pyramid (optional, default false) */
|
||||
@@ -1474,12 +1479,12 @@ declare namespace sharp {
|
||||
xres?: number | undefined;
|
||||
/** Vertical resolution in pixels/mm (optional, default 1.0) */
|
||||
yres?: number | undefined;
|
||||
/** Reduce bitdepth to 1, 2 or 4 bit (optional, default 8) */
|
||||
bitdepth?: 1 | 2 | 4 | 8 | undefined;
|
||||
/** Reduce bitdepth to 1, 2 or 4 bit (optional) */
|
||||
bitdepth?: 1 | 2 | 4 | undefined;
|
||||
/** Write 1-bit images as miniswhite (optional, default false) */
|
||||
miniswhite?: boolean | undefined;
|
||||
/** Resolution unit options: inch, cm (optional, default 'inch') */
|
||||
resolutionUnit?: 'inch' | 'cm' | undefined;
|
||||
resolutionUnit?: Unit | undefined;
|
||||
}
|
||||
|
||||
interface PngOptions extends OutputOptions {
|
||||
@@ -1601,10 +1606,12 @@ declare namespace sharp {
|
||||
threshold?: number | undefined;
|
||||
/** Does the input more closely resemble line art (e.g. vector) rather than being photographic? (optional, default false) */
|
||||
lineArt?: boolean | undefined;
|
||||
/** Leave a margin around trimmed content, value is in pixels. (optional, default 0) */
|
||||
margin?: number | undefined;
|
||||
}
|
||||
|
||||
interface RawOptions {
|
||||
depth?: 'char' | 'uchar' | 'short' | 'ushort' | 'int' | 'uint' | 'float' | 'complex' | 'double' | 'dpcomplex';
|
||||
depth?: keyof DepthEnum;
|
||||
}
|
||||
|
||||
/** 1 for grayscale, 2 for grayscale + alpha, 3 for sRGB, 4 for CMYK or RGBA */
|
||||
@@ -1784,6 +1791,7 @@ declare namespace sharp {
|
||||
interface KernelEnum {
|
||||
nearest: 'nearest';
|
||||
cubic: 'cubic';
|
||||
linear: 'linear';
|
||||
mitchell: 'mitchell';
|
||||
lanczos2: 'lanczos2';
|
||||
lanczos3: 'lanczos3';
|
||||
@@ -1905,16 +1913,13 @@ declare namespace sharp {
|
||||
}
|
||||
|
||||
interface FormatEnum {
|
||||
avif: AvailableFormatInfo;
|
||||
dcraw: AvailableFormatInfo;
|
||||
dz: AvailableFormatInfo;
|
||||
exr: AvailableFormatInfo;
|
||||
fits: AvailableFormatInfo;
|
||||
gif: AvailableFormatInfo;
|
||||
heif: AvailableFormatInfo;
|
||||
input: AvailableFormatInfo;
|
||||
jpeg: AvailableFormatInfo;
|
||||
jpg: AvailableFormatInfo;
|
||||
jp2: AvailableFormatInfo;
|
||||
jxl: AvailableFormatInfo;
|
||||
magick: AvailableFormatInfo;
|
||||
@@ -1926,8 +1931,7 @@ declare namespace sharp {
|
||||
raw: AvailableFormatInfo;
|
||||
svg: AvailableFormatInfo;
|
||||
tiff: AvailableFormatInfo;
|
||||
tif: AvailableFormatInfo;
|
||||
v: AvailableFormatInfo;
|
||||
vips: AvailableFormatInfo;
|
||||
webp: AvailableFormatInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const Sharp = require('./constructor');
|
||||
require('./input')(Sharp);
|
||||
|
||||
39
lib/input.js
39
lib/input.js
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const is = require('./is');
|
||||
const sharp = require('./sharp');
|
||||
@@ -30,7 +30,7 @@ const inputStreamParameters = [
|
||||
// Format-specific
|
||||
'jp2', 'openSlide', 'pdf', 'raw', 'svg', 'tiff',
|
||||
// Deprecated
|
||||
'failOnError', 'openSlideLevel', 'pdfBackground', 'tiffSubifd'
|
||||
'openSlideLevel', 'pdfBackground', 'tiffSubifd'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
const inputDescriptor = {
|
||||
autoOrient: false,
|
||||
failOn: 'warning',
|
||||
limitInputPixels: Math.pow(0x3FFF, 2),
|
||||
limitInputPixels: 0x3FFF ** 2,
|
||||
ignoreIcc: false,
|
||||
unlimited: false,
|
||||
sequentialRead: true
|
||||
@@ -106,14 +106,6 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
}`);
|
||||
}
|
||||
if (is.object(inputOptions)) {
|
||||
// Deprecated: failOnError
|
||||
if (is.defined(inputOptions.failOnError)) {
|
||||
if (is.bool(inputOptions.failOnError)) {
|
||||
inputDescriptor.failOn = inputOptions.failOnError ? 'warning' : 'none';
|
||||
} else {
|
||||
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
|
||||
}
|
||||
}
|
||||
// failOn
|
||||
if (is.defined(inputOptions.failOn)) {
|
||||
if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {
|
||||
@@ -150,7 +142,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
if (is.defined(inputOptions.limitInputPixels)) {
|
||||
if (is.bool(inputOptions.limitInputPixels)) {
|
||||
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels
|
||||
? Math.pow(0x3FFF, 2)
|
||||
? 0x3FFF ** 2
|
||||
: 0;
|
||||
} else if (is.integer(inputOptions.limitInputPixels) && is.inRange(inputOptions.limitInputPixels, 0, Number.MAX_SAFE_INTEGER)) {
|
||||
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
|
||||
@@ -513,7 +505,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
}
|
||||
}
|
||||
} else if (is.defined(inputOptions)) {
|
||||
throw new Error('Invalid input options ' + inputOptions);
|
||||
throw new Error(`Invalid input options ${inputOptions}`);
|
||||
}
|
||||
return inputDescriptor;
|
||||
}
|
||||
@@ -525,10 +517,8 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
* @param {string} encoding - unused
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function _write (chunk, encoding, callback) {
|
||||
/* istanbul ignore else */
|
||||
function _write (chunk, _encoding, callback) {
|
||||
if (Array.isArray(this.options.input.buffer)) {
|
||||
/* istanbul ignore else */
|
||||
if (is.buffer(chunk)) {
|
||||
if (this.options.input.buffer.length === 0) {
|
||||
this.on('finish', () => {
|
||||
@@ -572,17 +562,17 @@ function _isStreamInput () {
|
||||
* such as resize or rotate.
|
||||
*
|
||||
* Dimensions in the response will respect the `page` and `pages` properties of the
|
||||
* {@link /api-constructor#parameters|constructor parameters}.
|
||||
* {@link /api-constructor/ constructor parameters}.
|
||||
*
|
||||
* A `Promise` is returned when `callback` is not provided.
|
||||
*
|
||||
* - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
|
||||
* - `format`: Name of decoder used to parse image e.g. `jpeg`, `png`, `webp`, `gif`, `svg`, `heif`, `tiff`
|
||||
* - `size`: Total size of image in bytes, for Stream and Buffer input only
|
||||
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
|
||||
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
|
||||
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
|
||||
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
|
||||
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
||||
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
|
||||
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/enum.BandFormat.html)
|
||||
* - `density`: Number of pixels per inch (DPI), if present
|
||||
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||
@@ -609,6 +599,7 @@ function _isStreamInput () {
|
||||
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
||||
* - `formatMagick`: String containing format for images loaded via *magick
|
||||
* - `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
|
||||
* - `gainMap.image`: HDR gain map, if present, as compressed JPEG image.
|
||||
*
|
||||
* @example
|
||||
* const metadata = await sharp(input).metadata();
|
||||
@@ -794,7 +785,7 @@ function stats (callback) {
|
||||
* @module Sharp
|
||||
* @private
|
||||
*/
|
||||
module.exports = function (Sharp) {
|
||||
module.exports = (Sharp) => {
|
||||
Object.assign(Sharp.prototype, {
|
||||
// Private
|
||||
_inputOptionsFromObject,
|
||||
|
||||
64
lib/is.js
64
lib/is.js
@@ -1,61 +1,49 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Is this value defined and not null?
|
||||
* @private
|
||||
*/
|
||||
const defined = function (val) {
|
||||
return typeof val !== 'undefined' && val !== null;
|
||||
};
|
||||
const defined = (val) => typeof val !== 'undefined' && val !== null;
|
||||
|
||||
/**
|
||||
* Is this value an object?
|
||||
* @private
|
||||
*/
|
||||
const object = function (val) {
|
||||
return typeof val === 'object';
|
||||
};
|
||||
const object = (val) => typeof val === 'object';
|
||||
|
||||
/**
|
||||
* Is this value a plain object?
|
||||
* @private
|
||||
*/
|
||||
const plainObject = function (val) {
|
||||
return Object.prototype.toString.call(val) === '[object Object]';
|
||||
};
|
||||
const plainObject = (val) => Object.prototype.toString.call(val) === '[object Object]';
|
||||
|
||||
/**
|
||||
* Is this value a function?
|
||||
* @private
|
||||
*/
|
||||
const fn = function (val) {
|
||||
return typeof val === 'function';
|
||||
};
|
||||
const fn = (val) => typeof val === 'function';
|
||||
|
||||
/**
|
||||
* Is this value a boolean?
|
||||
* @private
|
||||
*/
|
||||
const bool = function (val) {
|
||||
return typeof val === 'boolean';
|
||||
};
|
||||
const bool = (val) => typeof val === 'boolean';
|
||||
|
||||
/**
|
||||
* Is this value a Buffer object?
|
||||
* @private
|
||||
*/
|
||||
const buffer = function (val) {
|
||||
return val instanceof Buffer;
|
||||
};
|
||||
const buffer = (val) => val instanceof Buffer;
|
||||
|
||||
/**
|
||||
* Is this value a typed array object?. E.g. Uint8Array or Uint8ClampedArray?
|
||||
* @private
|
||||
*/
|
||||
const typedArray = function (val) {
|
||||
const typedArray = (val) => {
|
||||
if (defined(val)) {
|
||||
switch (val.constructor) {
|
||||
case Uint8Array:
|
||||
@@ -78,49 +66,37 @@ const typedArray = function (val) {
|
||||
* Is this value an ArrayBuffer object?
|
||||
* @private
|
||||
*/
|
||||
const arrayBuffer = function (val) {
|
||||
return val instanceof ArrayBuffer;
|
||||
};
|
||||
const arrayBuffer = (val) => val instanceof ArrayBuffer;
|
||||
|
||||
/**
|
||||
* Is this value a non-empty string?
|
||||
* @private
|
||||
*/
|
||||
const string = function (val) {
|
||||
return typeof val === 'string' && val.length > 0;
|
||||
};
|
||||
const string = (val) => typeof val === 'string' && val.length > 0;
|
||||
|
||||
/**
|
||||
* Is this value a real number?
|
||||
* @private
|
||||
*/
|
||||
const number = function (val) {
|
||||
return typeof val === 'number' && !Number.isNaN(val);
|
||||
};
|
||||
const number = (val) => typeof val === 'number' && !Number.isNaN(val);
|
||||
|
||||
/**
|
||||
* Is this value an integer?
|
||||
* @private
|
||||
*/
|
||||
const integer = function (val) {
|
||||
return Number.isInteger(val);
|
||||
};
|
||||
const integer = (val) => Number.isInteger(val);
|
||||
|
||||
/**
|
||||
* Is this value within an inclusive given range?
|
||||
* @private
|
||||
*/
|
||||
const inRange = function (val, min, max) {
|
||||
return val >= min && val <= max;
|
||||
};
|
||||
const inRange = (val, min, max) => val >= min && val <= max;
|
||||
|
||||
/**
|
||||
* Is this value within the elements of an array?
|
||||
* @private
|
||||
*/
|
||||
const inArray = function (val, list) {
|
||||
return list.includes(val);
|
||||
};
|
||||
const inArray = (val, list) => list.includes(val);
|
||||
|
||||
/**
|
||||
* Create an Error with a message relating to an invalid parameter.
|
||||
@@ -131,11 +107,9 @@ const inArray = function (val, list) {
|
||||
* @returns {Error} Containing the formatted message.
|
||||
* @private
|
||||
*/
|
||||
const invalidParameterError = function (name, expected, actual) {
|
||||
return new Error(
|
||||
const invalidParameterError = (name, expected, actual) => new Error(
|
||||
`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensures an Error from C++ contains a JS stack.
|
||||
@@ -145,7 +119,7 @@ const invalidParameterError = function (name, expected, actual) {
|
||||
* @returns {Error} Error with message and stack.
|
||||
* @private
|
||||
*/
|
||||
const nativeError = function (native, context) {
|
||||
const nativeError = (native, context) => {
|
||||
context.message = native.message;
|
||||
return context;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const { createHash } = require('node:crypto');
|
||||
@@ -12,13 +12,13 @@ const detectLibc = require('detect-libc');
|
||||
|
||||
const { config, engines, optionalDependencies } = require('../package.json');
|
||||
|
||||
const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || /* istanbul ignore next */
|
||||
config.libvips;
|
||||
/* node:coverage ignore next */
|
||||
const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || config.libvips;
|
||||
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
|
||||
|
||||
const prebuiltPlatforms = [
|
||||
'darwin-arm64', 'darwin-x64',
|
||||
'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-s390x', 'linux-x64',
|
||||
'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-riscv64', 'linux-s390x', 'linux-x64',
|
||||
'linuxmusl-arm64', 'linuxmusl-x64',
|
||||
'win32-arm64', 'win32-ia32', 'win32-x64'
|
||||
];
|
||||
@@ -36,17 +36,16 @@ const log = (item) => {
|
||||
}
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
/* node:coverage ignore next */
|
||||
const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
|
||||
|
||||
const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
|
||||
|
||||
/* istanbul ignore next */
|
||||
const buildPlatformArch = () => {
|
||||
/* node:coverage ignore next 3 */
|
||||
if (isEmscripten()) {
|
||||
return 'wasm32';
|
||||
}
|
||||
/* eslint camelcase: ["error", { allow: ["^npm_config_"] }] */
|
||||
const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
|
||||
const libc = typeof npm_config_libc === 'string' ? npm_config_libc : runtimeLibc();
|
||||
return `${npm_config_platform || process.platform}${libc}-${npm_config_arch || process.arch}`;
|
||||
@@ -56,19 +55,19 @@ const buildSharpLibvipsIncludeDir = () => {
|
||||
try {
|
||||
return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/include`);
|
||||
} catch {
|
||||
/* node:coverage ignore next 5 */
|
||||
try {
|
||||
return require('@img/sharp-libvips-dev/include');
|
||||
} catch {}
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
return '';
|
||||
};
|
||||
|
||||
const buildSharpLibvipsCPlusPlusDir = () => {
|
||||
/* node:coverage ignore next 4 */
|
||||
try {
|
||||
return require('@img/sharp-libvips-dev/cplusplus');
|
||||
} catch {}
|
||||
/* istanbul ignore next */
|
||||
return '';
|
||||
};
|
||||
|
||||
@@ -76,16 +75,17 @@ const buildSharpLibvipsLibDir = () => {
|
||||
try {
|
||||
return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/lib`);
|
||||
} catch {
|
||||
/* node:coverage ignore next 5 */
|
||||
try {
|
||||
return require(`@img/sharp-libvips-${buildPlatformArch()}/lib`);
|
||||
} catch {}
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
return '';
|
||||
};
|
||||
|
||||
/* node:coverage disable */
|
||||
|
||||
const isUnsupportedNodeRuntime = () => {
|
||||
/* istanbul ignore next */
|
||||
if (process.release?.name === 'node' && process.versions) {
|
||||
if (!semverSatisfies(process.versions.node, engines.node)) {
|
||||
return { found: process.versions.node, expected: engines.node };
|
||||
@@ -93,14 +93,12 @@ const isUnsupportedNodeRuntime = () => {
|
||||
}
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
const isEmscripten = () => {
|
||||
const { CC } = process.env;
|
||||
return Boolean(CC && CC.endsWith('/emcc'));
|
||||
return Boolean(CC?.endsWith('/emcc'));
|
||||
};
|
||||
|
||||
const isRosetta = () => {
|
||||
/* istanbul ignore next */
|
||||
if (process.platform === 'darwin' && process.arch === 'x64') {
|
||||
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
|
||||
return (translated || '').trim() === 'sysctl.proc_translated: 1';
|
||||
@@ -108,6 +106,8 @@ const isRosetta = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
/* node:coverage enable */
|
||||
|
||||
const sha512 = (s) => createHash('sha512').update(s).digest('hex');
|
||||
|
||||
const yarnLocator = () => {
|
||||
@@ -121,7 +121,8 @@ const yarnLocator = () => {
|
||||
return '';
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
/* node:coverage disable */
|
||||
|
||||
const spawnRebuild = () =>
|
||||
spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, {
|
||||
...spawnSyncOptions,
|
||||
@@ -137,16 +138,17 @@ const globalLibvipsVersion = () => {
|
||||
PKG_CONFIG_PATH: pkgConfigPath()
|
||||
}
|
||||
}).stdout;
|
||||
/* istanbul ignore next */
|
||||
return (globalLibvipsVersion || '').trim();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
/* node:coverage enable */
|
||||
|
||||
const pkgConfigPath = () => {
|
||||
if (process.platform !== 'win32') {
|
||||
/* node:coverage ignore next 4 */
|
||||
const brewPkgConfigPath = spawnSync(
|
||||
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
|
||||
spawnSyncOptions
|
||||
@@ -178,13 +180,13 @@ const useGlobalLibvips = (logger) => {
|
||||
if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
|
||||
return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS', logger);
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
/* node:coverage ignore next 3 */
|
||||
if (isRosetta()) {
|
||||
return skipSearch(false, 'Rosetta', logger);
|
||||
}
|
||||
const globalVipsVersion = globalLibvipsVersion();
|
||||
return !!globalVipsVersion && /* istanbul ignore next */
|
||||
semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
|
||||
/* node:coverage ignore next */
|
||||
return !!globalVipsVersion && semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const is = require('./is');
|
||||
|
||||
@@ -239,7 +239,7 @@ function affine (matrix, options) {
|
||||
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
|
||||
* Fine-grained control over the level of sharpening in "flat" (m1) and "jagged" (m2) areas is available.
|
||||
*
|
||||
* See {@link https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen|libvips sharpen} operation.
|
||||
* See {@link https://www.libvips.org/API/current/method.Image.sharpen.html libvips sharpen} operation.
|
||||
*
|
||||
* @example
|
||||
* const data = await sharp(input).sharpen().toBuffer();
|
||||
@@ -259,45 +259,18 @@ function affine (matrix, options) {
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object|number} [options] - if present, is an Object with attributes
|
||||
* @param {Object} [options] - if present, is an Object with attributes
|
||||
* @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`, between 0.000001 and 10
|
||||
* @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas, between 0 and 1000000
|
||||
* @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas, between 0 and 1000000
|
||||
* @param {number} [options.x1=2.0] - threshold between "flat" and "jagged", between 0 and 1000000
|
||||
* @param {number} [options.y2=10.0] - maximum amount of brightening, between 0 and 1000000
|
||||
* @param {number} [options.y3=20.0] - maximum amount of darkening, between 0 and 1000000
|
||||
* @param {number} [flat] - (deprecated) see `options.m1`.
|
||||
* @param {number} [jagged] - (deprecated) see `options.m2`.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
function sharpen (options, flat, jagged) {
|
||||
if (!is.defined(options)) {
|
||||
// No arguments: default to mild sharpen
|
||||
this.options.sharpenSigma = -1;
|
||||
} else if (is.bool(options)) {
|
||||
// Deprecated boolean argument: apply mild sharpen?
|
||||
this.options.sharpenSigma = options ? -1 : 0;
|
||||
} else if (is.number(options) && is.inRange(options, 0.01, 10000)) {
|
||||
// Deprecated numeric argument: specific sigma
|
||||
this.options.sharpenSigma = options;
|
||||
// Deprecated control over flat areas
|
||||
if (is.defined(flat)) {
|
||||
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
|
||||
this.options.sharpenM1 = flat;
|
||||
} else {
|
||||
throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
|
||||
}
|
||||
}
|
||||
// Deprecated control over jagged areas
|
||||
if (is.defined(jagged)) {
|
||||
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
|
||||
this.options.sharpenM2 = jagged;
|
||||
} else {
|
||||
throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
|
||||
}
|
||||
}
|
||||
} else if (is.plainObject(options)) {
|
||||
function sharpen (options) {
|
||||
if (is.plainObject(options)) {
|
||||
if (is.number(options.sigma) && is.inRange(options.sigma, 0.000001, 10)) {
|
||||
this.options.sharpenSigma = options.sigma;
|
||||
} else {
|
||||
@@ -339,7 +312,7 @@ function sharpen (options, flat, jagged) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', options);
|
||||
this.options.sharpenSigma = -1;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -485,7 +458,7 @@ function erode (width) {
|
||||
/**
|
||||
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
||||
*
|
||||
* See also {@link /api-channel#removealpha|removeAlpha}.
|
||||
* See also {@link /api-channel#removealpha removeAlpha}.
|
||||
*
|
||||
* @example
|
||||
* await sharp(rgbaInput)
|
||||
@@ -510,8 +483,6 @@ function flatten (options) {
|
||||
*
|
||||
* Existing alpha channel values for non-white pixels remain unchanged.
|
||||
*
|
||||
* This feature is experimental and the API may change.
|
||||
*
|
||||
* @since 0.32.1
|
||||
*
|
||||
* @example
|
||||
@@ -660,7 +631,7 @@ function normalize (options) {
|
||||
|
||||
/**
|
||||
* Perform contrast limiting adaptive histogram equalization
|
||||
* {@link https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE|CLAHE}.
|
||||
* {@link https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE CLAHE}.
|
||||
*
|
||||
* This will, in general, enhance the clarity of the image by bringing out darker details.
|
||||
*
|
||||
@@ -742,9 +713,7 @@ function convolve (kernel) {
|
||||
}
|
||||
// Default scale is sum of kernel values
|
||||
if (!is.integer(kernel.scale)) {
|
||||
kernel.scale = kernel.kernel.reduce(function (a, b) {
|
||||
return a + b;
|
||||
}, 0);
|
||||
kernel.scale = kernel.kernel.reduce((a, b) => a + b, 0);
|
||||
}
|
||||
// Clip scale to a minimum value of 1
|
||||
if (kernel.scale < 1) {
|
||||
@@ -989,7 +958,7 @@ function modulate (options) {
|
||||
* @module Sharp
|
||||
* @private
|
||||
*/
|
||||
module.exports = function (Sharp) {
|
||||
module.exports = (Sharp) => {
|
||||
Object.assign(Sharp.prototype, {
|
||||
autoOrient,
|
||||
rotate,
|
||||
|
||||
135
lib/output.js
135
lib/output.js
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const path = require('node:path');
|
||||
const is = require('./is');
|
||||
@@ -43,7 +43,7 @@ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math
|
||||
* Note that raw pixel data is only supported for buffer output.
|
||||
*
|
||||
* By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
* See {@link #withmetadata|withMetadata} for control over this.
|
||||
* See {@link #withmetadata withMetadata} for control over this.
|
||||
*
|
||||
* The caller is responsible for ensuring directory structures and permissions exist.
|
||||
*
|
||||
@@ -76,7 +76,7 @@ function toFile (fileOut, callback) {
|
||||
err = new Error('Missing output file path');
|
||||
} else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
|
||||
err = new Error('Cannot use same file for input and output');
|
||||
} else if (jp2Regex.test(path.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
|
||||
} else if (jp2Regex.test(path.extname(fileOut)) && !this.constructor.format.jp2.output.file) {
|
||||
err = errJp2Save();
|
||||
}
|
||||
if (err) {
|
||||
@@ -97,12 +97,12 @@ function toFile (fileOut, callback) {
|
||||
* Write output to a Buffer.
|
||||
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
||||
*
|
||||
* Use {@link #toformat|toFormat} or one of the format-specific functions such as {@link jpeg}, {@link png} etc. to set the output format.
|
||||
* Use {@link #toformat toFormat} or one of the format-specific functions such as {@link #jpeg jpeg}, {@link #png png} etc. to set the output format.
|
||||
*
|
||||
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
||||
*
|
||||
* By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
* See {@link #withmetadata|withMetadata} for control over this.
|
||||
* See {@link #withmetadata withMetadata} for control over this.
|
||||
*
|
||||
* `callback`, if present, gets three arguments `(err, data, info)` where:
|
||||
* - `err` is an error, if any.
|
||||
@@ -164,6 +164,41 @@ function toBuffer (options, callback) {
|
||||
return this._pipeline(is.fn(options) ? options : callback, stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write output to a `Uint8Array` backed by a transferable `ArrayBuffer`.
|
||||
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
||||
*
|
||||
* Use {@link #toformat toFormat} or one of the format-specific functions such as {@link #jpeg jpeg}, {@link #png png} etc. to set the output format.
|
||||
*
|
||||
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
||||
*
|
||||
* By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
* See {@link #keepexif keepExif} and similar methods for control over this.
|
||||
*
|
||||
* Resolves with an `Object` containing:
|
||||
* - `data` is the output image as a `Uint8Array` backed by a transferable `ArrayBuffer`.
|
||||
* - `info` contains properties relating to the output image such as `width` and `height`.
|
||||
*
|
||||
* @since v0.35.0
|
||||
*
|
||||
* @example
|
||||
* const { data, info } = await sharp(input).toUint8Array();
|
||||
*
|
||||
* @example
|
||||
* const { data } = await sharp(input)
|
||||
* .avif()
|
||||
* .toUint8Array();
|
||||
* const base64String = data.toBase64();
|
||||
*
|
||||
* @returns {Promise<{ data: Uint8Array, info: Object }>}
|
||||
*/
|
||||
function toUint8Array () {
|
||||
this.options.resolveWithObject = true;
|
||||
this.options.typedArrayOut = true;
|
||||
const stack = Error();
|
||||
return this._pipeline(null, stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep all EXIF metadata from the input image in the output image.
|
||||
*
|
||||
@@ -256,7 +291,7 @@ function withExifMerge (exif) {
|
||||
/**
|
||||
* Keep ICC profile from the input image in the output image.
|
||||
*
|
||||
* Where necessary, will attempt to convert the output colour space to match the profile.
|
||||
* When input and output colour spaces differ, use with {@link /api-colour/#tocolourspace toColourspace} and optionally {@link /api-colour/#pipelinecolourspace pipelineColourspace}.
|
||||
*
|
||||
* @since 0.33.0
|
||||
*
|
||||
@@ -265,6 +300,13 @@ function withExifMerge (exif) {
|
||||
* .keepIccProfile()
|
||||
* .toBuffer();
|
||||
*
|
||||
* @example
|
||||
* const cmykOutputWithIccProfile = await sharp(cmykInputWithIccProfile)
|
||||
* .pipelineColourspace('cmyk')
|
||||
* .toColourspace('cmyk')
|
||||
* .keepIccProfile()
|
||||
* .toBuffer();
|
||||
*
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
function keepIccProfile () {
|
||||
@@ -312,6 +354,30 @@ function withIccProfile (icc, options) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the input contains gain map metadata, use it to convert the main image to HDR (High Dynamic Range) before further processing.
|
||||
* The input gain map is discarded.
|
||||
*
|
||||
* If the output is JPEG, generate and attach a new ISO 21496-1 gain map.
|
||||
* JPEG output options other than `quality` are ignored.
|
||||
*
|
||||
* This feature is experimental and the API may change.
|
||||
*
|
||||
* @since 0.35.0
|
||||
*
|
||||
* @example
|
||||
* const outputWithGainMap = await sharp(inputWithGainMap)
|
||||
* .withGainMap()
|
||||
* .toBuffer();
|
||||
*
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
function withGainMap() {
|
||||
this.options.withGainMap = true;
|
||||
this.options.colourspace = 'scrgb';
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep XMP metadata from the input image in the output image.
|
||||
*
|
||||
@@ -565,7 +631,7 @@ function jpeg (options) {
|
||||
* Set `palette` to `true` for slower, indexed PNG output.
|
||||
*
|
||||
* For 16 bits per pixel output, convert to `rgb16` via
|
||||
* {@link /api-colour#tocolourspace|toColourspace}.
|
||||
* {@link /api-colour/#tocolourspace toColourspace}.
|
||||
*
|
||||
* @example
|
||||
* // Convert any input to full colour PNG output
|
||||
@@ -683,6 +749,7 @@ function png (options) {
|
||||
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
||||
* @param {boolean} [options.minSize=false] - prevent use of animation key frames to minimise file size (slow)
|
||||
* @param {boolean} [options.mixed=false] - allow mixture of lossy and lossless animation frames (slow)
|
||||
* @param {boolean} [options.exact=false] - preserve the colour data in transparent pixels
|
||||
* @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
@@ -735,6 +802,9 @@ function webp (options) {
|
||||
if (is.defined(options.mixed)) {
|
||||
this._setBooleanOption('webpMixed', options.mixed);
|
||||
}
|
||||
if (is.defined(options.exact)) {
|
||||
this._setBooleanOption('webpExact', options.exact);
|
||||
}
|
||||
}
|
||||
trySetAnimationOptions(options, this.options);
|
||||
return this._updateFormatOut('webp', options);
|
||||
@@ -845,13 +915,12 @@ function gif (options) {
|
||||
return this._updateFormatOut('gif', options);
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
/**
|
||||
* Use these JP2 options for output image.
|
||||
*
|
||||
* Requires libvips compiled with support for OpenJPEG.
|
||||
* The prebuilt binaries do not include this - see
|
||||
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
|
||||
* {@link /install#custom-libvips installing a custom libvips}.
|
||||
*
|
||||
* @example
|
||||
* // Convert any input to lossless JP2 output
|
||||
@@ -880,7 +949,8 @@ function gif (options) {
|
||||
* @throws {Error} Invalid options
|
||||
*/
|
||||
function jp2 (options) {
|
||||
if (!this.constructor.format.jp2k.output.buffer) {
|
||||
/* node:coverage ignore next 41 */
|
||||
if (!this.constructor.format.jp2.output.buffer) {
|
||||
throw errJp2Save();
|
||||
}
|
||||
if (is.object(options)) {
|
||||
@@ -959,7 +1029,7 @@ function trySetAnimationOptions (source, target) {
|
||||
/**
|
||||
* Use these TIFF options for output image.
|
||||
*
|
||||
* The `density` can be set in pixels/inch via {@link #withmetadata|withMetadata}
|
||||
* The `density` can be set in pixels/inch via {@link #withmetadata withMetadata}
|
||||
* instead of providing `xres` and `yres` in pixels/mm.
|
||||
*
|
||||
* @example
|
||||
@@ -976,6 +1046,7 @@ function trySetAnimationOptions (source, target) {
|
||||
* @param {number} [options.quality=80] - quality, integer 1-100
|
||||
* @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
|
||||
* @param {string} [options.compression='jpeg'] - compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k
|
||||
* @param {boolean} [options.bigtiff=false] - use BigTIFF variant (has no effect when compression is none)
|
||||
* @param {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
|
||||
* @param {boolean} [options.pyramid=false] - write an image pyramid
|
||||
* @param {boolean} [options.tile=false] - write a tiled tiff
|
||||
@@ -984,7 +1055,7 @@ function trySetAnimationOptions (source, target) {
|
||||
* @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
|
||||
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
|
||||
* @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
|
||||
* @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
|
||||
* @param {number} [options.bitdepth=0] - reduce bitdepth to 1, 2 or 4 bit
|
||||
* @param {boolean} [options.miniswhite=false] - write 1-bit images as miniswhite
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
@@ -999,10 +1070,10 @@ function tiff (options) {
|
||||
}
|
||||
}
|
||||
if (is.defined(options.bitdepth)) {
|
||||
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [1, 2, 4, 8])) {
|
||||
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [1, 2, 4])) {
|
||||
this.options.tiffBitdepth = options.bitdepth;
|
||||
} else {
|
||||
throw is.invalidParameterError('bitdepth', '1, 2, 4 or 8', options.bitdepth);
|
||||
throw is.invalidParameterError('bitdepth', '1, 2 or 4', options.bitdepth);
|
||||
}
|
||||
}
|
||||
// tiling
|
||||
@@ -1054,6 +1125,10 @@ function tiff (options) {
|
||||
throw is.invalidParameterError('compression', 'one of: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k', options.compression);
|
||||
}
|
||||
}
|
||||
// bigtiff
|
||||
if (is.defined(options.bigtiff)) {
|
||||
this._setBooleanOption('tiffBigtiff', options.bigtiff);
|
||||
}
|
||||
// predictor
|
||||
if (is.defined(options.predictor)) {
|
||||
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
|
||||
@@ -1080,8 +1155,7 @@ function tiff (options) {
|
||||
* AVIF image sequences are not supported.
|
||||
* Prebuilt binaries support a bitdepth of 8 only.
|
||||
*
|
||||
* This feature is experimental on the Windows ARM64 platform
|
||||
* and requires a CPU with ARM64v8.4 or later.
|
||||
* When using Windows ARM64, this feature requires a CPU with ARM64v8.4 or later.
|
||||
*
|
||||
* @example
|
||||
* const data = await sharp(input)
|
||||
@@ -1101,11 +1175,13 @@ function tiff (options) {
|
||||
* @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 {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
|
||||
* @param {string} [options.tune='iq'] - tune output for a quality metric, one of 'iq' (default), 'ssim' (default when lossless) or 'psnr'
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
*/
|
||||
function avif (options) {
|
||||
return this.heif({ ...options, compression: 'av1' });
|
||||
const tune = is.object(options) && is.defined(options.tune) ? options.tune : 'iq';
|
||||
return this.heif({ ...options, compression: 'av1', tune });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1128,6 +1204,7 @@ function avif (options) {
|
||||
* @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 {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
|
||||
* @param {string} [options.tune='ssim'] - tune output for a quality metric, one of 'ssim' (default), 'psnr' or 'iq'
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
*/
|
||||
@@ -1176,6 +1253,17 @@ function heif (options) {
|
||||
throw is.invalidParameterError('bitdepth', '8, 10 or 12', options.bitdepth);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.tune)) {
|
||||
if (is.string(options.tune) && is.inArray(options.tune, ['iq', 'ssim', 'psnr'])) {
|
||||
if (this.options.heifLossless && options.tune === 'iq') {
|
||||
this.options.heifTune = 'ssim';
|
||||
} else {
|
||||
this.options.heifTune = options.tune;
|
||||
}
|
||||
} else {
|
||||
throw is.invalidParameterError('tune', 'one of: psnr, ssim, iq', options.tune);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw is.invalidParameterError('options', 'Object', options);
|
||||
}
|
||||
@@ -1189,7 +1277,7 @@ function heif (options) {
|
||||
*
|
||||
* Requires libvips compiled with support for libjxl.
|
||||
* The prebuilt binaries do not include this - see
|
||||
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
|
||||
* {@link /install/#custom-libvips installing a custom libvips}.
|
||||
*
|
||||
* @since 0.31.3
|
||||
*
|
||||
@@ -1502,7 +1590,6 @@ function _setBooleanOption (key, val) {
|
||||
* @private
|
||||
*/
|
||||
function _read () {
|
||||
/* istanbul ignore else */
|
||||
if (!this.options.streamOut) {
|
||||
this.options.streamOut = true;
|
||||
const stack = Error();
|
||||
@@ -1619,16 +1706,18 @@ function _pipeline (callback, stack) {
|
||||
* @module Sharp
|
||||
* @private
|
||||
*/
|
||||
module.exports = function (Sharp) {
|
||||
module.exports = (Sharp) => {
|
||||
Object.assign(Sharp.prototype, {
|
||||
// Public
|
||||
toFile,
|
||||
toBuffer,
|
||||
toUint8Array,
|
||||
keepExif,
|
||||
withExif,
|
||||
withExifMerge,
|
||||
keepIccProfile,
|
||||
withIccProfile,
|
||||
withGainMap,
|
||||
keepXmp,
|
||||
withXmp,
|
||||
keepMetadata,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const is = require('./is');
|
||||
|
||||
@@ -540,10 +540,19 @@ function extract (options) {
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @example
|
||||
* // Trim image leaving (up to) a 10 pixel margin around the trimmed content.
|
||||
* const output = await sharp(input)
|
||||
* .trim({
|
||||
* margin: 10
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {string|Object} [options.background='top-left pixel'] - Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
|
||||
* @param {number} [options.threshold=10] - Allowed difference from the above colour, a positive number.
|
||||
* @param {boolean} [options.lineArt=false] - Does the input more closely resemble line art (e.g. vector) rather than being photographic?
|
||||
* @param {number} [options.margin=0] - Leave a margin around trimmed content, value is in pixels.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
@@ -564,6 +573,13 @@ function trim (options) {
|
||||
if (is.defined(options.lineArt)) {
|
||||
this._setBooleanOption('trimLineArt', options.lineArt);
|
||||
}
|
||||
if (is.defined(options.margin)) {
|
||||
if (is.integer(options.margin) && options.margin >= 0) {
|
||||
this.options.trimMargin = options.margin;
|
||||
} else {
|
||||
throw is.invalidParameterError('margin', 'positive integer', options.margin);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw is.invalidParameterError('trim', 'object', options);
|
||||
}
|
||||
@@ -579,7 +595,7 @@ function trim (options) {
|
||||
* @module Sharp
|
||||
* @private
|
||||
*/
|
||||
module.exports = function (Sharp) {
|
||||
module.exports = (Sharp) => {
|
||||
Object.assign(Sharp.prototype, {
|
||||
resize,
|
||||
extend,
|
||||
|
||||
21
lib/sharp.js
21
lib/sharp.js
@@ -1,22 +1,25 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Inspects the runtime environment and exports the relevant sharp.node binary
|
||||
|
||||
const { familySync, versionSync } = require('detect-libc');
|
||||
|
||||
const { version } = require('../package.json');
|
||||
const { runtimePlatformArch, isUnsupportedNodeRuntime, prebuiltPlatforms, minimumLibvipsVersion } = require('./libvips');
|
||||
const runtimePlatform = runtimePlatformArch();
|
||||
|
||||
const paths = [
|
||||
`../src/build/Release/sharp-${runtimePlatform}.node`,
|
||||
'../src/build/Release/sharp-wasm32.node',
|
||||
`../src/build/Release/sharp-${runtimePlatform}-${version}.node`,
|
||||
`../src/build/Release/sharp-wasm32-${version}.node`,
|
||||
`@img/sharp-${runtimePlatform}/sharp.node`,
|
||||
'@img/sharp-wasm32/sharp.node'
|
||||
];
|
||||
|
||||
/* node:coverage disable */
|
||||
|
||||
let path, sharp;
|
||||
const errors = [];
|
||||
for (path of paths) {
|
||||
@@ -24,12 +27,10 @@ for (path of paths) {
|
||||
sharp = require(path);
|
||||
break;
|
||||
} catch (err) {
|
||||
/* istanbul ignore next */
|
||||
errors.push(err);
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2()) {
|
||||
const err = new Error('Prebuilt binaries for linux-x64 require v2 microarchitecture');
|
||||
err.code = 'Unsupported CPU';
|
||||
@@ -37,7 +38,6 @@ if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2())
|
||||
sharp = null;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (sharp) {
|
||||
module.exports = sharp;
|
||||
} else {
|
||||
@@ -73,6 +73,7 @@ if (sharp) {
|
||||
} else {
|
||||
help.push(
|
||||
`- Manually install libvips >= ${minimumLibvipsVersion}`,
|
||||
' See https://sharp.pixelplumbing.com/install#building-from-source',
|
||||
'- Add experimental WebAssembly-based dependencies:',
|
||||
' npm install --cpu=wasm32 sharp',
|
||||
' npm install @img/sharp-wasm32'
|
||||
@@ -88,7 +89,7 @@ if (sharp) {
|
||||
` Found ${libcFound}`,
|
||||
` Requires ${libcRequires}`
|
||||
);
|
||||
} catch (errEngines) {}
|
||||
} catch (_errEngines) {}
|
||||
}
|
||||
if (isLinux && /\/snap\/core[0-9]{2}/.test(messages)) {
|
||||
help.push(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const events = require('node:events');
|
||||
const detectLibc = require('detect-libc');
|
||||
@@ -24,7 +24,7 @@ const format = sharp.format();
|
||||
format.heif.output.alias = ['avif', 'heic'];
|
||||
format.jpeg.output.alias = ['jpe', 'jpg'];
|
||||
format.tiff.output.alias = ['tif'];
|
||||
format.jp2k.output.alias = ['j2c', 'j2k', 'jp2', 'jpx'];
|
||||
format.jp2.output.alias = ['j2c', 'j2k', 'jp2', 'jpx'];
|
||||
|
||||
/**
|
||||
* An Object containing the available interpolators and their proper values
|
||||
@@ -57,7 +57,7 @@ const interpolators = {
|
||||
let versions = {
|
||||
vips: libvipsVersion.semver
|
||||
};
|
||||
/* istanbul ignore next */
|
||||
/* node:coverage ignore next 15 */
|
||||
if (!libvipsVersion.isGlobal) {
|
||||
if (!libvipsVersion.isWasm) {
|
||||
try {
|
||||
@@ -75,7 +75,7 @@ if (!libvipsVersion.isGlobal) {
|
||||
}
|
||||
versions.sharp = require('../package.json').version;
|
||||
|
||||
/* istanbul ignore next */
|
||||
/* node:coverage ignore next 5 */
|
||||
if (versions.heif && format.heif) {
|
||||
// Prebuilt binaries provide AV1
|
||||
format.heif.input.fileSuffix = ['.avif'];
|
||||
@@ -136,7 +136,7 @@ cache(true);
|
||||
* and these are independent of the value set here.
|
||||
*
|
||||
* :::note
|
||||
* Further {@link /performance|control over performance} is available.
|
||||
* Further {@link /performance/ control over performance} is available.
|
||||
* :::
|
||||
*
|
||||
* @example
|
||||
@@ -150,7 +150,7 @@ cache(true);
|
||||
function concurrency (concurrency) {
|
||||
return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
/* node:coverage ignore next 7 */
|
||||
if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
|
||||
// Reduce default concurrency to 1 when using glibc memory allocator
|
||||
sharp.concurrency(1);
|
||||
@@ -277,7 +277,7 @@ function unblock (options) {
|
||||
* @module Sharp
|
||||
* @private
|
||||
*/
|
||||
module.exports = function (Sharp) {
|
||||
module.exports = (Sharp) => {
|
||||
Sharp.cache = cache;
|
||||
Sharp.concurrency = concurrency;
|
||||
Sharp.counters = counters;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-darwin-arm64",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with macOS 64-bit ARM",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.1"
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-darwin-arm64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"darwin"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-darwin-x64",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with macOS x64",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.1"
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-darwin-x64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"darwin"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Populate the npm package for the current platform with the local build
|
||||
|
||||
@@ -44,9 +44,10 @@ cpSync(releaseDir, libDir, {
|
||||
}
|
||||
});
|
||||
|
||||
// Generate README
|
||||
const { name, description } = require(`./${platform}/package.json`);
|
||||
// Generate README and index.cjs
|
||||
const { version, name, description } = require(`./${platform}/package.json`);
|
||||
writeFileSync(join(destDir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`);
|
||||
writeFileSync(join(destDir, 'index.cjs'), `module.exports = require('./lib/sharp-${platform}-${version}.node');`);
|
||||
|
||||
// Copy Apache-2.0 LICENSE
|
||||
copyFileSync(join(__dirname, '..', 'LICENSE'), join(destDir, 'LICENSE'));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-linux-arm",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.1"
|
||||
"@img/sharp-libvips-linux-arm": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-arm.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.31"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-linux-arm64",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Linux (glibc) 64-bit ARM",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.1"
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-arm64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.26"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-linux-ppc64",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Linux (glibc) ppc64",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.1"
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-ppc64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.36"
|
||||
|
||||
47
npm/linux-riscv64/package.json
Normal file
47
npm/linux-riscv64/package.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@img/sharp-linux-riscv64",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Linux (glibc) RISC-V 64-bit",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lovell/sharp.git",
|
||||
"directory": "npm/linux-riscv64"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.41"
|
||||
},
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-linux-s390x",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Linux (glibc) s390x",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.1"
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-s390x.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.36"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-linux-x64",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Linux (glibc) x64",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.1"
|
||||
"@img/sharp-libvips-linux-x64": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-x64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.26"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-linuxmusl-arm64",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Linux (musl) 64-bit ARM",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.1"
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linuxmusl-arm64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"musl": ">=1.2.2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-linuxmusl-x64",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Linux (musl) x64",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.1"
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.0-rc.2"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linuxmusl-x64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"musl": ">=1.2.2"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"private": "true",
|
||||
"workspaces": [
|
||||
"darwin-arm64",
|
||||
@@ -8,6 +8,7 @@
|
||||
"linux-arm",
|
||||
"linux-arm64",
|
||||
"linux-ppc64",
|
||||
"linux-riscv64",
|
||||
"linux-s390x",
|
||||
"linux-x64",
|
||||
"linuxmusl-arm64",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-wasm32",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with wasm32",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -23,15 +23,15 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-wasm32.node.js",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.4.5"
|
||||
"@emnapi/runtime": "^1.7.1"
|
||||
},
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-win32-arm64",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Windows 64-bit ARM",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,6 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib",
|
||||
"versions.json"
|
||||
],
|
||||
@@ -23,12 +24,12 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-win32-arm64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"win32"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@img/sharp-win32-ia32",
|
||||
"version": "0.34.4-rc.1",
|
||||
"description": "Prebuilt sharp for use with Windows x86 (32-bit)",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Windows x86 (deprecated)",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
"repository": {
|
||||
@@ -15,6 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib",
|
||||
"versions.json"
|
||||
],
|
||||
@@ -23,12 +24,12 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-win32-ia32.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": "^20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"win32"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@img/sharp-win32-x64",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"description": "Prebuilt sharp for use with Windows x64",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
@@ -15,6 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib",
|
||||
"versions.json"
|
||||
],
|
||||
@@ -23,12 +24,12 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-win32-x64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"win32"
|
||||
|
||||
125
package.json
125
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
||||
"version": "0.34.4-rc.1",
|
||||
"version": "0.35.0-rc.0",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
"contributors": [
|
||||
@@ -89,17 +89,19 @@
|
||||
"Lachlan Newman <lachnewman007@gmail.com>",
|
||||
"Dennis Beatty <dennis@dcbeatty.com>",
|
||||
"Ingvar Stepanyan <me@rreverser.com>",
|
||||
"Don Denton <don@happycollision.com>"
|
||||
"Don Denton <don@happycollision.com>",
|
||||
"Dmytro Tiapukhin <cool.gegeg@gmail.com>"
|
||||
],
|
||||
"scripts": {
|
||||
"install": "node install/check.js",
|
||||
"clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*",
|
||||
"test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types",
|
||||
"test-lint": "semistandard && cpplint",
|
||||
"test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha",
|
||||
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"",
|
||||
"build": "node install/build.js",
|
||||
"clean": "rm -rf src/build/ test/fixtures/output.*",
|
||||
"test": "npm run lint && npm run test-unit",
|
||||
"lint": "npm run lint-cpp && npm run lint-js && npm run lint-types",
|
||||
"lint-cpp": "cpplint --quiet src/*.h src/*.cc",
|
||||
"lint-js": "biome lint",
|
||||
"lint-types": "tsd --files ./test/types/sharp.test-d.ts",
|
||||
"test-leak": "./test/leak/leak.sh",
|
||||
"test-types": "tsd",
|
||||
"test-unit": "node --experimental-test-coverage test/unit.mjs",
|
||||
"package-from-local-build": "node npm/from-local-build.js",
|
||||
"package-release-notes": "node npm/release-notes.js",
|
||||
"docs-build": "node docs/build.mjs",
|
||||
@@ -137,84 +139,63 @@
|
||||
"vips"
|
||||
],
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
"detect-libc": "^2.0.4",
|
||||
"semver": "^7.7.2"
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.4-rc.1",
|
||||
"@img/sharp-darwin-x64": "0.34.4-rc.1",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.1",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.1",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.1",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.1",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.1",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.1",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.1",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.1",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.1",
|
||||
"@img/sharp-linux-arm": "0.34.4-rc.1",
|
||||
"@img/sharp-linux-arm64": "0.34.4-rc.1",
|
||||
"@img/sharp-linux-ppc64": "0.34.4-rc.1",
|
||||
"@img/sharp-linux-s390x": "0.34.4-rc.1",
|
||||
"@img/sharp-linux-x64": "0.34.4-rc.1",
|
||||
"@img/sharp-linuxmusl-arm64": "0.34.4-rc.1",
|
||||
"@img/sharp-linuxmusl-x64": "0.34.4-rc.1",
|
||||
"@img/sharp-wasm32": "0.34.4-rc.1",
|
||||
"@img/sharp-win32-arm64": "0.34.4-rc.1",
|
||||
"@img/sharp-win32-ia32": "0.34.4-rc.1",
|
||||
"@img/sharp-win32-x64": "0.34.4-rc.1"
|
||||
"@img/sharp-darwin-arm64": "0.35.0-rc.0",
|
||||
"@img/sharp-darwin-x64": "0.35.0-rc.0",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-linux-arm": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-linux-x64": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.0-rc.2",
|
||||
"@img/sharp-linux-arm": "0.35.0-rc.0",
|
||||
"@img/sharp-linux-arm64": "0.35.0-rc.0",
|
||||
"@img/sharp-linux-ppc64": "0.35.0-rc.0",
|
||||
"@img/sharp-linux-riscv64": "0.35.0-rc.0",
|
||||
"@img/sharp-linux-s390x": "0.35.0-rc.0",
|
||||
"@img/sharp-linux-x64": "0.35.0-rc.0",
|
||||
"@img/sharp-linuxmusl-arm64": "0.35.0-rc.0",
|
||||
"@img/sharp-linuxmusl-x64": "0.35.0-rc.0",
|
||||
"@img/sharp-wasm32": "0.35.0-rc.0",
|
||||
"@img/sharp-win32-arm64": "0.35.0-rc.0",
|
||||
"@img/sharp-win32-ia32": "0.35.0-rc.0",
|
||||
"@img/sharp-win32-x64": "0.35.0-rc.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@emnapi/runtime": "^1.4.5",
|
||||
"@img/sharp-libvips-dev": "1.2.1",
|
||||
"@img/sharp-libvips-dev-wasm32": "1.2.1",
|
||||
"@img/sharp-libvips-win32-arm64": "1.2.1",
|
||||
"@img/sharp-libvips-win32-ia32": "1.2.1",
|
||||
"@img/sharp-libvips-win32-x64": "1.2.1",
|
||||
"@biomejs/biome": "^2.3.10",
|
||||
"@cpplint/cli": "^0.1.0",
|
||||
"@emnapi/runtime": "^1.7.1",
|
||||
"@img/sharp-libvips-dev": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-dev-wasm32": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-win32-arm64": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-win32-ia32": "1.3.0-rc.2",
|
||||
"@img/sharp-libvips-win32-x64": "1.3.0-rc.2",
|
||||
"@types/node": "*",
|
||||
"cc": "^3.0.1",
|
||||
"emnapi": "^1.4.5",
|
||||
"exif-reader": "^2.0.2",
|
||||
"emnapi": "^1.7.1",
|
||||
"exif-reader": "^2.0.3",
|
||||
"extract-zip": "^2.0.1",
|
||||
"icc": "^3.0.0",
|
||||
"jsdoc-to-markdown": "^9.1.2",
|
||||
"license-checker": "^25.0.1",
|
||||
"mocha": "^11.7.1",
|
||||
"node-addon-api": "^8.5.0",
|
||||
"node-gyp": "^11.2.0",
|
||||
"nyc": "^17.1.0",
|
||||
"semistandard": "^17.0.0",
|
||||
"tar-fs": "^3.1.0",
|
||||
"tsd": "^0.32.0"
|
||||
"node-gyp": "^12.1.0",
|
||||
"tar-fs": "^3.1.1",
|
||||
"tsd": "^0.33.0"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"libvips": ">=8.17.1"
|
||||
"libvips": ">=8.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"semistandard": {
|
||||
"env": [
|
||||
"mocha"
|
||||
]
|
||||
},
|
||||
"cc": {
|
||||
"linelength": "120",
|
||||
"filter": [
|
||||
"build/include"
|
||||
]
|
||||
},
|
||||
"nyc": {
|
||||
"include": [
|
||||
"lib"
|
||||
]
|
||||
},
|
||||
"tsd": {
|
||||
"directory": "test/types/"
|
||||
}
|
||||
}
|
||||
|
||||
10
src/CPPLINT.cfg
Normal file
10
src/CPPLINT.cfg
Normal file
@@ -0,0 +1,10 @@
|
||||
set noparent
|
||||
|
||||
linelength=120
|
||||
|
||||
filter=-build/include
|
||||
filter=+build/include_alpha
|
||||
filter=+build/include_subdir
|
||||
filter=+build/include_what_you_use
|
||||
|
||||
filter=-whitespace/indent_namespace
|
||||
@@ -5,6 +5,7 @@
|
||||
'variables': {
|
||||
'vips_version': '<!(node -p "require(\'../lib/libvips\').minimumLibvipsVersion")',
|
||||
'platform_and_arch': '<!(node -p "require(\'../lib/libvips\').buildPlatformArch()")',
|
||||
'sharp_version': '<!(node -p "require(\'../package.json\').version")',
|
||||
'sharp_libvips_version': '<!(node -p "require(\'../package.json\').optionalDependencies[\'@img/sharp-libvips-<(platform_and_arch)\']")',
|
||||
'sharp_libvips_yarn_locator': '<!(node -p "require(\'../lib/libvips\').yarnLocator()")',
|
||||
'sharp_libvips_include_dir': '<!(node -p "require(\'../lib/libvips\').buildSharpLibvipsIncludeDir()")',
|
||||
@@ -20,6 +21,7 @@
|
||||
'defines': [
|
||||
'_VIPS_PUBLIC=__declspec(dllexport)',
|
||||
'_ALLOW_KEYWORD_MACROS',
|
||||
'_HAS_EXCEPTIONS=1',
|
||||
'G_DISABLE_ASSERT',
|
||||
'G_DISABLE_CAST_CHECKS',
|
||||
'G_DISABLE_CHECKS'
|
||||
@@ -81,7 +83,7 @@
|
||||
}]
|
||||
]
|
||||
}, {
|
||||
'target_name': 'sharp-<(platform_and_arch)',
|
||||
'target_name': 'sharp-<(platform_and_arch)-<(sharp_version)',
|
||||
'defines': [
|
||||
'G_DISABLE_ASSERT',
|
||||
'G_DISABLE_CAST_CHECKS',
|
||||
@@ -120,7 +122,7 @@
|
||||
'conditions': [
|
||||
['use_global_libvips == "true"', {
|
||||
# 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)'],
|
||||
'defines': [
|
||||
'SHARP_USE_GLOBAL_LIBVIPS'
|
||||
@@ -147,7 +149,8 @@
|
||||
['OS == "win"', {
|
||||
'defines': [
|
||||
'_ALLOW_KEYWORD_MACROS',
|
||||
'_FILE_OFFSET_BITS=64'
|
||||
'_FILE_OFFSET_BITS=64',
|
||||
'_HAS_EXCEPTIONS=1'
|
||||
],
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
@@ -168,6 +171,7 @@
|
||||
# Ensure runtime linking is relative to sharp.node
|
||||
'-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'',
|
||||
'-Wl,-rpath,\'@loader_path/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'',
|
||||
'-Wl,-rpath,\'@loader_path/../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'',
|
||||
'-Wl,-rpath,\'@loader_path/../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'',
|
||||
'-Wl,-rpath,\'@loader_path/../../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'',
|
||||
'-Wl,-rpath,\'@loader_path/../../../../../@img-sharp-libvips-<(platform_and_arch)-npm-<(sharp_libvips_version)-<(sharp_libvips_yarn_locator)/node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\''
|
||||
@@ -208,11 +212,9 @@
|
||||
'-Oz',
|
||||
'-sALLOW_MEMORY_GROWTH',
|
||||
'-sENVIRONMENT=node',
|
||||
'-sEXPORTED_FUNCTIONS=["emnapiInit", "_vips_shutdown", "_uv_library_shutdown"]',
|
||||
'-sEXPORTED_FUNCTIONS=emnapiInit,_vips_shutdown,_uv_library_shutdown',
|
||||
'-sNODERAWFS',
|
||||
'-sTEXTDECODER=0',
|
||||
'-sWASM_ASYNC_COMPILATION=0',
|
||||
'-sWASM_BIGINT'
|
||||
'-sWASM_ASYNC_COMPILATION=0'
|
||||
],
|
||||
'libraries': [
|
||||
'<!@(PKG_CONFIG_PATH="<!(node -p "require(\'@img/sharp-libvips-dev-wasm32/lib\')")/pkgconfig" pkg-config --static --libs vips-cpp)'
|
||||
@@ -283,7 +285,7 @@
|
||||
'target_name': 'copy-dll',
|
||||
'type': 'none',
|
||||
'dependencies': [
|
||||
'sharp-<(platform_and_arch)'
|
||||
'sharp-<(platform_and_arch)-<(sharp_version)'
|
||||
],
|
||||
'conditions': [
|
||||
['OS == "win"', {
|
||||
|
||||
101
src/common.cc
101
src/common.cc
@@ -1,18 +1,22 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <mutex> // NOLINT(build/c++11)
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <napi.h>
|
||||
#include <vips/vips8>
|
||||
|
||||
#include "common.h"
|
||||
#include "./common.h"
|
||||
|
||||
using vips::VImage;
|
||||
|
||||
@@ -285,6 +289,7 @@ namespace sharp {
|
||||
case ImageType::JXL: id = "jxl"; break;
|
||||
case ImageType::RAD: id = "rad"; break;
|
||||
case ImageType::DCRAW: id = "dcraw"; break;
|
||||
case ImageType::UHDR: id = "uhdr"; break;
|
||||
case ImageType::VIPS: id = "vips"; break;
|
||||
case ImageType::RAW: id = "raw"; break;
|
||||
case ImageType::UNKNOWN: id = "unknown"; break;
|
||||
@@ -335,6 +340,9 @@ namespace sharp {
|
||||
{ "VipsForeignLoadRadBuffer", ImageType::RAD },
|
||||
{ "VipsForeignLoadDcRawFile", ImageType::DCRAW },
|
||||
{ "VipsForeignLoadDcRawBuffer", ImageType::DCRAW },
|
||||
{ "VipsForeignLoadUhdr", ImageType::UHDR },
|
||||
{ "VipsForeignLoadUhdrFile", ImageType::UHDR },
|
||||
{ "VipsForeignLoadUhdrBuffer", ImageType::UHDR },
|
||||
{ "VipsForeignLoadVips", ImageType::VIPS },
|
||||
{ "VipsForeignLoadVipsFile", ImageType::VIPS },
|
||||
{ "VipsForeignLoadRaw", ImageType::RAW }
|
||||
@@ -352,6 +360,9 @@ namespace sharp {
|
||||
imageType = it->second;
|
||||
}
|
||||
}
|
||||
if (imageType == ImageType::UHDR) {
|
||||
imageType = ImageType::JPEG;
|
||||
}
|
||||
return imageType;
|
||||
}
|
||||
|
||||
@@ -371,6 +382,9 @@ namespace sharp {
|
||||
imageType = ImageType::MISSING;
|
||||
}
|
||||
}
|
||||
if (imageType == ImageType::UHDR) {
|
||||
imageType = ImageType::JPEG;
|
||||
}
|
||||
return imageType;
|
||||
}
|
||||
|
||||
@@ -396,6 +410,7 @@ namespace sharp {
|
||||
imageType == ImageType::JPEG ||
|
||||
imageType == ImageType::PNG ||
|
||||
imageType == ImageType::SVG ||
|
||||
imageType == ImageType::TIFF ||
|
||||
imageType == ImageType::HEIF;
|
||||
}
|
||||
|
||||
@@ -411,7 +426,7 @@ namespace sharp {
|
||||
}
|
||||
if (ImageTypeSupportsPage(imageType)) {
|
||||
option->set("n", descriptor->pages);
|
||||
option->set("page", descriptor->page);
|
||||
option->set("page", std::max(0, descriptor->page));
|
||||
}
|
||||
switch (imageType) {
|
||||
case ImageType::SVG:
|
||||
@@ -420,14 +435,14 @@ namespace sharp {
|
||||
->set("high_bitdepth", descriptor->svgHighBitdepth);
|
||||
break;
|
||||
case ImageType::TIFF:
|
||||
option->set("tiffSubifd", descriptor->tiffSubifd);
|
||||
option->set("subifd", descriptor->tiffSubifd);
|
||||
break;
|
||||
case ImageType::PDF:
|
||||
option->set("dpi", descriptor->density)
|
||||
->set("background", descriptor->pdfBackground);
|
||||
break;
|
||||
case ImageType::OPENSLIDE:
|
||||
option->set("openSlideLevel", descriptor->openSlideLevel);
|
||||
option->set("level", descriptor->openSlideLevel);
|
||||
break;
|
||||
case ImageType::JP2:
|
||||
option->set("oneshot", descriptor->jp2Oneshot);
|
||||
@@ -441,6 +456,22 @@ namespace sharp {
|
||||
return option;
|
||||
}
|
||||
|
||||
/*
|
||||
Should HEIF image be re-opened using the primary item?
|
||||
*/
|
||||
static bool HeifPrimaryPageReopen(VImage image, InputDescriptor *descriptor) {
|
||||
if (image.get_typeof(VIPS_META_N_PAGES) == G_TYPE_INT && image.get_typeof("heif-primary") == G_TYPE_INT) {
|
||||
if (image.get_int(VIPS_META_N_PAGES) > 1 && descriptor->pages == 1 && descriptor->page == -1) {
|
||||
int const pagePrimary = image.get_int("heif-primary");
|
||||
if (pagePrimary != 0) {
|
||||
descriptor->page = pagePrimary;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
|
||||
*/
|
||||
@@ -475,12 +506,15 @@ namespace sharp {
|
||||
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||
image = SetDensity(image, descriptor->density);
|
||||
} else if (imageType == ImageType::HEIF && HeifPrimaryPageReopen(image, descriptor)) {
|
||||
option = GetOptionsForImageType(imageType, descriptor);
|
||||
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
|
||||
}
|
||||
} catch (vips::VError const &err) {
|
||||
throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
|
||||
} catch (std::runtime_error const &err) {
|
||||
throw std::runtime_error(std::string("Input buffer has corrupt header: ") + err.what());
|
||||
}
|
||||
} else {
|
||||
throw vips::VError("Input buffer contains unsupported image format");
|
||||
throw std::runtime_error("Input buffer contains unsupported image format");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -551,10 +585,10 @@ namespace sharp {
|
||||
imageType = DetermineImageType(descriptor->file.data());
|
||||
if (imageType == ImageType::MISSING) {
|
||||
if (descriptor->file.find("<svg") != std::string::npos) {
|
||||
throw vips::VError("Input file is missing, did you mean "
|
||||
throw std::runtime_error("Input file is missing, did you mean "
|
||||
"sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");
|
||||
}
|
||||
throw vips::VError("Input file is missing: " + descriptor->file);
|
||||
throw std::runtime_error("Input file is missing: " + descriptor->file);
|
||||
}
|
||||
if (imageType != ImageType::UNKNOWN) {
|
||||
try {
|
||||
@@ -562,12 +596,15 @@ namespace sharp {
|
||||
image = VImage::new_from_file(descriptor->file.data(), option);
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||
image = SetDensity(image, descriptor->density);
|
||||
} else if (imageType == ImageType::HEIF && HeifPrimaryPageReopen(image, descriptor)) {
|
||||
option = GetOptionsForImageType(imageType, descriptor);
|
||||
image = VImage::new_from_file(descriptor->file.data(), option);
|
||||
}
|
||||
} catch (vips::VError const &err) {
|
||||
throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
|
||||
} catch (std::runtime_error const &err) {
|
||||
throw std::runtime_error(std::string("Input file has corrupt header: ") + err.what());
|
||||
}
|
||||
} else {
|
||||
throw vips::VError("Input file contains unsupported image format");
|
||||
throw std::runtime_error("Input file contains unsupported image format");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -575,7 +612,7 @@ namespace sharp {
|
||||
// Limit input images to a given number of pixels, where pixels = width * height
|
||||
if (descriptor->limitInputPixels > 0 &&
|
||||
static_cast<uint64_t>(image.width()) * image.height() > descriptor->limitInputPixels) {
|
||||
throw vips::VError("Input image exceeds pixel limit");
|
||||
throw std::runtime_error("Input image exceeds pixel limit");
|
||||
}
|
||||
return std::make_tuple(image, imageType);
|
||||
}
|
||||
@@ -751,19 +788,19 @@ namespace sharp {
|
||||
: image.height();
|
||||
if (imageType == ImageType::JPEG) {
|
||||
if (image.width() > 65535 || height > 65535) {
|
||||
throw vips::VError("Processed image is too large for the JPEG format");
|
||||
throw std::runtime_error("Processed image is too large for the JPEG format");
|
||||
}
|
||||
} else if (imageType == ImageType::WEBP) {
|
||||
if (image.width() > 16383 || height > 16383) {
|
||||
throw vips::VError("Processed image is too large for the WebP format");
|
||||
throw std::runtime_error("Processed image is too large for the WebP format");
|
||||
}
|
||||
} else if (imageType == ImageType::GIF) {
|
||||
if (image.width() > 65535 || height > 65535) {
|
||||
throw vips::VError("Processed image is too large for the GIF format");
|
||||
throw std::runtime_error("Processed image is too large for the GIF format");
|
||||
}
|
||||
} else if (imageType == ImageType::HEIF) {
|
||||
if (image.width() > 16384 || height > 16384) {
|
||||
throw vips::VError("Processed image is too large for the HEIF format");
|
||||
throw std::runtime_error("Processed image is too large for the HEIF format");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1122,4 +1159,20 @@ namespace sharp {
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/*
|
||||
Does this image have a gain map?
|
||||
*/
|
||||
bool HasGainMap(VImage image) {
|
||||
return image.get_typeof("gainmap-data") == VIPS_TYPE_BLOB;
|
||||
}
|
||||
|
||||
/*
|
||||
Removes gain map, if any.
|
||||
*/
|
||||
VImage RemoveGainMap(VImage image) {
|
||||
VImage copy = image.copy();
|
||||
copy.remove("gainmap-data");
|
||||
return copy;
|
||||
}
|
||||
} // namespace sharp
|
||||
|
||||
30
src/common.h
30
src/common.h
@@ -1,13 +1,16 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef SRC_COMMON_H_
|
||||
#define SRC_COMMON_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
#include <napi.h>
|
||||
#include <vips/vips8>
|
||||
@@ -15,9 +18,9 @@
|
||||
// Verify platform and compiler compatibility
|
||||
|
||||
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 17) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 17 && VIPS_MICRO_VERSION < 1)
|
||||
#error "libvips version 8.17.1+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 18) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 18 && VIPS_MICRO_VERSION < 0)
|
||||
#error "libvips version 8.18.0+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
#endif
|
||||
|
||||
#if defined(__has_include)
|
||||
@@ -30,7 +33,7 @@ using vips::VImage;
|
||||
|
||||
namespace sharp {
|
||||
|
||||
struct InputDescriptor { // NOLINT(runtime/indentation_namespace)
|
||||
struct InputDescriptor {
|
||||
std::string name;
|
||||
std::string file;
|
||||
bool autoOrient;
|
||||
@@ -102,7 +105,7 @@ namespace sharp {
|
||||
rawPremultiplied(false),
|
||||
rawPageHeight(0),
|
||||
pages(1),
|
||||
page(0),
|
||||
page(-1),
|
||||
createChannels(0),
|
||||
createWidth(0),
|
||||
createHeight(0),
|
||||
@@ -170,6 +173,7 @@ namespace sharp {
|
||||
JXL,
|
||||
RAD,
|
||||
DCRAW,
|
||||
UHDR,
|
||||
VIPS,
|
||||
RAW,
|
||||
UNKNOWN,
|
||||
@@ -394,6 +398,16 @@ namespace sharp {
|
||||
*/
|
||||
VImage StaySequential(VImage image, bool condition = true);
|
||||
|
||||
/*
|
||||
Does this image have a gain map?
|
||||
*/
|
||||
bool HasGainMap(VImage image);
|
||||
|
||||
/*
|
||||
Removes gain map, if any.
|
||||
*/
|
||||
VImage RemoveGainMap(VImage image);
|
||||
|
||||
} // namespace sharp
|
||||
|
||||
#endif // SRC_COMMON_H_
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
'-sAUTO_JS_LIBRARIES=0',
|
||||
'-sAUTO_NATIVE_LIBRARIES=0',
|
||||
'-sDEFAULT_TO_CXX=0',
|
||||
'-sNODEJS_CATCH_EXIT=0',
|
||||
'-sNODEJS_CATCH_REJECTION=0'
|
||||
],
|
||||
'defines': [
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* global Module, ENV, _vips_shutdown, _uv_library_shutdown */
|
||||
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <napi.h>
|
||||
#include <vips/vips8>
|
||||
|
||||
#include "common.h"
|
||||
#include "metadata.h"
|
||||
#include "./common.h"
|
||||
#include "./metadata.h"
|
||||
|
||||
static void* readPNGComment(VipsImage *image, const char *field, GValue *value, void *p);
|
||||
|
||||
@@ -27,7 +31,7 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
|
||||
try {
|
||||
std::tie(image, imageType) = OpenInput(baton->input);
|
||||
} catch (vips::VError const &err) {
|
||||
} catch (std::runtime_error const &err) {
|
||||
(baton->err).append(err.what());
|
||||
}
|
||||
if (imageType != sharp::ImageType::UNKNOWN) {
|
||||
@@ -137,6 +141,14 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength);
|
||||
baton->tifftagPhotoshopLength = tifftagPhotoshopLength;
|
||||
}
|
||||
// Gain Map
|
||||
if (image.get_typeof("gainmap-data") == VIPS_TYPE_BLOB) {
|
||||
size_t gainMapLength;
|
||||
void const *gainMap = image.get_blob("gainmap-data", &gainMapLength);
|
||||
baton->gainMap = static_cast<char *>(g_malloc(gainMapLength));
|
||||
memcpy(baton->gainMap, gainMap, gainMapLength);
|
||||
baton->gainMapLength = gainMapLength;
|
||||
}
|
||||
// PNG comments
|
||||
vips_image_map(image.get_image(), readPNGComment, &baton->comments);
|
||||
}
|
||||
@@ -178,10 +190,6 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
info.Set("isPalette", baton->isPalette);
|
||||
if (baton->bitsPerSample > 0) {
|
||||
info.Set("bitsPerSample", baton->bitsPerSample);
|
||||
if (baton->isPalette) {
|
||||
// Deprecated, remove with libvips 8.17.0
|
||||
info.Set("paletteBitDepth", baton->bitsPerSample);
|
||||
}
|
||||
}
|
||||
if (baton->pages > 0) {
|
||||
info.Set("pages", baton->pages);
|
||||
@@ -215,10 +223,10 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
if (!baton->levels.empty()) {
|
||||
int i = 0;
|
||||
Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
|
||||
for (std::pair<int, int> const &l : baton->levels) {
|
||||
for (const auto& [width, height] : baton->levels) {
|
||||
Napi::Object level = Napi::Object::New(env);
|
||||
level.Set("width", l.first);
|
||||
level.Set("height", l.second);
|
||||
level.Set("width", width);
|
||||
level.Set("height", height);
|
||||
levels.Set(i++, level);
|
||||
}
|
||||
info.Set("levels", levels);
|
||||
@@ -261,24 +269,30 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
info.Set("iptc", Napi::Buffer<char>::NewOrCopy(env, baton->iptc, baton->iptcLength, sharp::FreeCallback));
|
||||
}
|
||||
if (baton->xmpLength > 0) {
|
||||
info.Set("xmp", Napi::Buffer<char>::NewOrCopy(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
|
||||
if (g_utf8_validate(static_cast<char const *>(baton->xmp), baton->xmpLength, nullptr)) {
|
||||
info.Set("xmpAsString",
|
||||
Napi::String::New(env, static_cast<char const *>(baton->xmp), baton->xmpLength));
|
||||
}
|
||||
info.Set("xmp", Napi::Buffer<char>::NewOrCopy(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
|
||||
}
|
||||
if (baton->tifftagPhotoshopLength > 0) {
|
||||
info.Set("tifftagPhotoshop",
|
||||
Napi::Buffer<char>::NewOrCopy(env, baton->tifftagPhotoshop,
|
||||
baton->tifftagPhotoshopLength, sharp::FreeCallback));
|
||||
}
|
||||
if (baton->gainMapLength > 0) {
|
||||
Napi::Object gainMap = Napi::Object::New(env);
|
||||
info.Set("gainMap", gainMap);
|
||||
gainMap.Set("image",
|
||||
Napi::Buffer<char>::NewOrCopy(env, baton->gainMap, baton->gainMapLength, sharp::FreeCallback));
|
||||
}
|
||||
if (baton->comments.size() > 0) {
|
||||
int i = 0;
|
||||
Napi::Array comments = Napi::Array::New(env, baton->comments.size());
|
||||
for (auto &c : baton->comments) {
|
||||
for (const auto& [keyword, text] : baton->comments) {
|
||||
Napi::Object comment = Napi::Object::New(env);
|
||||
comment.Set("keyword", c.first);
|
||||
comment.Set("text", c.second);
|
||||
comment.Set("keyword", keyword);
|
||||
comment.Set("text", text);
|
||||
comments.Set(i++, comment);
|
||||
}
|
||||
info.Set("comments", comments);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef SRC_METADATA_H_
|
||||
#define SRC_METADATA_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <napi.h>
|
||||
|
||||
#include "./common.h"
|
||||
@@ -50,6 +53,8 @@ struct MetadataBaton {
|
||||
size_t xmpLength;
|
||||
char *tifftagPhotoshop;
|
||||
size_t tifftagPhotoshopLength;
|
||||
char *gainMap;
|
||||
size_t gainMapLength;
|
||||
MetadataComments comments;
|
||||
std::string err;
|
||||
|
||||
@@ -79,7 +84,9 @@ struct MetadataBaton {
|
||||
xmp(nullptr),
|
||||
xmpLength(0),
|
||||
tifftagPhotoshop(nullptr),
|
||||
tifftagPhotoshopLength(0) {}
|
||||
tifftagPhotoshopLength(0),
|
||||
gainMap(nullptr),
|
||||
gainMapLength(0) {}
|
||||
};
|
||||
|
||||
Napi::Value metadata(const Napi::CallbackInfo& info);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
@@ -8,11 +10,10 @@
|
||||
#include <vector>
|
||||
#include <vips/vips8>
|
||||
|
||||
#include "common.h"
|
||||
#include "operations.h"
|
||||
#include "./common.h"
|
||||
#include "./operations.h"
|
||||
|
||||
using vips::VImage;
|
||||
using vips::VError;
|
||||
|
||||
namespace sharp {
|
||||
/*
|
||||
@@ -283,9 +284,9 @@ namespace sharp {
|
||||
/*
|
||||
Trim an image
|
||||
*/
|
||||
VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt) {
|
||||
VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt, int const margin) {
|
||||
if (image.width() < 3 && image.height() < 3) {
|
||||
throw VError("Image to trim must be at least 3x3 pixels");
|
||||
throw std::runtime_error("Image to trim must be at least 3x3 pixels");
|
||||
}
|
||||
if (background.size() == 0) {
|
||||
// Top-left pixel provides the default background colour if none is given
|
||||
@@ -318,18 +319,36 @@ namespace sharp {
|
||||
if (widthA > 0 && heightA > 0) {
|
||||
if (width > 0 && height > 0) {
|
||||
// Combined bounding box (B)
|
||||
int const leftB = std::min(left, leftA);
|
||||
int const topB = std::min(top, topA);
|
||||
int const widthB = std::max(left + width, leftA + widthA) - leftB;
|
||||
int const heightB = std::max(top + height, topA + heightA) - topB;
|
||||
int leftB = std::min(left, leftA);
|
||||
int topB = std::min(top, topA);
|
||||
int widthB = std::max(left + width, leftA + widthA) - leftB;
|
||||
int heightB = std::max(top + height, topA + heightA) - topB;
|
||||
if (margin > 0) {
|
||||
leftB = std::max(0, leftB - margin);
|
||||
topB = std::max(0, topB - margin);
|
||||
widthB = std::min(image.width() - leftB, widthB + 2 * margin);
|
||||
heightB = std::min(image.height() - topB, heightB + 2 * margin);
|
||||
}
|
||||
return image.extract_area(leftB, topB, widthB, heightB);
|
||||
} else {
|
||||
// Use alpha only
|
||||
if (margin > 0) {
|
||||
leftA = std::max(0, leftA - margin);
|
||||
topA = std::max(0, topA - margin);
|
||||
widthA = std::min(image.width() - leftA, widthA + 2 * margin);
|
||||
heightA = std::min(image.height() - topA, heightA + 2 * margin);
|
||||
}
|
||||
return image.extract_area(leftA, topA, widthA, heightA);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (width > 0 && height > 0) {
|
||||
if (margin > 0) {
|
||||
left = std::max(0, left - margin);
|
||||
top = std::max(0, top - margin);
|
||||
width = std::min(image.width() - left, width + 2 * margin);
|
||||
height = std::min(image.height() - top, height + 2 * margin);
|
||||
}
|
||||
return image.extract_area(left, top, width, height);
|
||||
}
|
||||
return image;
|
||||
@@ -341,7 +360,7 @@ namespace sharp {
|
||||
VImage Linear(VImage image, std::vector<double> const a, std::vector<double> const b) {
|
||||
size_t const bands = static_cast<size_t>(image.bands());
|
||||
if (a.size() > bands) {
|
||||
throw VError("Band expansion using linear is unsupported");
|
||||
throw std::runtime_error("Band expansion using linear is unsupported");
|
||||
}
|
||||
bool const uchar = !Is16Bit(image.interpretation());
|
||||
if (image.has_alpha() && a.size() != bands && (a.size() == 1 || a.size() == bands - 1 || bands - 1 == 1)) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef SRC_OPERATIONS_H_
|
||||
#define SRC_OPERATIONS_H_
|
||||
@@ -8,6 +10,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <vips/vips8>
|
||||
|
||||
using vips::VImage;
|
||||
@@ -79,7 +82,7 @@ namespace sharp {
|
||||
/*
|
||||
Trim an image
|
||||
*/
|
||||
VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt);
|
||||
VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt, int const margin);
|
||||
|
||||
/*
|
||||
* Linear adjustment (a * in + b)
|
||||
|
||||
136
src/pipeline.cc
136
src/pipeline.cc
@@ -1,9 +1,11 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <filesystem> // NOLINT(build/c++17)
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
@@ -17,9 +19,9 @@
|
||||
#include <vips/vips8>
|
||||
#include <napi.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "operations.h"
|
||||
#include "pipeline.h"
|
||||
#include "./common.h"
|
||||
#include "./operations.h"
|
||||
#include "./pipeline.h"
|
||||
|
||||
class PipelineWorker : public Napi::AsyncWorker {
|
||||
public:
|
||||
@@ -82,7 +84,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (nPages == -1) {
|
||||
// Resolve the number of pages if we need to render until the end of the document
|
||||
nPages = image.get_typeof(VIPS_META_N_PAGES) != 0
|
||||
? image.get_int(VIPS_META_N_PAGES) - baton->input->page
|
||||
? image.get_int(VIPS_META_N_PAGES) - std::max(0, baton->input->page)
|
||||
: 1;
|
||||
}
|
||||
|
||||
@@ -151,7 +153,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (baton->trimThreshold >= 0.0) {
|
||||
MultiPageUnsupported(nPages, "Trim");
|
||||
image = sharp::StaySequential(image);
|
||||
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold, baton->trimLineArt);
|
||||
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold, baton->trimLineArt, baton->trimMargin);
|
||||
baton->trimOffsetLeft = image.xoffset();
|
||||
baton->trimOffsetTop = image.yoffset();
|
||||
}
|
||||
@@ -272,7 +274,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
}
|
||||
sharp::SetDensity(image, baton->input->density);
|
||||
if (image.width() > 32767 || image.height() > 32767) {
|
||||
throw vips::VError("Input SVG image will exceed 32767x32767 pixel limit when scaled");
|
||||
throw std::runtime_error("Input SVG image will exceed 32767x32767 pixel limit when scaled");
|
||||
}
|
||||
} else if (inputImageType == sharp::ImageType::PDF) {
|
||||
if (baton->input->buffer != nullptr) {
|
||||
@@ -288,12 +290,20 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
}
|
||||
} else {
|
||||
if (inputImageType == sharp::ImageType::SVG && (image.width() > 32767 || image.height() > 32767)) {
|
||||
throw vips::VError("Input SVG image exceeds 32767x32767 pixel limit");
|
||||
throw std::runtime_error("Input SVG image exceeds 32767x32767 pixel limit");
|
||||
}
|
||||
}
|
||||
if (baton->input->autoOrient) {
|
||||
image = sharp::RemoveExifOrientation(image);
|
||||
}
|
||||
if (sharp::HasGainMap(image)) {
|
||||
if (baton->withGainMap) {
|
||||
image = image.uhdr2scRGB();
|
||||
}
|
||||
image = sharp::RemoveGainMap(image);
|
||||
} else {
|
||||
baton->withGainMap = false;
|
||||
}
|
||||
|
||||
// Any pre-shrinking may already have been done
|
||||
inputWidth = image.width();
|
||||
@@ -333,7 +343,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
image.interpretation() != VIPS_INTERPRETATION_LABS &&
|
||||
image.interpretation() != VIPS_INTERPRETATION_GREY16 &&
|
||||
baton->colourspacePipeline != VIPS_INTERPRETATION_CMYK &&
|
||||
!baton->input->ignoreIcc
|
||||
!baton->input->ignoreIcc && !baton->withGainMap
|
||||
) {
|
||||
// Convert to sRGB/P3 using embedded profile
|
||||
try {
|
||||
@@ -453,12 +463,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground, shouldPremultiplyAlpha);
|
||||
|
||||
// Embed
|
||||
int left;
|
||||
int top;
|
||||
std::tie(left, top) = sharp::CalculateEmbedPosition(
|
||||
const auto& [left, top] = sharp::CalculateEmbedPosition(
|
||||
inputWidth, inputHeight, baton->width, baton->height, baton->position);
|
||||
int width = std::max(inputWidth, baton->width);
|
||||
int height = std::max(inputHeight, baton->height);
|
||||
const int width = std::max(inputWidth, baton->width);
|
||||
const int height = std::max(inputHeight, baton->height);
|
||||
|
||||
image = nPages > 1
|
||||
? sharp::EmbedMultiPage(image,
|
||||
@@ -477,13 +485,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Crop
|
||||
if (baton->position < 9) {
|
||||
// Gravity-based crop
|
||||
int left;
|
||||
int top;
|
||||
|
||||
std::tie(left, top) = sharp::CalculateCrop(
|
||||
const auto& [left, top] = sharp::CalculateCrop(
|
||||
inputWidth, inputHeight, baton->width, baton->height, baton->position);
|
||||
int width = std::min(inputWidth, baton->width);
|
||||
int height = std::min(inputHeight, baton->height);
|
||||
const int width = std::min(inputWidth, baton->width);
|
||||
const int height = std::min(inputHeight, baton->height);
|
||||
|
||||
image = nPages > 1
|
||||
? sharp::CropMultiPage(image,
|
||||
@@ -670,7 +675,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
// Verify within current dimensions
|
||||
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
|
||||
throw vips::VError("Image to composite must have same dimensions or smaller");
|
||||
throw std::runtime_error("Image to composite must have same dimensions or smaller");
|
||||
}
|
||||
// Check if overlay is tiled
|
||||
if (composite->tile) {
|
||||
@@ -795,20 +800,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
image = sharp::EnsureAlpha(image, baton->ensureAlpha);
|
||||
}
|
||||
|
||||
// Convert image to sRGB, if not already
|
||||
// Ensure output colour space
|
||||
if (sharp::Is16Bit(image.interpretation())) {
|
||||
image = image.cast(VIPS_FORMAT_USHORT);
|
||||
}
|
||||
if (image.interpretation() != baton->colourspace) {
|
||||
// Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
|
||||
image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
|
||||
// Transform colours from embedded profile to output profile
|
||||
if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) && baton->colourspacePipeline != VIPS_INTERPRETATION_CMYK &&
|
||||
baton->withIccProfile.empty() && sharp::HasProfile(image)) {
|
||||
image = image.icc_transform(processingProfile, VImage::option()
|
||||
->set("embedded", true)
|
||||
->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
|
||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||
if (inputProfile.first != nullptr && baton->withIccProfile.empty()) {
|
||||
image = sharp::SetProfile(image, inputProfile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -843,8 +842,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
} catch(...) {
|
||||
sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid profile", nullptr);
|
||||
}
|
||||
} else if (baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) {
|
||||
image = sharp::SetProfile(image, inputProfile);
|
||||
}
|
||||
|
||||
// Negate the colours in the image
|
||||
@@ -866,8 +863,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (!baton->withExifMerge) {
|
||||
image = sharp::RemoveExif(image);
|
||||
}
|
||||
for (const auto& s : baton->withExif) {
|
||||
image.set(s.first.data(), s.second.data());
|
||||
for (const auto& [key, value] : baton->withExif) {
|
||||
image.set(key.c_str(), value.c_str());
|
||||
}
|
||||
}
|
||||
// XMP buffer
|
||||
@@ -968,6 +965,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("effort", baton->webpEffort)
|
||||
->set("min_size", baton->webpMinSize)
|
||||
->set("mixed", baton->webpMixed)
|
||||
->set("exact", baton->webpExact)
|
||||
->set("alpha_q", baton->webpAlphaQuality)));
|
||||
baton->bufferOut = static_cast<char*>(area->data);
|
||||
baton->bufferOutLength = area->length;
|
||||
@@ -1009,6 +1007,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("Q", baton->tiffQuality)
|
||||
->set("bitdepth", baton->tiffBitdepth)
|
||||
->set("compression", baton->tiffCompression)
|
||||
->set("bigtiff", baton->tiffBigtiff)
|
||||
->set("miniswhite", baton->tiffMiniswhite)
|
||||
->set("predictor", baton->tiffPredictor)
|
||||
->set("pyramid", baton->tiffPyramid)
|
||||
@@ -1034,6 +1033,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("compression", baton->heifCompression)
|
||||
->set("effort", baton->heifEffort)
|
||||
->set("bitdepth", baton->heifBitdepth)
|
||||
->set("tune", baton->heifTune.c_str())
|
||||
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
||||
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||
->set("lossless", baton->heifLossless)));
|
||||
@@ -1086,20 +1086,19 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Get raw image data
|
||||
baton->bufferOut = static_cast<char*>(image.write_to_memory(&baton->bufferOutLength));
|
||||
if (baton->bufferOut == nullptr) {
|
||||
(baton->err).append("Could not allocate enough memory for raw output");
|
||||
return Error();
|
||||
throw std::runtime_error("Could not allocate enough memory for raw output");
|
||||
}
|
||||
baton->formatOut = "raw";
|
||||
} else {
|
||||
// Unsupported output format
|
||||
(baton->err).append("Unsupported output format ");
|
||||
auto unsupported = std::string("Unsupported output format ");
|
||||
if (baton->formatOut == "input") {
|
||||
(baton->err).append("when trying to match input format of ");
|
||||
(baton->err).append(ImageTypeId(inputImageType));
|
||||
unsupported.append("when trying to match input format of ");
|
||||
unsupported.append(ImageTypeId(inputImageType));
|
||||
} else {
|
||||
(baton->err).append(baton->formatOut);
|
||||
unsupported.append(baton->formatOut);
|
||||
}
|
||||
return Error();
|
||||
throw std::runtime_error(unsupported);
|
||||
}
|
||||
} else {
|
||||
// File output
|
||||
@@ -1178,6 +1177,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("effort", baton->webpEffort)
|
||||
->set("min_size", baton->webpMinSize)
|
||||
->set("mixed", baton->webpMixed)
|
||||
->set("exact", baton->webpExact)
|
||||
->set("alpha_q", baton->webpAlphaQuality));
|
||||
baton->formatOut = "webp";
|
||||
} else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
|
||||
@@ -1211,6 +1211,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("Q", baton->tiffQuality)
|
||||
->set("bitdepth", baton->tiffBitdepth)
|
||||
->set("compression", baton->tiffCompression)
|
||||
->set("bigtiff", baton->tiffBigtiff)
|
||||
->set("miniswhite", baton->tiffMiniswhite)
|
||||
->set("predictor", baton->tiffPredictor)
|
||||
->set("pyramid", baton->tiffPyramid)
|
||||
@@ -1232,6 +1233,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("compression", baton->heifCompression)
|
||||
->set("effort", baton->heifEffort)
|
||||
->set("bitdepth", baton->heifBitdepth)
|
||||
->set("tune", baton->heifTune.c_str())
|
||||
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
||||
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
||||
->set("lossless", baton->heifLossless));
|
||||
@@ -1271,12 +1273,17 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
return Error();
|
||||
}
|
||||
}
|
||||
} catch (vips::VError const &err) {
|
||||
} catch (std::runtime_error const &err) {
|
||||
char const *what = err.what();
|
||||
if (what && what[0]) {
|
||||
(baton->err).append(what);
|
||||
} else {
|
||||
(baton->err).append("Unknown error");
|
||||
if (baton->input->failOn == VIPS_FAIL_ON_WARNING) {
|
||||
(baton->err).append("Warning treated as error due to failOn setting");
|
||||
baton->errUseWarning = true;
|
||||
} else {
|
||||
(baton->err).append("Unknown error");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clean up libvips' per-request data and threads
|
||||
@@ -1291,10 +1298,13 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Handle warnings
|
||||
std::string warning = sharp::VipsWarningPop();
|
||||
while (!warning.empty()) {
|
||||
debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
|
||||
if (baton->errUseWarning) {
|
||||
(baton->err).append("\n").append(warning);
|
||||
} else {
|
||||
debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
|
||||
}
|
||||
warning = sharp::VipsWarningPop();
|
||||
}
|
||||
|
||||
if (baton->err.empty()) {
|
||||
int width = baton->width;
|
||||
int height = baton->height;
|
||||
@@ -1337,12 +1347,21 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
}
|
||||
|
||||
if (baton->bufferOutLength > 0) {
|
||||
// Add buffer size to info
|
||||
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
|
||||
// Pass ownership of output data to Buffer instance
|
||||
Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
|
||||
baton->bufferOutLength, sharp::FreeCallback);
|
||||
Callback().Call(Receiver().Value(), { env.Null(), data, info });
|
||||
if (baton->typedArrayOut) {
|
||||
// ECMAScript ArrayBuffer with Uint8Array view
|
||||
Napi::ArrayBuffer ab = Napi::ArrayBuffer::New(env, baton->bufferOutLength);
|
||||
memcpy(ab.Data(), baton->bufferOut, baton->bufferOutLength);
|
||||
sharp::FreeCallback(static_cast<char*>(baton->bufferOut), nullptr);
|
||||
Napi::TypedArrayOf<uint8_t> data = Napi::TypedArrayOf<uint8_t>::New(env,
|
||||
baton->bufferOutLength, ab, 0, napi_uint8_array);
|
||||
Callback().Call(Receiver().Value(), { env.Null(), data, info });
|
||||
} else {
|
||||
// Node.js Buffer
|
||||
Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
|
||||
baton->bufferOutLength, sharp::FreeCallback);
|
||||
Callback().Call(Receiver().Value(), { env.Null(), data, info });
|
||||
}
|
||||
} else {
|
||||
// Add file size to info
|
||||
if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
|
||||
@@ -1386,7 +1405,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
void MultiPageUnsupported(int const pages, std::string op) {
|
||||
if (pages > 1) {
|
||||
throw vips::VError(op + " is not supported for multi-page images");
|
||||
throw std::runtime_error(op + " is not supported for multi-page images");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1435,11 +1454,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
std::string
|
||||
AssembleSuffixString(std::string extname, std::vector<std::pair<std::string, std::string>> options) {
|
||||
std::string argument;
|
||||
for (auto const &option : options) {
|
||||
for (const auto& [key, value] : options) {
|
||||
if (!argument.empty()) {
|
||||
argument += ",";
|
||||
}
|
||||
argument += option.first + "=" + option.second;
|
||||
argument += key + "=" + value;
|
||||
}
|
||||
return extname + "[" + argument + "]";
|
||||
}
|
||||
@@ -1469,6 +1488,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
{"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
|
||||
{"min_size", baton->webpMinSize ? "true" : "false"},
|
||||
{"mixed", baton->webpMixed ? "true" : "false"},
|
||||
{"exact", baton->webpExact ? "true" : "false"},
|
||||
{"effort", std::to_string(baton->webpEffort)}
|
||||
};
|
||||
suffix = AssembleSuffixString(".webp", options);
|
||||
@@ -1615,6 +1635,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->trimBackground = sharp::AttrAsVectorOfDouble(options, "trimBackground");
|
||||
baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
|
||||
baton->trimLineArt = sharp::AttrAsBool(options, "trimLineArt");
|
||||
baton->trimMargin = sharp::AttrAsUint32(options, "trimMargin");
|
||||
baton->gamma = sharp::AttrAsDouble(options, "gamma");
|
||||
baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
|
||||
baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
|
||||
@@ -1692,6 +1713,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
// Output
|
||||
baton->formatOut = sharp::AttrAsStr(options, "formatOut");
|
||||
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
||||
baton->typedArrayOut = sharp::AttrAsBool(options, "typedArrayOut");
|
||||
baton->keepMetadata = sharp::AttrAsUint32(options, "keepMetadata");
|
||||
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
||||
baton->withMetadataDensity = sharp::AttrAsDouble(options, "withMetadataDensity");
|
||||
@@ -1706,6 +1728,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
}
|
||||
baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
|
||||
baton->withXmp = sharp::AttrAsStr(options, "withXmp");
|
||||
baton->withGainMap = sharp::AttrAsBool(options, "withGainMap");
|
||||
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
||||
baton->loop = sharp::AttrAsUint32(options, "loop");
|
||||
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
|
||||
@@ -1741,6 +1764,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
|
||||
baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
|
||||
baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
|
||||
baton->webpExact = sharp::AttrAsBool(options, "webpExact");
|
||||
baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
|
||||
baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
|
||||
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
||||
@@ -1750,6 +1774,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->gifReuse = sharp::AttrAsBool(options, "gifReuse");
|
||||
baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
|
||||
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
||||
baton->tiffBigtiff = sharp::AttrAsBool(options, "tiffBigtiff");
|
||||
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
|
||||
baton->tiffMiniswhite = sharp::AttrAsBool(options, "tiffMiniswhite");
|
||||
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
|
||||
@@ -1774,6 +1799,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
|
||||
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
||||
baton->heifBitdepth = sharp::AttrAsUint32(options, "heifBitdepth");
|
||||
baton->heifTune = sharp::AttrAsStr(options, "heifTune");
|
||||
baton->jxlDistance = sharp::AttrAsDouble(options, "jxlDistance");
|
||||
baton->jxlDecodingTier = sharp::AttrAsUint32(options, "jxlDecodingTier");
|
||||
baton->jxlEffort = sharp::AttrAsUint32(options, "jxlEffort");
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef SRC_PIPELINE_H_
|
||||
#define SRC_PIPELINE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <napi.h>
|
||||
#include <vips/vips8>
|
||||
@@ -46,6 +48,7 @@ struct PipelineBaton {
|
||||
size_t bufferOutLength;
|
||||
int pageHeightOut;
|
||||
int pagesOut;
|
||||
bool typedArrayOut;
|
||||
std::vector<Composite *> composite;
|
||||
std::vector<sharp::InputDescriptor *> joinChannelIn;
|
||||
int topOffsetPre;
|
||||
@@ -99,6 +102,7 @@ struct PipelineBaton {
|
||||
bool trimLineArt;
|
||||
int trimOffsetLeft;
|
||||
int trimOffsetTop;
|
||||
int trimMargin;
|
||||
std::vector<double> linearA;
|
||||
std::vector<double> linearB;
|
||||
int dilateWidth;
|
||||
@@ -165,6 +169,7 @@ struct PipelineBaton {
|
||||
int webpEffort;
|
||||
bool webpMinSize;
|
||||
bool webpMixed;
|
||||
bool webpExact;
|
||||
int gifBitdepth;
|
||||
int gifEffort;
|
||||
double gifDither;
|
||||
@@ -175,6 +180,7 @@ struct PipelineBaton {
|
||||
bool gifProgressive;
|
||||
int tiffQuality;
|
||||
VipsForeignTiffCompression tiffCompression;
|
||||
bool tiffBigtiff;
|
||||
VipsForeignTiffPredictor tiffPredictor;
|
||||
bool tiffPyramid;
|
||||
int tiffBitdepth;
|
||||
@@ -191,12 +197,14 @@ struct PipelineBaton {
|
||||
std::string heifChromaSubsampling;
|
||||
bool heifLossless;
|
||||
int heifBitdepth;
|
||||
std::string heifTune;
|
||||
double jxlDistance;
|
||||
int jxlDecodingTier;
|
||||
int jxlEffort;
|
||||
bool jxlLossless;
|
||||
VipsBandFormat rawDepth;
|
||||
std::string err;
|
||||
bool errUseWarning;
|
||||
int keepMetadata;
|
||||
int withMetadataOrientation;
|
||||
double withMetadataDensity;
|
||||
@@ -204,6 +212,7 @@ struct PipelineBaton {
|
||||
std::unordered_map<std::string, std::string> withExif;
|
||||
bool withExifMerge;
|
||||
std::string withXmp;
|
||||
bool withGainMap;
|
||||
int timeoutSeconds;
|
||||
std::vector<double> convKernel;
|
||||
int convKernelWidth;
|
||||
@@ -238,6 +247,7 @@ struct PipelineBaton {
|
||||
bufferOutLength(0),
|
||||
pageHeightOut(0),
|
||||
pagesOut(0),
|
||||
typedArrayOut(false),
|
||||
topOffsetPre(-1),
|
||||
topOffsetPost(-1),
|
||||
channels(0),
|
||||
@@ -277,6 +287,7 @@ struct PipelineBaton {
|
||||
trimLineArt(false),
|
||||
trimOffsetLeft(0),
|
||||
trimOffsetTop(0),
|
||||
trimMargin(0),
|
||||
linearA{},
|
||||
linearB{},
|
||||
dilateWidth(0),
|
||||
@@ -340,6 +351,7 @@ struct PipelineBaton {
|
||||
webpEffort(4),
|
||||
webpMinSize(false),
|
||||
webpMixed(false),
|
||||
webpExact(false),
|
||||
gifBitdepth(8),
|
||||
gifEffort(7),
|
||||
gifDither(1.0),
|
||||
@@ -350,9 +362,10 @@ struct PipelineBaton {
|
||||
gifProgressive(false),
|
||||
tiffQuality(80),
|
||||
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
||||
tiffBigtiff(false),
|
||||
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
||||
tiffPyramid(false),
|
||||
tiffBitdepth(8),
|
||||
tiffBitdepth(0),
|
||||
tiffMiniswhite(false),
|
||||
tiffTile(false),
|
||||
tiffTileHeight(256),
|
||||
@@ -366,15 +379,18 @@ struct PipelineBaton {
|
||||
heifChromaSubsampling("4:4:4"),
|
||||
heifLossless(false),
|
||||
heifBitdepth(8),
|
||||
heifTune("ssim"),
|
||||
jxlDistance(1.0),
|
||||
jxlDecodingTier(0),
|
||||
jxlEffort(7),
|
||||
jxlLossless(false),
|
||||
rawDepth(VIPS_FORMAT_UCHAR),
|
||||
errUseWarning(false),
|
||||
keepMetadata(0),
|
||||
withMetadataOrientation(-1),
|
||||
withMetadataDensity(0.0),
|
||||
withExifMerge(true),
|
||||
withGainMap(false),
|
||||
timeoutSeconds(0),
|
||||
convKernelWidth(0),
|
||||
convKernelHeight(0),
|
||||
|
||||
18
src/sharp.cc
18
src/sharp.cc
@@ -1,16 +1,18 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <mutex> // NOLINT(build/c++11)
|
||||
#include <mutex>
|
||||
|
||||
#include <napi.h>
|
||||
#include <vips/vips8>
|
||||
|
||||
#include "common.h"
|
||||
#include "metadata.h"
|
||||
#include "pipeline.h"
|
||||
#include "utilities.h"
|
||||
#include "stats.h"
|
||||
#include "./common.h"
|
||||
#include "./metadata.h"
|
||||
#include "./pipeline.h"
|
||||
#include "./stats.h"
|
||||
#include "./utilities.h"
|
||||
|
||||
Napi::Object init(Napi::Env env, Napi::Object exports) {
|
||||
static std::once_flag sharp_vips_init_once;
|
||||
|
||||
20
src/stats.cc
20
src/stats.cc
@@ -1,15 +1,18 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <napi.h>
|
||||
#include <vips/vips8>
|
||||
|
||||
#include "common.h"
|
||||
#include "stats.h"
|
||||
#include "./common.h"
|
||||
#include "./stats.h"
|
||||
|
||||
class StatsWorker : public Napi::AsyncWorker {
|
||||
public:
|
||||
@@ -36,7 +39,7 @@ class StatsWorker : public Napi::AsyncWorker {
|
||||
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
|
||||
try {
|
||||
std::tie(image, imageType) = OpenInput(baton->input);
|
||||
} catch (vips::VError const &err) {
|
||||
} catch (std::runtime_error const &err) {
|
||||
(baton->err).append(err.what());
|
||||
}
|
||||
if (imageType != sharp::ImageType::UNKNOWN) {
|
||||
@@ -89,7 +92,7 @@ class StatsWorker : public Napi::AsyncWorker {
|
||||
baton->dominantRed = dx * 16 + 8;
|
||||
baton->dominantGreen = dy * 16 + 8;
|
||||
baton->dominantBlue = dz * 16 + 8;
|
||||
} catch (vips::VError const &err) {
|
||||
} catch (std::runtime_error const &err) {
|
||||
(baton->err).append(err.what());
|
||||
}
|
||||
}
|
||||
@@ -109,7 +112,6 @@ class StatsWorker : public Napi::AsyncWorker {
|
||||
debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
|
||||
warning = sharp::VipsWarningPop();
|
||||
}
|
||||
|
||||
if (baton->err.empty()) {
|
||||
// Stats Object
|
||||
Napi::Object info = Napi::Object::New(env);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef SRC_STATS_H_
|
||||
#define SRC_STATS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <napi.h>
|
||||
|
||||
#include "./common.h"
|
||||
@@ -24,7 +27,7 @@ struct ChannelStats {
|
||||
|
||||
ChannelStats(int minVal, int maxVal, double sumVal, double squaresSumVal,
|
||||
double meanVal, double stdevVal, int minXVal, int minYVal, int maxXVal, int maxYVal):
|
||||
min(minVal), max(maxVal), sum(sumVal), squaresSum(squaresSumVal),
|
||||
min(minVal), max(maxVal), sum(sumVal), squaresSum(squaresSumVal), // NOLINT(build/include_what_you_use)
|
||||
mean(meanVal), stdev(stdevVal), minX(minXVal), minY(minYVal), maxX(maxXVal), maxY(maxYVal) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include <napi.h>
|
||||
#include <vips/vips8>
|
||||
#include <vips/vector.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "operations.h"
|
||||
#include "utilities.h"
|
||||
#include "./common.h"
|
||||
#include "./operations.h"
|
||||
#include "./utilities.h"
|
||||
|
||||
/*
|
||||
Get and set cache limits
|
||||
@@ -121,6 +123,7 @@ Napi::Value format(const Napi::CallbackInfo& info) {
|
||||
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz",
|
||||
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl", "rad", "dcraw"
|
||||
}) {
|
||||
std::string id = f == "jp2k" ? "jp2" : f;
|
||||
// Input
|
||||
const VipsObjectClass *oc = vips_class_find("VipsOperation", (f + "load").c_str());
|
||||
Napi::Boolean hasInputFile = Napi::Boolean::New(env, oc);
|
||||
@@ -152,11 +155,11 @@ Napi::Value format(const Napi::CallbackInfo& info) {
|
||||
output.Set("stream", hasOutputBuffer);
|
||||
// Other attributes
|
||||
Napi::Object container = Napi::Object::New(env);
|
||||
container.Set("id", f);
|
||||
container.Set("id", id);
|
||||
container.Set("input", input);
|
||||
container.Set("output", output);
|
||||
// Add to set of formats
|
||||
format.Set(f, container);
|
||||
format.Set(id, container);
|
||||
}
|
||||
|
||||
// Raw, uncompressed data
|
||||
@@ -241,7 +244,7 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
|
||||
}
|
||||
// Calculate colour distance
|
||||
maxColourDistance = image1.dE00(image2).max();
|
||||
} catch (vips::VError const &err) {
|
||||
} catch (std::runtime_error const &err) {
|
||||
throw Napi::Error::New(env, err.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef SRC_UTILITIES_H_
|
||||
#define SRC_UTILITIES_H_
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
|
||||
const sharp = require('../');
|
||||
|
||||
const usingCache = !process.env.G_DEBUG;
|
||||
const usingSimd = !process.env.VIPS_NOVECTOR;
|
||||
const concurrency = Number(process.env.VIPS_CONCURRENCY) || 0;
|
||||
|
||||
exports.mochaHooks = {
|
||||
beforeEach () {
|
||||
sharp.cache(usingCache);
|
||||
sharp.simd(usingSimd);
|
||||
sharp.concurrency(concurrency);
|
||||
},
|
||||
|
||||
afterEach () {
|
||||
if (global.gc) {
|
||||
global.gc();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -15,7 +15,7 @@ RUN apt-get install -y imagemagick libmagick++-dev graphicsmagick
|
||||
# Install sharp
|
||||
RUN mkdir /tmp/sharp
|
||||
RUN cd /tmp && git clone --single-branch --branch $BRANCH https://github.com/lovell/sharp.git
|
||||
RUN cd /tmp/sharp && npm install --build-from-source
|
||||
RUN cd /tmp/sharp && npm install && npm run build
|
||||
|
||||
# Install benchmark test
|
||||
RUN cd /tmp/sharp/test/bench && npm install --omit optional
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
process.env.UV_THREADPOOL_SIZE = 64;
|
||||
|
||||
const assert = require('assert');
|
||||
const assert = require('node:assert');
|
||||
const async = require('async');
|
||||
|
||||
const sharp = require('../../');
|
||||
@@ -16,32 +16,29 @@ const height = 480;
|
||||
|
||||
sharp.concurrency(1);
|
||||
|
||||
const timer = setInterval(function () {
|
||||
const timer = setInterval(() => {
|
||||
console.dir(sharp.counters());
|
||||
}, 100);
|
||||
|
||||
async.mapSeries([1, 1, 2, 4, 8, 16, 32, 64], function (parallelism, next) {
|
||||
const start = new Date().getTime();
|
||||
async.mapSeries([1, 1, 2, 4, 8, 16, 32, 64], (parallelism, next) => {
|
||||
const start = Date.now();
|
||||
async.times(parallelism,
|
||||
function (id, callback) {
|
||||
/* jslint unused: false */
|
||||
sharp(fixtures.inputJpg).resize(width, height).toBuffer(function (err, buffer) {
|
||||
(_id, callback) => {
|
||||
sharp(fixtures.inputJpg).resize(width, height).toBuffer((err, buffer) => {
|
||||
buffer = null;
|
||||
callback(err, new Date().getTime() - start);
|
||||
callback(err, Date.now() - start);
|
||||
});
|
||||
},
|
||||
function (err, ids) {
|
||||
(err, ids) => {
|
||||
assert(!err);
|
||||
assert(ids.length === parallelism);
|
||||
ids.sort();
|
||||
const mean = ids.reduce(function (a, b) {
|
||||
return a + b;
|
||||
}) / ids.length;
|
||||
console.log(parallelism + ' parallel calls: fastest=' + ids[0] + 'ms slowest=' + ids[ids.length - 1] + 'ms mean=' + mean + 'ms');
|
||||
const mean = ids.reduce((a, b) => a + b) / ids.length;
|
||||
console.log(`${parallelism} parallel calls: fastest=${ids[0]}ms slowest=${ids[ids.length - 1]}ms mean=${mean}ms`);
|
||||
next();
|
||||
}
|
||||
);
|
||||
}, function () {
|
||||
}, () => {
|
||||
clearInterval(timer);
|
||||
console.dir(sharp.counters());
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('node:fs');
|
||||
const { execSync } = require('node:child_process');
|
||||
|
||||
const async = require('async');
|
||||
const Benchmark = require('benchmark');
|
||||
@@ -12,7 +12,7 @@ const Benchmark = require('benchmark');
|
||||
const safeRequire = (name) => {
|
||||
try {
|
||||
return require(name);
|
||||
} catch (err) {}
|
||||
} catch (_err) {}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -45,13 +45,13 @@ console.log(`Detected ${physicalCores} physical cores`);
|
||||
sharp.concurrency(physicalCores);
|
||||
|
||||
async.series({
|
||||
jpeg: function (callback) {
|
||||
jpeg: (callback) => {
|
||||
const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
|
||||
const jpegSuite = new Benchmark.Suite('jpeg');
|
||||
// jimp
|
||||
jpegSuite.add('jimp-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: async function (deferred) {
|
||||
fn: async (deferred) => {
|
||||
const image = await Jimp.read(inputJpgBuffer);
|
||||
await image
|
||||
.resize({ w: width, h: height, mode: Jimp.RESIZE_BICUBIC })
|
||||
@@ -60,7 +60,7 @@ async.series({
|
||||
}
|
||||
}).add('jimp-file-file', {
|
||||
defer: true,
|
||||
fn: async function (deferred) {
|
||||
fn: async (deferred) => {
|
||||
const image = await Jimp.read(fixtures.inputJpg);
|
||||
await image
|
||||
.resize({ w: width, h: height, mode: Jimp.RESIZE_BICUBIC })
|
||||
@@ -71,14 +71,14 @@ async.series({
|
||||
// mapnik
|
||||
mapnik && jpegSuite.add('mapnik-file-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
mapnik.Image.open(fixtures.inputJpg, function (err, img) {
|
||||
fn: (deferred) => {
|
||||
mapnik.Image.open(fixtures.inputJpg, (err, img) => {
|
||||
if (err) throw err;
|
||||
img
|
||||
.resize(width, height, {
|
||||
scaling_method: mapnik.imageScaling.lanczos
|
||||
})
|
||||
.save(outputJpg, 'jpeg:quality=80', function (err) {
|
||||
.save(outputJpg, 'jpeg:quality=80', (err) => {
|
||||
if (err) throw err;
|
||||
deferred.resolve();
|
||||
});
|
||||
@@ -86,14 +86,14 @@ async.series({
|
||||
}
|
||||
}).add('mapnik-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
mapnik.Image.fromBytes(inputJpgBuffer, { max_size: 3000 }, function (err, img) {
|
||||
fn: (deferred) => {
|
||||
mapnik.Image.fromBytes(inputJpgBuffer, { max_size: 3000 }, (err, img) => {
|
||||
if (err) throw err;
|
||||
img
|
||||
.resize(width, height, {
|
||||
scaling_method: mapnik.imageScaling.lanczos
|
||||
})
|
||||
.encode('jpeg:quality=80', function (err) {
|
||||
.encode('jpeg:quality=80', (err) => {
|
||||
if (err) throw err;
|
||||
deferred.resolve();
|
||||
});
|
||||
@@ -103,7 +103,7 @@ async.series({
|
||||
// imagemagick
|
||||
jpegSuite.add('imagemagick-file-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
imagemagick.resize({
|
||||
srcPath: fixtures.inputJpg,
|
||||
dstPath: outputJpg,
|
||||
@@ -112,7 +112,7 @@ async.series({
|
||||
height,
|
||||
format: 'jpg',
|
||||
filter: 'Lanczos'
|
||||
}, function (err) {
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -124,12 +124,12 @@ async.series({
|
||||
// gm
|
||||
jpegSuite.add('gm-buffer-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
gm(inputJpgBuffer)
|
||||
.filter('Lanczos')
|
||||
.resize(width, height)
|
||||
.quality(80)
|
||||
.write(outputJpg, function (err) {
|
||||
.write(outputJpg, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -139,12 +139,12 @@ async.series({
|
||||
}
|
||||
}).add('gm-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
gm(inputJpgBuffer)
|
||||
.filter('Lanczos')
|
||||
.resize(width, height)
|
||||
.quality(80)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -154,12 +154,12 @@ async.series({
|
||||
}
|
||||
}).add('gm-file-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
gm(fixtures.inputJpg)
|
||||
.filter('Lanczos')
|
||||
.resize(width, height)
|
||||
.quality(80)
|
||||
.write(outputJpg, function (err) {
|
||||
.write(outputJpg, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -169,12 +169,12 @@ async.series({
|
||||
}
|
||||
}).add('gm-file-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
gm(fixtures.inputJpg)
|
||||
.filter('Lanczos')
|
||||
.resize(width, height)
|
||||
.quality(80)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -186,17 +186,17 @@ async.series({
|
||||
// tfjs
|
||||
tfjs && jpegSuite.add('tfjs-node-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
const decoded = tfjs.node.decodeJpeg(inputJpgBuffer);
|
||||
const resized = tfjs.image.resizeBilinear(decoded, [height, width]);
|
||||
tfjs
|
||||
.node
|
||||
.encodeJpeg(resized, 'rgb', 80)
|
||||
.then(function () {
|
||||
.then(() => {
|
||||
deferred.resolve();
|
||||
tfjs.disposeVariables();
|
||||
})
|
||||
.catch(function (err) {
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
@@ -204,10 +204,10 @@ async.series({
|
||||
// sharp
|
||||
jpegSuite.add('sharp-buffer-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.toFile(outputJpg, function (err) {
|
||||
.toFile(outputJpg, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -217,10 +217,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -228,12 +228,25 @@ async.series({
|
||||
}
|
||||
});
|
||||
}
|
||||
}).add('sharp-buffer-uint8array', {
|
||||
defer: true,
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.toUint8Array()
|
||||
.then(() => {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}).add('sharp-file-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(width, height)
|
||||
.toFile(outputJpg, function (err) {
|
||||
.toFile(outputJpg, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -243,10 +256,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-stream-stream', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
const readable = fs.createReadStream(fixtures.inputJpg);
|
||||
const writable = fs.createWriteStream(outputJpg);
|
||||
writable.on('finish', function () {
|
||||
writable.on('finish', () => {
|
||||
deferred.resolve();
|
||||
});
|
||||
const pipeline = sharp()
|
||||
@@ -255,10 +268,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-file-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(width, height)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -266,36 +279,49 @@ async.series({
|
||||
}
|
||||
});
|
||||
}
|
||||
}).add('sharp-promise', {
|
||||
}).add('sharp-file-uint8array', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
sharp(inputJpgBuffer)
|
||||
fn: (deferred) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(width, height)
|
||||
.toBuffer()
|
||||
.then(function () {
|
||||
.toUint8Array()
|
||||
.then(() => {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch(function (err) {
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}).on('cycle', function (event) {
|
||||
console.log('jpeg ' + String(event.target));
|
||||
}).add('sharp-promise', {
|
||||
defer: true,
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.toBuffer()
|
||||
.then(() => {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}).on('cycle', (event) => {
|
||||
console.log(`jpeg ${String(event.target)}`);
|
||||
}).on('complete', function () {
|
||||
callback(null, this.filter('fastest').map('name'));
|
||||
}).run();
|
||||
},
|
||||
// Effect of applying operations
|
||||
operations: function (callback) {
|
||||
operations: (callback) => {
|
||||
const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
|
||||
const operationsSuite = new Benchmark.Suite('operations');
|
||||
operationsSuite.add('sharp-sharpen-mild', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.sharpen()
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -305,11 +331,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-sharpen-radius', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.sharpen(3, 1, 3)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -319,11 +345,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-blur-mild', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.blur()
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -333,11 +359,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-blur-radius', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.blur(3)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -347,11 +373,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-gamma', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.gamma()
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -361,11 +387,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-normalise', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.normalise()
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -375,11 +401,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-greyscale', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.greyscale()
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -389,12 +415,12 @@ async.series({
|
||||
}
|
||||
}).add('sharp-greyscale-gamma', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.gamma()
|
||||
.greyscale()
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -404,11 +430,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-progressive', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.jpeg({ progressive: true })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -418,11 +444,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-without-chroma-subsampling', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.jpeg({ chromaSubsampling: '4:4:4' })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -432,11 +458,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-rotate', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.rotate(90)
|
||||
.resize(width, height)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -446,11 +472,11 @@ async.series({
|
||||
}
|
||||
}).add('sharp-without-simd', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp.simd(false);
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
sharp.simd(true);
|
||||
if (err) {
|
||||
throw err;
|
||||
@@ -461,10 +487,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-random-access-read', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer, { sequentialRead: false })
|
||||
.resize(width, height)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -474,13 +500,13 @@ async.series({
|
||||
}
|
||||
}).add('sharp-crop-entropy', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height, {
|
||||
fit: 'cover',
|
||||
position: sharp.strategy.entropy
|
||||
})
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -490,13 +516,13 @@ async.series({
|
||||
}
|
||||
}).add('sharp-crop-attention', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height, {
|
||||
fit: 'cover',
|
||||
position: sharp.strategy.attention
|
||||
})
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -504,21 +530,21 @@ async.series({
|
||||
}
|
||||
});
|
||||
}
|
||||
}).on('cycle', function (event) {
|
||||
console.log('operations ' + String(event.target));
|
||||
}).on('cycle', (event) => {
|
||||
console.log(`operations ${String(event.target)}`);
|
||||
}).on('complete', function () {
|
||||
callback(null, this.filter('fastest').map('name'));
|
||||
}).run();
|
||||
},
|
||||
// Comparative speed of kernels
|
||||
kernels: function (callback) {
|
||||
kernels: (callback) => {
|
||||
const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
|
||||
(new Benchmark.Suite('kernels')).add('sharp-cubic', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height, { kernel: 'cubic' })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -528,10 +554,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-lanczos2', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height, { kernel: 'lanczos2' })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -541,10 +567,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-lanczos3', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height, { kernel: 'lanczos3' })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -554,10 +580,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-mks2013', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height, { kernel: 'mks2013' })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -567,10 +593,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-mks2021', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height, { kernel: 'mks2021' })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -578,21 +604,21 @@ async.series({
|
||||
}
|
||||
});
|
||||
}
|
||||
}).on('cycle', function (event) {
|
||||
console.log('kernels ' + String(event.target));
|
||||
}).on('cycle', (event) => {
|
||||
console.log(`kernels ${String(event.target)}`);
|
||||
}).on('complete', function () {
|
||||
callback(null, this.filter('fastest').map('name'));
|
||||
}).run();
|
||||
},
|
||||
// PNG
|
||||
png: function (callback) {
|
||||
png: (callback) => {
|
||||
const inputPngBuffer = fs.readFileSync(fixtures.inputPngAlphaPremultiplicationLarge);
|
||||
const pngSuite = new Benchmark.Suite('png');
|
||||
const minSamples = 64;
|
||||
// jimp
|
||||
pngSuite.add('jimp-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: async function (deferred) {
|
||||
fn: async (deferred) => {
|
||||
const image = await Jimp.read(inputPngBuffer);
|
||||
await image
|
||||
.resize({ w: width, h: heightPng, mode: Jimp.RESIZE_BICUBIC })
|
||||
@@ -601,7 +627,7 @@ async.series({
|
||||
}
|
||||
}).add('jimp-file-file', {
|
||||
defer: true,
|
||||
fn: async function (deferred) {
|
||||
fn: async (deferred) => {
|
||||
const image = await Jimp.read(fixtures.inputPngAlphaPremultiplicationLarge);
|
||||
await image
|
||||
.resize({ w: width, h: heightPng, mode: Jimp.RESIZE_BICUBIC })
|
||||
@@ -612,18 +638,18 @@ async.series({
|
||||
// mapnik
|
||||
mapnik && pngSuite.add('mapnik-file-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
mapnik.Image.open(fixtures.inputPngAlphaPremultiplicationLarge, function (err, img) {
|
||||
fn: (deferred) => {
|
||||
mapnik.Image.open(fixtures.inputPngAlphaPremultiplicationLarge, (err, img) => {
|
||||
if (err) throw err;
|
||||
img.premultiply(function (err, img) {
|
||||
img.premultiply((err, img) => {
|
||||
if (err) throw err;
|
||||
img.resize(width, heightPng, {
|
||||
scaling_method: mapnik.imageScaling.lanczos
|
||||
}, function (err, img) {
|
||||
}, (err, img) => {
|
||||
if (err) throw err;
|
||||
img.demultiply(function (err, img) {
|
||||
img.demultiply((err, img) => {
|
||||
if (err) throw err;
|
||||
img.save(outputPng, 'png32:f=no:z=6', function (err) {
|
||||
img.save(outputPng, 'png32:f=no:z=6', (err) => {
|
||||
if (err) throw err;
|
||||
deferred.resolve();
|
||||
});
|
||||
@@ -634,18 +660,18 @@ async.series({
|
||||
}
|
||||
}).add('mapnik-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
mapnik.Image.fromBytes(inputPngBuffer, { max_size: 3000 }, function (err, img) {
|
||||
fn: (deferred) => {
|
||||
mapnik.Image.fromBytes(inputPngBuffer, { max_size: 3000 }, (err, img) => {
|
||||
if (err) throw err;
|
||||
img.premultiply(function (err, img) {
|
||||
img.premultiply((err, img) => {
|
||||
if (err) throw err;
|
||||
img.resize(width, heightPng, {
|
||||
scaling_method: mapnik.imageScaling.lanczos
|
||||
}, function (err, img) {
|
||||
}, (err, img) => {
|
||||
if (err) throw err;
|
||||
img.demultiply(function (err, img) {
|
||||
img.demultiply((err, img) => {
|
||||
if (err) throw err;
|
||||
img.encode('png32:f=no:z=6', function (err) {
|
||||
img.encode('png32:f=no:z=6', (err) => {
|
||||
if (err) throw err;
|
||||
deferred.resolve();
|
||||
});
|
||||
@@ -658,7 +684,7 @@ async.series({
|
||||
// imagemagick
|
||||
pngSuite.add('imagemagick-file-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
imagemagick.resize({
|
||||
srcPath: fixtures.inputPngAlphaPremultiplicationLarge,
|
||||
dstPath: outputPng,
|
||||
@@ -669,7 +695,7 @@ async.series({
|
||||
'-define', 'PNG:compression-level=6',
|
||||
'-define', 'PNG:compression-filter=0'
|
||||
]
|
||||
}, function (err) {
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -681,13 +707,13 @@ async.series({
|
||||
// gm
|
||||
pngSuite.add('gm-file-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
gm(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||
.filter('Lanczos')
|
||||
.resize(width, heightPng)
|
||||
.define('PNG:compression-level=6')
|
||||
.define('PNG:compression-filter=0')
|
||||
.write(outputPng, function (err) {
|
||||
.write(outputPng, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -697,13 +723,13 @@ async.series({
|
||||
}
|
||||
}).add('gm-file-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
gm(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||
.filter('Lanczos')
|
||||
.resize(width, heightPng)
|
||||
.define('PNG:compression-level=6')
|
||||
.define('PNG:compression-filter=0')
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -716,11 +742,11 @@ async.series({
|
||||
pngSuite.add('sharp-buffer-file', {
|
||||
defer: true,
|
||||
minSamples,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputPngBuffer)
|
||||
.resize(width, heightPng)
|
||||
.png({ compressionLevel: 6 })
|
||||
.toFile(outputPng, function (err) {
|
||||
.toFile(outputPng, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -731,11 +757,11 @@ async.series({
|
||||
}).add('sharp-buffer-buffer', {
|
||||
defer: true,
|
||||
minSamples,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputPngBuffer)
|
||||
.resize(width, heightPng)
|
||||
.png({ compressionLevel: 6 })
|
||||
.toBuffer(function (err, data) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -746,11 +772,11 @@ async.series({
|
||||
}).add('sharp-file-file', {
|
||||
defer: true,
|
||||
minSamples,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||
.resize(width, heightPng)
|
||||
.png({ compressionLevel: 6 })
|
||||
.toFile(outputPng, function (err) {
|
||||
.toFile(outputPng, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -761,11 +787,11 @@ async.series({
|
||||
}).add('sharp-file-buffer', {
|
||||
defer: true,
|
||||
minSamples,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||
.resize(width, heightPng)
|
||||
.png({ compressionLevel: 6 })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -776,11 +802,11 @@ async.series({
|
||||
}).add('sharp-progressive', {
|
||||
defer: true,
|
||||
minSamples,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputPngBuffer)
|
||||
.resize(width, heightPng)
|
||||
.png({ compressionLevel: 6, progressive: true })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -791,11 +817,11 @@ async.series({
|
||||
}).add('sharp-adaptiveFiltering', {
|
||||
defer: true,
|
||||
minSamples,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputPngBuffer)
|
||||
.resize(width, heightPng)
|
||||
.png({ adaptiveFiltering: true, compressionLevel: 6 })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -806,11 +832,11 @@ async.series({
|
||||
}).add('sharp-compressionLevel=9', {
|
||||
defer: true,
|
||||
minSamples,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputPngBuffer)
|
||||
.resize(width, heightPng)
|
||||
.png({ compressionLevel: 9 })
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -819,21 +845,21 @@ async.series({
|
||||
});
|
||||
}
|
||||
});
|
||||
pngSuite.on('cycle', function (event) {
|
||||
console.log(' png ' + String(event.target));
|
||||
pngSuite.on('cycle', (event) => {
|
||||
console.log(` png ${String(event.target)}`);
|
||||
}).on('complete', function () {
|
||||
callback(null, this.filter('fastest').map('name'));
|
||||
}).run();
|
||||
},
|
||||
// WebP
|
||||
webp: function (callback) {
|
||||
webp: (callback) => {
|
||||
const inputWebPBuffer = fs.readFileSync(fixtures.inputWebP);
|
||||
(new Benchmark.Suite('webp')).add('sharp-buffer-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputWebPBuffer)
|
||||
.resize(width, height)
|
||||
.toFile(outputWebP, function (err) {
|
||||
.toFile(outputWebP, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -843,10 +869,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-buffer-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(inputWebPBuffer)
|
||||
.resize(width, height)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -856,10 +882,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-file-file', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(fixtures.inputWebP)
|
||||
.resize(width, height)
|
||||
.toFile(outputWebP, function (err) {
|
||||
.toFile(outputWebP, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -869,10 +895,10 @@ async.series({
|
||||
}
|
||||
}).add('sharp-file-buffer', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(fixtures.inputWebP)
|
||||
.resize(width, height)
|
||||
.toBuffer(function (err) {
|
||||
.toBuffer((err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -880,19 +906,19 @@ async.series({
|
||||
}
|
||||
});
|
||||
}
|
||||
}).on('cycle', function (event) {
|
||||
console.log('webp ' + String(event.target));
|
||||
}).on('cycle', (event) => {
|
||||
console.log(`webp ${String(event.target)}`);
|
||||
}).on('complete', function () {
|
||||
callback(null, this.filter('fastest').map('name'));
|
||||
}).run();
|
||||
}
|
||||
}, function (err, results) {
|
||||
}, (err, results) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
Object.keys(results).forEach(function (format) {
|
||||
Object.keys(results).forEach((format) => {
|
||||
if (results[format].toString().substr(0, 5) !== 'sharp') {
|
||||
console.log('sharp was slower than ' + results[format] + ' for ' + format);
|
||||
console.log(`sharp was slower than ${results[format]} for ${format}`);
|
||||
}
|
||||
});
|
||||
console.dir(sharp.cache());
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'use strict';
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const imagemagick = require('imagemagick');
|
||||
const gm = require('gm');
|
||||
const assert = require('assert');
|
||||
const assert = require('node:assert');
|
||||
const Benchmark = require('benchmark');
|
||||
|
||||
const sharp = require('../../');
|
||||
@@ -16,13 +16,11 @@ sharp.cache(false);
|
||||
const min = 320;
|
||||
const max = 960;
|
||||
|
||||
const randomDimension = function () {
|
||||
return Math.ceil((Math.random() * (max - min)) + min);
|
||||
};
|
||||
const randomDimension = () => Math.ceil((Math.random() * (max - min)) + min);
|
||||
|
||||
new Benchmark.Suite('random').add('imagemagick', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
imagemagick.resize({
|
||||
srcPath: fixtures.inputJpg,
|
||||
dstPath: fixtures.path('output.jpg'),
|
||||
@@ -31,7 +29,7 @@ new Benchmark.Suite('random').add('imagemagick', {
|
||||
height: randomDimension(),
|
||||
format: 'jpg',
|
||||
filter: 'Lanczos'
|
||||
}, function (err) {
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -41,12 +39,12 @@ new Benchmark.Suite('random').add('imagemagick', {
|
||||
}
|
||||
}).add('gm', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
gm(fixtures.inputJpg)
|
||||
.resize(randomDimension(), randomDimension())
|
||||
.filter('Lanczos')
|
||||
.quality(80)
|
||||
.toBuffer(function (err, buffer) {
|
||||
.toBuffer((err, buffer) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -57,10 +55,10 @@ new Benchmark.Suite('random').add('imagemagick', {
|
||||
}
|
||||
}).add('sharp', {
|
||||
defer: true,
|
||||
fn: function (deferred) {
|
||||
fn: (deferred) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(randomDimension(), randomDimension())
|
||||
.toBuffer(function (err, buffer) {
|
||||
.toBuffer((err, buffer) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -69,9 +67,9 @@ new Benchmark.Suite('random').add('imagemagick', {
|
||||
}
|
||||
});
|
||||
}
|
||||
}).on('cycle', function (event) {
|
||||
}).on('cycle', (event) => {
|
||||
console.log(String(event.target));
|
||||
}).on('complete', function () {
|
||||
const winner = this.filter('fastest').map('name');
|
||||
assert.strictEqual('sharp', String(winner), 'sharp was slower than ' + winner);
|
||||
assert.strictEqual('sharp', String(winner), `sharp was slower than ${winner}`);
|
||||
}).run();
|
||||
|
||||
BIN
test/fixtures/bonne.geo.tif
vendored
Normal file
BIN
test/fixtures/bonne.geo.tif
vendored
Normal file
Binary file not shown.
BIN
test/fixtures/gain-map.jpg
vendored
Normal file
BIN
test/fixtures/gain-map.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
35
test/fixtures/index.js
vendored
35
test/fixtures/index.js
vendored
@@ -1,16 +1,14 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const path = require('node:path');
|
||||
const sharp = require('../../');
|
||||
const maxColourDistance = require('../../lib/sharp')._maxColourDistance;
|
||||
|
||||
// Helpers
|
||||
const getPath = function (filename) {
|
||||
return path.join(__dirname, filename);
|
||||
};
|
||||
const getPath = (filename) => path.join(__dirname, filename);
|
||||
|
||||
// Generates a 64-bit-as-binary-string image fingerprint
|
||||
// Based on the dHash gradient method - see http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
|
||||
@@ -22,7 +20,7 @@ async function fingerprint (image) {
|
||||
.resize(9, 8, { fit: sharp.fit.fill })
|
||||
.raw()
|
||||
.toBuffer()
|
||||
.then(function (data) {
|
||||
.then((data) => {
|
||||
let fingerprint = '';
|
||||
for (let col = 0; col < 8; col++) {
|
||||
for (let row = 0; row < 8; row++) {
|
||||
@@ -72,9 +70,11 @@ module.exports = {
|
||||
inputJpgRandom: getPath('random.jpg'), // convert -size 200x200 xc: +noise Random random.jpg
|
||||
inputJpgThRandom: getPath('thRandom.jpg'), // convert random.jpg -channel G -threshold 5% -separate +channel -negate thRandom.jpg
|
||||
inputJpgLossless: getPath('testimgl.jpg'), // Lossless JPEG from ftp://ftp.fu-berlin.de/unix/X11/graphics/ImageMagick/delegates/ljpeg-6b.tar.gz
|
||||
inputJpgWithGainMap: getPath('gain-map.jpg'), // https://github.com/libvips/libvips/issues/3799
|
||||
|
||||
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
||||
inputPngGradients: getPath('gradients-rgb8.png'),
|
||||
inputPngWithSlightGradientBorder: getPath('slight-gradient-border.png'),
|
||||
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
||||
inputPngCompleteTransparency: getPath('full-transparent.png'),
|
||||
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
|
||||
@@ -115,6 +115,7 @@ module.exports = {
|
||||
inputTiff8BitDepth: getPath('8bit_depth.tiff'),
|
||||
inputTifftagPhotoshop: getPath('tifftag-photoshop.tiff'), // https://github.com/lovell/sharp/issues/1600
|
||||
inputTiffFogra: getPath('fogra-0-100-100-0.tif'), // https://github.com/lovell/sharp/issues/4045
|
||||
inputTiffGeo: getPath('bonne.geo.tif'), // https://download.osgeo.org/geotiff/samples/intergraph
|
||||
|
||||
inputJp2: getPath('relax.jp2'), // https://www.fnordware.com/j2k/relax.jp2
|
||||
inputJp2TileParts: getPath('relax_tileparts.jp2'), // kdu_expand -i relax.jp2 -o relax-tmp.tif ; kdu_compress -i relax-tmp.tif -o relax_tileparts.jp2 -jp2_space sRGB Clayers=8 -rate 1.0,0.04 Stiles='{128,128}' ORGtparts=L ; rm relax-tmp.tif
|
||||
@@ -126,7 +127,7 @@ module.exports = {
|
||||
inputSvgSmallViewBox: getPath('circle.svg'),
|
||||
inputSvgWithEmbeddedImages: getPath('struct-image-04-t.svg'), // https://dev.w3.org/SVG/profiles/1.2T/test/svg/struct-image-04-t.svg
|
||||
inputAvif: getPath('sdr_cosmos12920_cicp1-13-6_yuv444_full_qp10.avif'), // CC by-nc-nd https://github.com/AOMediaCodec/av1-avif/tree/master/testFiles/Netflix
|
||||
|
||||
inputAvifWithPitmBox: getPath('pitm.avif'), // https://github.com/lovell/sharp/issues/4487
|
||||
inputJPGBig: getPath('flowers.jpeg'),
|
||||
|
||||
inputPngDotAndLines: getPath('dot-and-lines.png'),
|
||||
@@ -147,14 +148,12 @@ module.exports = {
|
||||
path: getPath,
|
||||
|
||||
// Path for expected output images
|
||||
expected: function (filename) {
|
||||
return getPath(path.join('expected', filename));
|
||||
},
|
||||
expected: (filename) => getPath(path.join('expected', filename)),
|
||||
|
||||
// Verify similarity of expected vs actual images via fingerprint
|
||||
// Specify distance threshold using `options={threshold: 42}`, default
|
||||
// `threshold` is 5;
|
||||
assertSimilar: async function (expectedImage, actualImage, options, callback) {
|
||||
assertSimilar: async (expectedImage, actualImage, options, callback) => {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
@@ -194,12 +193,12 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
assertMaxColourDistance: function (actualImagePath, expectedImagePath, acceptedDistance) {
|
||||
assertMaxColourDistance: (actualImagePath, expectedImagePath, acceptedDistance) => {
|
||||
if (typeof actualImagePath !== 'string') {
|
||||
throw new TypeError('`actualImagePath` must be a string; got ' + actualImagePath);
|
||||
throw new TypeError(`\`actualImagePath\` must be a string; got ${actualImagePath}`);
|
||||
}
|
||||
if (typeof expectedImagePath !== 'string') {
|
||||
throw new TypeError('`expectedImagePath` must be a string; got ' + expectedImagePath);
|
||||
throw new TypeError(`\`expectedImagePath\` must be a string; got ${expectedImagePath}`);
|
||||
}
|
||||
if (typeof acceptedDistance !== 'number') {
|
||||
// Default threshold
|
||||
@@ -207,7 +206,7 @@ module.exports = {
|
||||
}
|
||||
const distance = maxColourDistance(actualImagePath, expectedImagePath);
|
||||
if (distance > acceptedDistance) {
|
||||
throw new Error('Expected maximum absolute distance of ' + acceptedDistance + ', actual ' + distance);
|
||||
throw new Error(`Expected maximum absolute distance of ${acceptedDistance}, actual ${distance}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
test/fixtures/pitm.avif
vendored
Normal file
BIN
test/fixtures/pitm.avif
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
BIN
test/fixtures/slight-gradient-border.png
vendored
Normal file
BIN
test/fixtures/slight-gradient-border.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -18,5 +18,5 @@ for test in $TESTS; do
|
||||
--show-leak-kinds=definite,indirect \
|
||||
--num-callers=20 \
|
||||
--trace-children=yes \
|
||||
node --expose-gc --zero-fill-buffers node_modules/.bin/mocha --no-config --slow=60000 --timeout=120000 --require test/beforeEach.js "test/unit/$test";
|
||||
node --zero-fill-buffers --test "test/unit/$test";
|
||||
done
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// biome-ignore-all lint/correctness/noUnusedFunctionParameters: types only test file
|
||||
// biome-ignore-all lint/correctness/noUnusedVariables: types only test file
|
||||
|
||||
import sharp = require('../../');
|
||||
|
||||
import { createReadStream, createWriteStream } from 'fs';
|
||||
import { createReadStream, createWriteStream } from 'node:fs';
|
||||
|
||||
const input: Buffer = Buffer.alloc(0);
|
||||
const readableStream: NodeJS.ReadableStream = createReadStream(input);
|
||||
@@ -79,10 +82,13 @@ sharp({
|
||||
let transformer = sharp()
|
||||
.resize(300)
|
||||
.on('info', (info: sharp.OutputInfo) => {
|
||||
console.log('Image height is ' + info.height);
|
||||
console.log(`Image height is ${info.height}`);
|
||||
});
|
||||
readableStream.pipe(transformer).pipe(writableStream);
|
||||
|
||||
sharp().toUint8Array();
|
||||
sharp().toUint8Array().then(({ data }) => data.byteLength);
|
||||
|
||||
console.log(sharp.format);
|
||||
console.log(sharp.versions);
|
||||
|
||||
@@ -228,7 +234,7 @@ sharp(input)
|
||||
|
||||
sharp(input)
|
||||
.resize(100, 100)
|
||||
.toFormat('jpg')
|
||||
.toFormat('avif')
|
||||
.toBuffer({ resolveWithObject: false })
|
||||
.then((outputBuffer: Buffer) => {
|
||||
// Resolves with a Buffer object when resolveWithObject is false
|
||||
@@ -261,9 +267,7 @@ sharp(input)
|
||||
// Output to tif
|
||||
sharp(input)
|
||||
.resize(100, 100)
|
||||
.toFormat('tif')
|
||||
.toFormat('tiff')
|
||||
.toFormat(sharp.format.tif)
|
||||
.toFormat(sharp.format.tiff)
|
||||
.toBuffer();
|
||||
|
||||
@@ -318,7 +322,7 @@ sharp('input.gif')
|
||||
// From https://sharp.pixelplumbing.com/api-output#examples-9
|
||||
// Extract raw RGB pixel data from JPEG input
|
||||
sharp('input.jpg')
|
||||
.raw()
|
||||
.raw({ depth: 'ushort' })
|
||||
.toBuffer({ resolveWithObject: true })
|
||||
.then(({ data, info }) => {
|
||||
console.log(data);
|
||||
@@ -359,7 +363,7 @@ sharp(input)
|
||||
.avif({ quality: 50, lossless: false, effort: 5, chromaSubsampling: '4:2:0' })
|
||||
.heif()
|
||||
.heif({})
|
||||
.heif({ quality: 50, compression: 'hevc', lossless: false, effort: 5, chromaSubsampling: '4:2:0' })
|
||||
.heif({ quality: 50, compression: 'hevc', lossless: false, effort: 5, chromaSubsampling: '4:2:0', tune: 'psnr' })
|
||||
.toBuffer({ resolveWithObject: true })
|
||||
.then(({ data, info }) => {
|
||||
console.log(data);
|
||||
@@ -542,8 +546,8 @@ sharp('input.tiff').jxl({ decodingTier: 4 }).toFile('out.jxl');
|
||||
sharp('input.tiff').jxl({ lossless: true }).toFile('out.jxl');
|
||||
sharp('input.tiff').jxl({ effort: 7 }).toFile('out.jxl');
|
||||
|
||||
// Support `minSize` and `mixed` webp options
|
||||
sharp('input.tiff').webp({ minSize: true, mixed: true }).toFile('out.gif');
|
||||
// Support webp options
|
||||
sharp('input.tiff').webp({ minSize: true, mixed: true, exact: true }).toFile('out.webp');
|
||||
|
||||
// 'failOn' input param
|
||||
sharp('input.tiff', { failOn: 'none' });
|
||||
@@ -595,7 +599,7 @@ const vertexSplitQuadraticBasisSpline: string = sharp.interpolators.vertexSplitQ
|
||||
// Triming
|
||||
sharp(input).trim({ background: '#000' }).toBuffer();
|
||||
sharp(input).trim({ threshold: 10, lineArt: true }).toBuffer();
|
||||
sharp(input).trim({ background: '#bf1942', threshold: 30 }).toBuffer();
|
||||
sharp(input).trim({ background: '#bf1942', threshold: 30, margin: 20 }).toBuffer();
|
||||
|
||||
// Text input
|
||||
sharp({
|
||||
@@ -768,3 +772,35 @@ sharp().erode();
|
||||
sharp().erode(1);
|
||||
sharp().dilate();
|
||||
sharp().dilate(1);
|
||||
|
||||
sharp.format.dcraw;
|
||||
sharp.format.dz;
|
||||
sharp.format.fits;
|
||||
sharp.format.gif;
|
||||
sharp.format.heif;
|
||||
sharp.format.jp2;
|
||||
sharp.format.jpeg;
|
||||
sharp.format.jxl;
|
||||
sharp.format.magick;
|
||||
sharp.format.openslide;
|
||||
sharp.format.pdf;
|
||||
sharp.format.png;
|
||||
sharp.format.ppm;
|
||||
sharp.format.rad;
|
||||
sharp.format.raw;
|
||||
sharp.format.svg;
|
||||
sharp.format.tiff;
|
||||
sharp.format.vips;
|
||||
sharp.format.webp;
|
||||
// @ts-expect-error
|
||||
sharp.format.avif;
|
||||
// @ts-expect-error
|
||||
sharp.format.input;
|
||||
// @ts-expect-error
|
||||
sharp.format.jp2k;
|
||||
// @ts-expect-error
|
||||
sharp.format.jpg;
|
||||
// @ts-expect-error
|
||||
sharp.format.tif;
|
||||
// @ts-expect-error
|
||||
sharp.format.v;
|
||||
|
||||
16
test/unit.mjs
Normal file
16
test/unit.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { run } from 'node:test';
|
||||
import { spec } from 'node:test/reporters';
|
||||
|
||||
const files = (await readdir('./test/unit')).map((f) => `./test/unit/${f}`);
|
||||
|
||||
run({
|
||||
files,
|
||||
concurrency: true,
|
||||
timeout: 60000,
|
||||
coverage: true,
|
||||
coverageIncludeGlobs: ['lib/*.js'],
|
||||
branchCoverage: 100,
|
||||
})
|
||||
.compose(new spec())
|
||||
.pipe(process.stdout);
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
|
||||
const sharp = require('../../');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fixtures = require('../fixtures');
|
||||
const sharp = require('../../');
|
||||
|
||||
describe('Alpha transparency', function () {
|
||||
it('Flatten to black', function (done) {
|
||||
describe('Alpha transparency', () => {
|
||||
it('Flatten to black', (_t, done) => {
|
||||
sharp(fixtures.inputPngWithTransparency)
|
||||
.flatten()
|
||||
.resize(400, 300)
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(400, info.width);
|
||||
assert.strictEqual(300, info.height);
|
||||
@@ -20,14 +21,14 @@ describe('Alpha transparency', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Flatten to RGB orange', function (done) {
|
||||
it('Flatten to RGB orange', (_t, done) => {
|
||||
sharp(fixtures.inputPngWithTransparency)
|
||||
.resize(400, 300)
|
||||
.flatten({
|
||||
background: { r: 255, g: 102, b: 0 }
|
||||
})
|
||||
.jpeg({ chromaSubsampling: '4:4:4' })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(400, info.width);
|
||||
assert.strictEqual(300, info.height);
|
||||
@@ -35,12 +36,12 @@ describe('Alpha transparency', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Flatten to CSS/hex orange', function (done) {
|
||||
it('Flatten to CSS/hex orange', (_t, done) => {
|
||||
sharp(fixtures.inputPngWithTransparency)
|
||||
.resize(400, 300)
|
||||
.flatten({ background: '#ff6600' })
|
||||
.jpeg({ chromaSubsampling: '4:4:4' })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(400, info.width);
|
||||
assert.strictEqual(300, info.height);
|
||||
@@ -48,13 +49,13 @@ describe('Alpha transparency', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Flatten 16-bit PNG with transparency to orange', function (done) {
|
||||
it('Flatten 16-bit PNG with transparency to orange', (_t, done) => {
|
||||
const output = fixtures.path('output.flatten-rgb16-orange.jpg');
|
||||
sharp(fixtures.inputPngWithTransparency16bit)
|
||||
.flatten({
|
||||
background: { r: 255, g: 102, b: 0 }
|
||||
})
|
||||
.toFile(output, function (err, info) {
|
||||
.toFile(output, (err, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, info.size > 0);
|
||||
assert.strictEqual(32, info.width);
|
||||
@@ -64,10 +65,10 @@ describe('Alpha transparency', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Do not flatten', function (done) {
|
||||
it('Do not flatten', (_t, done) => {
|
||||
sharp(fixtures.inputPngWithTransparency)
|
||||
.flatten(false)
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, _data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('png', info.format);
|
||||
assert.strictEqual(4, info.channels);
|
||||
@@ -75,10 +76,10 @@ describe('Alpha transparency', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Ignored for JPEG', function (done) {
|
||||
it('Ignored for JPEG', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.flatten({ background: '#ff0000' })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, _data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(3, info.channels);
|
||||
@@ -98,68 +99,60 @@ describe('Alpha transparency', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Enlargement with non-nearest neighbor interpolation shouldn’t cause dark edges', function () {
|
||||
it('Enlargement with non-nearest neighbor interpolation shouldn’t cause dark edges', () => {
|
||||
const base = 'alpha-premultiply-enlargement-2048x1536-paper.png';
|
||||
const actual = fixtures.path('output.' + base);
|
||||
const actual = fixtures.path(`output.${base}`);
|
||||
const expected = fixtures.expected(base);
|
||||
return sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.resize(2048, 1536)
|
||||
.toFile(actual)
|
||||
.then(function () {
|
||||
.then(() => {
|
||||
fixtures.assertMaxColourDistance(actual, expected, 102);
|
||||
});
|
||||
});
|
||||
|
||||
it('Reduction with non-nearest neighbor interpolation shouldn’t cause dark edges', function () {
|
||||
it('Reduction with non-nearest neighbor interpolation shouldn’t cause dark edges', () => {
|
||||
const base = 'alpha-premultiply-reduction-1024x768-paper.png';
|
||||
const actual = fixtures.path('output.' + base);
|
||||
const actual = fixtures.path(`output.${base}`);
|
||||
const expected = fixtures.expected(base);
|
||||
return sharp(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||
.resize(1024, 768)
|
||||
.toFile(actual)
|
||||
.then(function () {
|
||||
.then(() => {
|
||||
fixtures.assertMaxColourDistance(actual, expected, 102);
|
||||
});
|
||||
});
|
||||
|
||||
it('Removes alpha from fixtures with transparency, ignores those without', function () {
|
||||
return Promise.all([
|
||||
it('Removes alpha from fixtures with transparency, ignores those without', () => Promise.all([
|
||||
fixtures.inputPngWithTransparency,
|
||||
fixtures.inputPngWithTransparency16bit,
|
||||
fixtures.inputWebPWithTransparency,
|
||||
fixtures.inputJpg,
|
||||
fixtures.inputPng,
|
||||
fixtures.inputWebP
|
||||
].map(function (input) {
|
||||
return sharp(input)
|
||||
].map((input) => sharp(input)
|
||||
.resize(10)
|
||||
.removeAlpha()
|
||||
.toBuffer({ resolveWithObject: true })
|
||||
.then(function (result) {
|
||||
.then((result) => {
|
||||
assert.strictEqual(3, result.info.channels);
|
||||
});
|
||||
}));
|
||||
});
|
||||
}))));
|
||||
|
||||
it('Ensures alpha from fixtures without transparency, ignores those with', function () {
|
||||
return Promise.all([
|
||||
it('Ensures alpha from fixtures without transparency, ignores those with', () => Promise.all([
|
||||
fixtures.inputPngWithTransparency,
|
||||
fixtures.inputPngWithTransparency16bit,
|
||||
fixtures.inputWebPWithTransparency,
|
||||
fixtures.inputJpg,
|
||||
fixtures.inputPng,
|
||||
fixtures.inputWebP
|
||||
].map(function (input) {
|
||||
return sharp(input)
|
||||
].map((input) => sharp(input)
|
||||
.resize(10)
|
||||
.ensureAlpha()
|
||||
.png()
|
||||
.toBuffer({ resolveWithObject: true })
|
||||
.then(function (result) {
|
||||
.then((result) => {
|
||||
assert.strictEqual(4, result.info.channels);
|
||||
});
|
||||
}));
|
||||
});
|
||||
}))));
|
||||
|
||||
it('Valid ensureAlpha value used for alpha channel', async () => {
|
||||
const background = { r: 255, g: 0, b: 0 };
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
|
||||
const sharp = require('../../');
|
||||
const { inputAvif, inputJpg, inputGifAnimated } = require('../fixtures');
|
||||
const {
|
||||
inputAvif,
|
||||
inputAvifWithPitmBox,
|
||||
inputJpg,
|
||||
inputGifAnimated,
|
||||
inputPng,
|
||||
} = require('../fixtures');
|
||||
|
||||
describe('AVIF', () => {
|
||||
it('called without options does not throw an error', () => {
|
||||
@@ -16,16 +23,13 @@ describe('AVIF', () => {
|
||||
});
|
||||
|
||||
it('can convert AVIF to JPEG', async () => {
|
||||
const data = await sharp(inputAvif)
|
||||
.resize(32)
|
||||
.jpeg()
|
||||
.toBuffer();
|
||||
const { size, ...metadata } = await sharp(data)
|
||||
.metadata();
|
||||
const data = await sharp(inputAvif).resize(32).jpeg().toBuffer();
|
||||
const { size, ...metadata } = await sharp(data).metadata();
|
||||
void size;
|
||||
assert.deepStrictEqual(metadata, {
|
||||
autoOrient: {
|
||||
height: 13,
|
||||
width: 32
|
||||
width: 32,
|
||||
},
|
||||
channels: 3,
|
||||
chromaSubsampling: '4:2:0',
|
||||
@@ -40,7 +44,7 @@ describe('AVIF', () => {
|
||||
isProgressive: false,
|
||||
isPalette: false,
|
||||
space: 'srgb',
|
||||
width: 32
|
||||
width: 32,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,12 +53,12 @@ describe('AVIF', () => {
|
||||
.resize(32)
|
||||
.avif({ effort: 0 })
|
||||
.toBuffer();
|
||||
const { size, ...metadata } = await sharp(data)
|
||||
.metadata();
|
||||
const { size, ...metadata } = await sharp(data).metadata();
|
||||
void size;
|
||||
assert.deepStrictEqual(metadata, {
|
||||
autoOrient: {
|
||||
height: 26,
|
||||
width: 32
|
||||
width: 32,
|
||||
},
|
||||
channels: 3,
|
||||
compression: 'av1',
|
||||
@@ -69,20 +73,47 @@ describe('AVIF', () => {
|
||||
pagePrimary: 0,
|
||||
pages: 1,
|
||||
space: 'srgb',
|
||||
width: 32
|
||||
width: 32,
|
||||
});
|
||||
});
|
||||
|
||||
it('can convert PNG to lossless AVIF', async () => {
|
||||
const data = await sharp(inputPng)
|
||||
.resize(32)
|
||||
.avif({ lossless: true, effort: 0 })
|
||||
.toBuffer();
|
||||
const { size, ...metadata } = await sharp(data).metadata();
|
||||
void size;
|
||||
assert.deepStrictEqual(metadata, {
|
||||
autoOrient: {
|
||||
height: 24,
|
||||
width: 32,
|
||||
},
|
||||
channels: 3,
|
||||
compression: 'av1',
|
||||
depth: 'uchar',
|
||||
format: 'heif',
|
||||
hasAlpha: false,
|
||||
hasProfile: false,
|
||||
height: 24,
|
||||
isProgressive: false,
|
||||
isPalette: false,
|
||||
bitsPerSample: 8,
|
||||
pagePrimary: 0,
|
||||
pages: 1,
|
||||
space: 'srgb',
|
||||
width: 32,
|
||||
});
|
||||
});
|
||||
|
||||
it('can passthrough AVIF', async () => {
|
||||
const data = await sharp(inputAvif)
|
||||
.resize(32)
|
||||
.toBuffer();
|
||||
const { size, ...metadata } = await sharp(data)
|
||||
.metadata();
|
||||
const data = await sharp(inputAvif).resize(32).toBuffer();
|
||||
const { size, ...metadata } = await sharp(data).metadata();
|
||||
void size;
|
||||
assert.deepStrictEqual(metadata, {
|
||||
autoOrient: {
|
||||
height: 13,
|
||||
width: 32
|
||||
width: 32,
|
||||
},
|
||||
channels: 3,
|
||||
compression: 'av1',
|
||||
@@ -97,7 +128,7 @@ describe('AVIF', () => {
|
||||
pagePrimary: 0,
|
||||
pages: 1,
|
||||
space: 'srgb',
|
||||
width: 32
|
||||
width: 32,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -106,12 +137,12 @@ describe('AVIF', () => {
|
||||
.resize(10)
|
||||
.avif({ effort: 0 })
|
||||
.toBuffer();
|
||||
const { size, ...metadata } = await sharp(data)
|
||||
.metadata();
|
||||
const { size, ...metadata } = await sharp(data).metadata();
|
||||
void size;
|
||||
assert.deepStrictEqual(metadata, {
|
||||
autoOrient: {
|
||||
height: 300,
|
||||
width: 10
|
||||
width: 10,
|
||||
},
|
||||
channels: 4,
|
||||
compression: 'av1',
|
||||
@@ -126,7 +157,7 @@ describe('AVIF', () => {
|
||||
pagePrimary: 0,
|
||||
pages: 1,
|
||||
space: 'srgb',
|
||||
width: 10
|
||||
width: 10,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -136,12 +167,12 @@ describe('AVIF', () => {
|
||||
.sharpen()
|
||||
.avif({ effort: 0 })
|
||||
.toBuffer();
|
||||
const { size, ...metadata } = await sharp(data)
|
||||
.metadata();
|
||||
const { size, ...metadata } = await sharp(data).metadata();
|
||||
void size;
|
||||
assert.deepStrictEqual(metadata, {
|
||||
autoOrient: {
|
||||
height: 26,
|
||||
width: 32
|
||||
width: 32,
|
||||
},
|
||||
channels: 3,
|
||||
compression: 'av1',
|
||||
@@ -156,27 +187,89 @@ describe('AVIF', () => {
|
||||
pagePrimary: 0,
|
||||
pages: 1,
|
||||
space: 'srgb',
|
||||
width: 32
|
||||
width: 32,
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid width - too large', async () =>
|
||||
assert.rejects(
|
||||
() => sharp({ create: { width: 16385, height: 16, channels: 3, background: 'red' } }).avif().toBuffer(),
|
||||
/Processed image is too large for the HEIF format/
|
||||
)
|
||||
);
|
||||
() =>
|
||||
sharp({
|
||||
create: { width: 16385, height: 16, channels: 3, background: 'red' },
|
||||
})
|
||||
.avif()
|
||||
.toBuffer(),
|
||||
/Processed image is too large for the HEIF format/,
|
||||
));
|
||||
|
||||
it('Invalid height - too large', async () =>
|
||||
assert.rejects(
|
||||
() => sharp({ create: { width: 16, height: 16385, channels: 3, background: 'red' } }).avif().toBuffer(),
|
||||
/Processed image is too large for the HEIF format/
|
||||
)
|
||||
);
|
||||
() =>
|
||||
sharp({
|
||||
create: { width: 16, height: 16385, channels: 3, background: 'red' },
|
||||
})
|
||||
.avif()
|
||||
.toBuffer(),
|
||||
/Processed image is too large for the HEIF format/,
|
||||
));
|
||||
|
||||
it('Invalid bitdepth value throws error', async () => {
|
||||
assert.rejects(
|
||||
it('Invalid bitdepth value throws error', () =>
|
||||
assert.throws(
|
||||
() => sharp().avif({ bitdepth: 11 }),
|
||||
/Error: Expected 8, 10 or 12 for bitdepth but received 11 of type number/);
|
||||
/Expected 8, 10 or 12 for bitdepth but received 11 of type number/,
|
||||
));
|
||||
|
||||
it('Different tune options result in different file sizes', async () => {
|
||||
const ssim = await sharp(inputJpg)
|
||||
.resize(32)
|
||||
.avif({ tune: 'ssim', effort: 0 })
|
||||
.toBuffer();
|
||||
const iq = await sharp(inputJpg)
|
||||
.resize(32)
|
||||
.avif({ tune: 'iq', effort: 0 })
|
||||
.toBuffer();
|
||||
assert(ssim.length < iq.length);
|
||||
});
|
||||
|
||||
it('AVIF with non-zero primary item uses it as default page', async () => {
|
||||
const { exif, ...metadata } = await sharp(inputAvifWithPitmBox).metadata();
|
||||
void exif;
|
||||
assert.deepStrictEqual(metadata, {
|
||||
format: 'heif',
|
||||
width: 4096,
|
||||
height: 800,
|
||||
space: 'srgb',
|
||||
channels: 3,
|
||||
depth: 'uchar',
|
||||
isProgressive: false,
|
||||
isPalette: false,
|
||||
bitsPerSample: 8,
|
||||
pages: 5,
|
||||
pagePrimary: 4,
|
||||
compression: 'av1',
|
||||
resolutionUnit: 'cm',
|
||||
hasProfile: false,
|
||||
hasAlpha: false,
|
||||
autoOrient: { width: 4096, height: 800 },
|
||||
});
|
||||
|
||||
const data = await sharp(inputAvifWithPitmBox)
|
||||
.png({ compressionLevel: 0 })
|
||||
.toBuffer();
|
||||
const { size, ...pngMetadata } = await sharp(data).metadata();
|
||||
assert.deepStrictEqual(pngMetadata, {
|
||||
format: 'png',
|
||||
width: 4096,
|
||||
height: 800,
|
||||
space: 'srgb',
|
||||
channels: 3,
|
||||
depth: 'uchar',
|
||||
isProgressive: false,
|
||||
isPalette: false,
|
||||
bitsPerSample: 8,
|
||||
hasProfile: false,
|
||||
hasAlpha: false,
|
||||
autoOrient: { width: 4096, height: 800 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,51 +1,52 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fixtures = require('../fixtures');
|
||||
const sharp = require('../../');
|
||||
|
||||
describe('Bandbool per-channel boolean operations', function () {
|
||||
describe('Bandbool per-channel boolean operations', () => {
|
||||
[
|
||||
sharp.bool.and,
|
||||
sharp.bool.or,
|
||||
sharp.bool.eor
|
||||
]
|
||||
.forEach(function (op) {
|
||||
it(op + ' operation', function (done) {
|
||||
.forEach((op) => {
|
||||
it(`${op} operation`, (_t, done) => {
|
||||
sharp(fixtures.inputPngBooleanNoAlpha)
|
||||
.bandbool(op)
|
||||
.toColourspace('b-w')
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(200, info.width);
|
||||
assert.strictEqual(200, info.height);
|
||||
assert.strictEqual(1, info.channels);
|
||||
fixtures.assertSimilar(fixtures.expected('bandbool_' + op + '_result.png'), data, done);
|
||||
fixtures.assertSimilar(fixtures.expected(`bandbool_${op}_result.png`), data, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sRGB image retains 3 channels', function (done) {
|
||||
it('sRGB image retains 3 channels', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.bandbool('and')
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, _data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(3, info.channels);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid operation', function () {
|
||||
assert.throws(function () {
|
||||
it('Invalid operation', () => {
|
||||
assert.throws(() => {
|
||||
sharp().bandbool('fail');
|
||||
});
|
||||
});
|
||||
|
||||
it('Missing operation', function () {
|
||||
assert.throws(function () {
|
||||
it('Missing operation', () => {
|
||||
assert.throws(() => {
|
||||
sharp().bandbool();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
|
||||
const sharp = require('../../');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
describe('Blur', function () {
|
||||
it('specific radius 1', function (done) {
|
||||
describe('Blur', () => {
|
||||
it('specific radius 1', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur(1)
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@@ -22,11 +23,11 @@ describe('Blur', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('specific radius 10', function (done) {
|
||||
it('specific radius 10', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur(10)
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@@ -35,11 +36,11 @@ describe('Blur', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('specific options.sigma 10', function (done) {
|
||||
it('specific options.sigma 10', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur({ sigma: 10 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@@ -48,11 +49,11 @@ describe('Blur', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('specific radius 0.3', function (done) {
|
||||
it('specific radius 0.3', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur(0.3)
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@@ -61,11 +62,11 @@ describe('Blur', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('mild blur', function (done) {
|
||||
it('mild blur', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur()
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@@ -74,17 +75,17 @@ describe('Blur', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid radius', function () {
|
||||
assert.throws(function () {
|
||||
it('invalid radius', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg).blur(0.1);
|
||||
});
|
||||
});
|
||||
|
||||
it('blurred image is smaller than non-blurred', function (done) {
|
||||
it('blurred image is smaller than non-blurred', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur(false)
|
||||
.toBuffer(function (err, notBlurred, info) {
|
||||
.toBuffer((err, notBlurred, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, notBlurred.length > 0);
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
@@ -93,7 +94,7 @@ describe('Blur', function () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur(true)
|
||||
.toBuffer(function (err, blurred, info) {
|
||||
.toBuffer((err, blurred, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, blurred.length > 0);
|
||||
assert.strictEqual(true, blurred.length < notBlurred.length);
|
||||
@@ -105,18 +106,18 @@ describe('Blur', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid precision', function () {
|
||||
assert.throws(function () {
|
||||
it('invalid precision', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg).blur({ sigma: 1, precision: 'invalid' });
|
||||
}, /Expected one of: integer, float, approximate for precision but received invalid of type string/);
|
||||
});
|
||||
|
||||
it('invalid minAmplitude', function () {
|
||||
assert.throws(function () {
|
||||
it('invalid minAmplitude', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg).blur({ sigma: 1, minAmplitude: 0 });
|
||||
}, /Expected number between 0.001 and 1 for minAmplitude but received 0 of type number/);
|
||||
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg).blur({ sigma: 1, minAmplitude: 1.01 });
|
||||
}, /Expected number between 0.001 and 1 for minAmplitude but received 1.01 of type number/);
|
||||
});
|
||||
@@ -149,8 +150,8 @@ describe('Blur', function () {
|
||||
await fixtures.assertSimilar(fixtures.expected('blur-10.jpg'), minAmplitudeLow);
|
||||
});
|
||||
|
||||
it('options.sigma is required if options object is passed', function () {
|
||||
assert.throws(function () {
|
||||
it('options.sigma is required if options object is passed', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg).blur({ precision: 'invalid' });
|
||||
}, /Expected number between 0.3 and 1000 for options.sigma but received undefined of type undefined/);
|
||||
});
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
const fs = require('node:fs');
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
|
||||
const fixtures = require('../fixtures');
|
||||
const sharp = require('../../');
|
||||
|
||||
describe('Boolean operation between two images', function () {
|
||||
describe('Boolean operation between two images', () => {
|
||||
const inputJpgBooleanTestBuffer = fs.readFileSync(fixtures.inputJpgBooleanTest);
|
||||
|
||||
[
|
||||
@@ -17,63 +18,63 @@ describe('Boolean operation between two images', function () {
|
||||
sharp.bool.or,
|
||||
sharp.bool.eor
|
||||
]
|
||||
.forEach(function (op) {
|
||||
it(op + ' operation, file', function (done) {
|
||||
.forEach((op) => {
|
||||
it(`${op} operation, file`, (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.boolean(fixtures.inputJpgBooleanTest, op)
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(320, info.width);
|
||||
assert.strictEqual(240, info.height);
|
||||
fixtures.assertSimilar(fixtures.expected('boolean_' + op + '_result.jpg'), data, done);
|
||||
fixtures.assertSimilar(fixtures.expected(`boolean_${op}_result.jpg`), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it(op + ' operation, buffer', function (done) {
|
||||
it(`${op} operation, buffer`, (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.boolean(inputJpgBooleanTestBuffer, op)
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(320, info.width);
|
||||
assert.strictEqual(240, info.height);
|
||||
fixtures.assertSimilar(fixtures.expected('boolean_' + op + '_result.jpg'), data, done);
|
||||
fixtures.assertSimilar(fixtures.expected(`boolean_${op}_result.jpg`), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it(op + ' operation, raw', function (done) {
|
||||
it(`${op} operation, raw`, (_t, done) => {
|
||||
sharp(fixtures.inputJpgBooleanTest)
|
||||
.raw()
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.boolean(data, op, { raw: info })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(320, info.width);
|
||||
assert.strictEqual(240, info.height);
|
||||
fixtures.assertSimilar(fixtures.expected('boolean_' + op + '_result.jpg'), data, done);
|
||||
fixtures.assertSimilar(fixtures.expected(`boolean_${op}_result.jpg`), data, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid operation', function () {
|
||||
assert.throws(function () {
|
||||
it('Invalid operation', () => {
|
||||
assert.throws(() => {
|
||||
sharp().boolean(fixtures.inputJpgBooleanTest, 'fail');
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid operation, non-string', function () {
|
||||
assert.throws(function () {
|
||||
it('Invalid operation, non-string', () => {
|
||||
assert.throws(() => {
|
||||
sharp().boolean(fixtures.inputJpgBooleanTest, null);
|
||||
});
|
||||
});
|
||||
|
||||
it('Missing input', function () {
|
||||
assert.throws(function () {
|
||||
it('Missing input', () => {
|
||||
assert.throws(() => {
|
||||
sharp().boolean();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,139 +1,140 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
|
||||
const sharp = require('../../lib');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
describe('Clahe', function () {
|
||||
it('width 5 width 5 maxSlope 0', function (done) {
|
||||
describe('Clahe', () => {
|
||||
it('width 5 width 5 maxSlope 0', (_t, done) => {
|
||||
sharp(fixtures.inputJpgClahe)
|
||||
.clahe({ width: 5, height: 5, maxSlope: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('clahe-5-5-0.jpg'), data, { threshold: 10 }, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('width 5 width 5 maxSlope 5', function (done) {
|
||||
it('width 5 width 5 maxSlope 5', (_t, done) => {
|
||||
sharp(fixtures.inputJpgClahe)
|
||||
.clahe({ width: 5, height: 5, maxSlope: 5 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('clahe-5-5-5.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('width 11 width 25 maxSlope 14', function (done) {
|
||||
it('width 11 width 25 maxSlope 14', (_t, done) => {
|
||||
sharp(fixtures.inputJpgClahe)
|
||||
.clahe({ width: 11, height: 25, maxSlope: 14 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('clahe-11-25-14.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('width 50 width 50 maxSlope 0', function (done) {
|
||||
it('width 50 width 50 maxSlope 0', (_t, done) => {
|
||||
sharp(fixtures.inputJpgClahe)
|
||||
.clahe({ width: 50, height: 50, maxSlope: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('clahe-50-50-0.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('width 50 width 50 maxSlope 14', function (done) {
|
||||
it('width 50 width 50 maxSlope 14', (_t, done) => {
|
||||
sharp(fixtures.inputJpgClahe)
|
||||
.clahe({ width: 50, height: 50, maxSlope: 14 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('clahe-50-50-14.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('width 100 width 50 maxSlope 3', function (done) {
|
||||
it('width 100 width 50 maxSlope 3', (_t, done) => {
|
||||
sharp(fixtures.inputJpgClahe)
|
||||
.clahe({ width: 100, height: 50, maxSlope: 3 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('width 100 width 100 maxSlope 0', function (done) {
|
||||
it('width 100 width 100 maxSlope 0', (_t, done) => {
|
||||
sharp(fixtures.inputJpgClahe)
|
||||
.clahe({ width: 100, height: 100, maxSlope: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('clahe-100-100-0.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid maxSlope', function () {
|
||||
assert.throws(function () {
|
||||
it('invalid maxSlope', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: -5 });
|
||||
});
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 110 });
|
||||
});
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 5.5 });
|
||||
});
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 'a string' });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid width', function () {
|
||||
assert.throws(function () {
|
||||
it('invalid width', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 100.5, height: 100 });
|
||||
});
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: -5, height: 100 });
|
||||
});
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: true, height: 100 });
|
||||
});
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 'string test', height: 100 });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid height', function () {
|
||||
assert.throws(function () {
|
||||
it('invalid height', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100.5 });
|
||||
});
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: -5 });
|
||||
});
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: true });
|
||||
});
|
||||
assert.throws(function () {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 'string test' });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid options object', function () {
|
||||
assert.throws(function () {
|
||||
it('invalid options object', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpgClahe).clahe(100, 100, 5);
|
||||
});
|
||||
});
|
||||
|
||||
it('uses default maxSlope of 3', function (done) {
|
||||
it('uses default maxSlope of 3', (_t, done) => {
|
||||
sharp(fixtures.inputJpgClahe)
|
||||
.clahe({ width: 100, height: 50 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done);
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const assert = require('assert');
|
||||
const fs = require('node:fs');
|
||||
const { afterEach, beforeEach, describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
|
||||
const sharp = require('../../');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
describe('Clone', function () {
|
||||
beforeEach(function () {
|
||||
describe('Clone', () => {
|
||||
beforeEach(() => {
|
||||
sharp.cache(false);
|
||||
});
|
||||
afterEach(function () {
|
||||
afterEach(() => {
|
||||
sharp.cache(true);
|
||||
});
|
||||
|
||||
it('Read from Stream and write to multiple Streams', function (done) {
|
||||
it('Read from Stream and write to multiple Streams', (_t, done) => {
|
||||
let finishEventsExpected = 2;
|
||||
// Output stream 1
|
||||
const output1 = fixtures.path('output.multi-stream.1.jpg');
|
||||
const writable1 = fs.createWriteStream(output1);
|
||||
writable1.on('finish', function () {
|
||||
sharp(output1).toBuffer(function (err, data, info) {
|
||||
writable1.on('finish', () => {
|
||||
sharp(output1).toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual(data.length, info.size);
|
||||
@@ -40,8 +41,8 @@ describe('Clone', function () {
|
||||
// Output stream 2
|
||||
const output2 = fixtures.path('output.multi-stream.2.jpg');
|
||||
const writable2 = fs.createWriteStream(output2);
|
||||
writable2.on('finish', function () {
|
||||
sharp(output2).toBuffer(function (err, data, info) {
|
||||
writable2.on('finish', () => {
|
||||
sharp(output2).toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual(data.length, info.size);
|
||||
@@ -64,14 +65,14 @@ describe('Clone', function () {
|
||||
fs.createReadStream(fixtures.inputJpg).pipe(rotator);
|
||||
});
|
||||
|
||||
it('Stream-based input attaches finish event listener to original', function () {
|
||||
it('Stream-based input attaches finish event listener to original', () => {
|
||||
const original = sharp();
|
||||
const clone = original.clone();
|
||||
assert.strictEqual(1, original.listenerCount('finish'));
|
||||
assert.strictEqual(0, clone.listenerCount('finish'));
|
||||
});
|
||||
|
||||
it('Non Stream-based input does not attach finish event listeners', function () {
|
||||
it('Non Stream-based input does not attach finish event listeners', () => {
|
||||
const original = sharp(fixtures.inputJpg);
|
||||
const clone = original.clone();
|
||||
assert.strictEqual(0, original.listenerCount('finish'));
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
// Copyright 2013 Lovell Fuller and others.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
|
||||
const sharp = require('../../');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
describe('Colour space conversion', function () {
|
||||
it('To greyscale', function (done) {
|
||||
describe('Colour space conversion', () => {
|
||||
it('To greyscale', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.greyscale()
|
||||
.toFile(fixtures.path('output.greyscale-gamma-0.0.jpg'), done);
|
||||
});
|
||||
|
||||
it('To greyscale with gamma correction', function (done) {
|
||||
it('To greyscale with gamma correction', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.gamma()
|
||||
@@ -24,19 +25,19 @@ describe('Colour space conversion', function () {
|
||||
.toFile(fixtures.path('output.greyscale-gamma-2.2.jpg'), done);
|
||||
});
|
||||
|
||||
it('Not to greyscale', function (done) {
|
||||
it('Not to greyscale', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.greyscale(false)
|
||||
.toFile(fixtures.path('output.greyscale-not.jpg'), done);
|
||||
});
|
||||
|
||||
it('Greyscale with single channel output', function (done) {
|
||||
it('Greyscale with single channel output', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.greyscale()
|
||||
.toColourspace('b-w')
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(1, info.channels);
|
||||
assert.strictEqual(320, info.width);
|
||||
@@ -55,10 +56,10 @@ describe('Colour space conversion', function () {
|
||||
assert.strictEqual(format, 'webp');
|
||||
});
|
||||
|
||||
it('From CMYK to sRGB', function (done) {
|
||||
it('From CMYK to sRGB', (_t, done) => {
|
||||
sharp(fixtures.inputJpgWithCmykProfile)
|
||||
.resize(320)
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
@@ -67,13 +68,13 @@ describe('Colour space conversion', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('From CMYK to sRGB with white background, not yellow', function (done) {
|
||||
it('From CMYK to sRGB with white background, not yellow', (_t, done) => {
|
||||
sharp(fixtures.inputJpgWithCmykProfile)
|
||||
.resize(320, 240, {
|
||||
fit: sharp.fit.contain,
|
||||
background: 'white'
|
||||
})
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@@ -82,10 +83,10 @@ describe('Colour space conversion', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('From profile-less CMYK to sRGB', function (done) {
|
||||
it('From profile-less CMYK to sRGB', (_t, done) => {
|
||||
sharp(fixtures.inputJpgWithCmykNoProfile)
|
||||
.resize(320)
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@@ -122,14 +123,14 @@ describe('Colour space conversion', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('CMYK profile to CMYK profile with negate', (done) => {
|
||||
it('CMYK profile to CMYK profile with negate', (_t, done) => {
|
||||
sharp(fixtures.inputTiffFogra)
|
||||
.resize(320, 240)
|
||||
.toColourspace('cmyk')
|
||||
.pipelineColourspace('cmyk')
|
||||
.withIccProfile(fixtures.path('XCMYK 2017.icc'))
|
||||
.negate()
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('tiff', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@@ -143,13 +144,13 @@ describe('Colour space conversion', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('From sRGB with RGB16 pipeline, resize with gamma, to sRGB', function (done) {
|
||||
it('From sRGB with RGB16 pipeline, resize with gamma, to sRGB', (_t, done) => {
|
||||
sharp(fixtures.inputPngGradients)
|
||||
.pipelineColourspace('rgb16')
|
||||
.resize(320)
|
||||
.gamma()
|
||||
.toColourspace('srgb')
|
||||
.toBuffer(function (err, data, info) {
|
||||
.toBuffer((err, data, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(320, info.width);
|
||||
fixtures.assertSimilar(fixtures.expected('colourspace-gradients-gamma-resize.png'), data, {
|
||||
@@ -177,15 +178,15 @@ describe('Colour space conversion', function () {
|
||||
assert.strictEqual(b, 34);
|
||||
});
|
||||
|
||||
it('Invalid pipelineColourspace input', function () {
|
||||
assert.throws(function () {
|
||||
it('Invalid pipelineColourspace input', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.pipelineColorspace(null);
|
||||
}, /Expected string for colourspace but received null of type object/);
|
||||
});
|
||||
|
||||
it('Invalid toColourspace input', function () {
|
||||
assert.throws(function () {
|
||||
it('Invalid toColourspace input', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.toColourspace(null);
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user