Compare commits

...

32 Commits

Author SHA1 Message Date
Kleis Auke Wolthuizen
3609c61a22
Tests: fix JP2 suite with global libvips (#4477) 2025-11-15 10:55:58 +00:00
Jiralite
dc6820b49f
TypeScript: tag deprecated constructor properties (#4474) 2025-11-10 16:41:22 +00:00
Sylvester Keil
f2a49d19c9
Fix invalid escape sequence (#4471) 2025-11-07 11:39:39 +00:00
Lovell Fuller
e062456868
Release v0.34.5 2025-11-06 14:06:31 +00:00
Lovell Fuller
6450c704a6
Prerelease v0.34.5-rc.1 2025-11-06 11:34:00 +00:00
Lovell Fuller
f7c95d1bf0
TypeScript: consolidate a few enum-like properties 2025-11-06 11:15:28 +00:00
Lovell Fuller
ef86a75560
Prerelease v0.34.5-rc.0 2025-11-05 15:41:31 +00:00
Lovell Fuller
6c1e840098
Use structured binding for tuples where possible 2025-11-05 15:30:52 +00:00
Lovell Fuller
e1628d8ef5
Simplify ICC processing when retaining input profiles #4468
Takes advantage of libvips' improved ICC handling
2025-11-04 15:06:49 +00:00
Lovell Fuller
4f9f8179a6
Linter: apply all recommended biome settings
Enforces previously-skipped useArrowFunction check
2025-11-04 09:41:45 +00:00
Lovell Fuller
09d5aa8cfa Docs: update internal and libvips doc links 2025-11-02 14:38:22 +00:00
Lovell Fuller
040b73ca74 Upgrade to libvips v8.17.3 2025-11-01 12:23:32 +00:00
Lovell Fuller
1f2f33d9a7 Ensure licensing headers are retained by code bundlers 2025-10-31 11:52:41 +00:00
Kleis Auke Wolthuizen
69b2c45615
Tests: migrate text suite to async (#4466) 2025-10-27 16:21:37 +00:00
Lovell Fuller
9e4e184132 Add experimental support for prebuilt linux-riscv64 binaries 2025-10-19 11:56:45 +01:00
Lovell Fuller
206eb4a89a Limit colour strings to 200 chars, helps reduce effect of potential ReDoS 2025-10-18 14:52:17 +01:00
Lovell Fuller
c1c16ed3e6 Improve error messaging when only warnings issued #4465 2025-10-17 14:57:38 +01:00
Lovell Fuller
b7fda60a85 Bump deps 2025-10-17 14:56:17 +01:00
Lovell Fuller
1bbee519aa Separate build script from install script #4458
The --build-from-source flag is now deprecated and will soon
be removed along with the need to define an install script.

This will remove a whole category of package manager
warnings about install scripts and "built" dependencies.

Most people don't need to build sharp from source, but for
those that do, a suitable method is now something like:

$ npm install package-that-depends-on-sharp
$ npm explore sharp -- npm run build
2025-10-07 16:11:54 +01:00
Lovell Fuller
2324d75f7f CI: Upgrade to macOS 15 (Sequoia) 2025-10-05 11:28:58 +01:00
Lovell Fuller
5e72ad95fa Docs: changelog entry for #4459 2025-09-30 10:54:00 +01:00
throwbi
6b922b30d5
Add support for BigTIFF output (#4459) 2025-09-30 09:41:02 +01:00
Lovell Fuller
54722dd582 Modernise C++ linter using new @cpplint/cli tooling 2025-09-22 14:59:52 +01:00
Lovell Fuller
adb6275ae9 Remove licensing checker/linter
This tool appears to no longer be maintained, but more
importantly there are far fewer production dependencies now
than when first introduced, and all are known/trusted.
2025-09-21 12:11:02 +01:00
Lovell Fuller
f2978651f0 Migrate from mocha to Node.js native test runner
Includes coverage reports when using Node.js 22 onwards
2025-09-21 12:03:27 +01:00
Kleis Auke Wolthuizen
c446d743a2
Docs: libvips manages its own thread pool (#4455) 2025-09-20 13:03:38 +01:00
Lovell Fuller
3498eb63e3 Docs: partially-revert 3009957, fix link to glibc malloc tunables 2025-09-20 10:47:43 +01:00
Lovell Fuller
3009957120 Docs: Add note about libvips thread pool sizing 2025-09-19 13:13:53 +01:00
Lovell Fuller
b36237ddcb Switch linter from semistandard to biome
Uses the recommended rules apart from complexity/useArrowFunction,
which would affect about 1700 lines of code with little benefit
right now. This is something that can be addressed over time.
2025-09-18 21:18:31 +01:00
Lovell Fuller
a0af662d78 CI: Separate platform-independent linter tasks
Run these before platform-specific build/testing tasks
2025-09-18 13:21:03 +01:00
Lovell Fuller
ee437832e2 Release v0.34.4 2025-09-17 13:57:10 +01:00
Lovell Fuller
529901177b CI/Docs: Deno v2 support 2025-09-17 13:56:49 +01:00
137 changed files with 3015 additions and 2842 deletions

View File

@ -1,5 +1,5 @@
freebsd_instance: freebsd_instance:
image_family: freebsd-15-0-snap image_family: freebsd-15-0
task: task:
name: FreeBSD name: FreeBSD
@ -9,9 +9,10 @@ task:
prerequisites_script: prerequisites_script:
- pkg update -f - pkg update -f
- pkg upgrade -y - pkg upgrade -y
- pkg install -y devel/git devel/pkgconf graphics/vips www/node20 www/npm - pkg install -y devel/git devel/pkgconf graphics/vips www/node22 www/npm
- pkg-config --modversion vips-cpp - pkg-config --modversion vips-cpp
install_script: install_script:
- npm install --build-from-source - npm install
- npm run build
test_script: test_script:
- npx mocha --no-config --spec=test/unit/io.js --timeout=30000 - node --test test/unit/io.js

View File

@ -7,6 +7,4 @@ indent_size = 2
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
max_line_length = 120
[*.md]
trim_trailing_whitespace = false

View File

@ -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. 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. New bugs are assigned a `triage` label whilst under investigation.
## Submit a new feature request ## 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. it's probably fastest to add a comment to it about your requirement.
Implementation is usually straightforward if libvips 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. the feature you need.
## Submit a Pull Request to fix a bug ## 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`. 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 ## 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. 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). 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 Where possible, the functional tests use gradient-based perceptual hashes

View File

@ -4,9 +4,23 @@ on:
- pull_request - pull_request
permissions: {} permissions: {}
jobs: jobs:
lint:
permissions:
contents: read
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v5
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: build-native:
permissions: permissions:
contents: read contents: read
needs: lint
name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}" name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
container: ${{ matrix.container }} container: ${{ matrix.container }}
@ -59,34 +73,34 @@ jobs:
nodejs_version: "^20.3.0" nodejs_version: "^20.3.0"
nodejs_version_major: 20 nodejs_version_major: 20
platform: linux-arm64 platform: linux-arm64
- os: macos-13 - os: macos-15-intel
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^18.17.0" nodejs_version: "^18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
platform: darwin-x64 platform: darwin-x64
package: true package: true
- os: macos-13 - os: macos-15-intel
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^20.3.0" nodejs_version: "^20.3.0"
nodejs_version_major: 20 nodejs_version_major: 20
platform: darwin-x64 platform: darwin-x64
- os: macos-13 - os: macos-15-intel
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^22.9.0" nodejs_version: "^22.9.0"
nodejs_version_major: 22 nodejs_version_major: 22
platform: darwin-x64 platform: darwin-x64
- os: macos-14 - os: macos-15
nodejs_arch: arm64 nodejs_arch: arm64
nodejs_version: "^18.17.0" nodejs_version: "^18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
platform: darwin-arm64 platform: darwin-arm64
package: true package: true
- os: macos-14 - os: macos-15
nodejs_arch: arm64 nodejs_arch: arm64
nodejs_version: "^20.3.0" nodejs_version: "^20.3.0"
nodejs_version_major: 20 nodejs_version_major: 20
platform: darwin-arm64 platform: darwin-arm64
- os: macos-14 - os: macos-15
nodejs_arch: arm64 nodejs_arch: arm64
nodejs_version: "^22.9.0" nodejs_version: "^22.9.0"
nodejs_version_major: 22 nodejs_version_major: 22
@ -150,17 +164,15 @@ jobs:
python-version: "3.12" python-version: "3.12"
- name: Dependencies (Node.js) - name: Dependencies (Node.js)
if: "!contains(matrix.platform, 'linuxmusl')" if: "!contains(matrix.platform, 'linuxmusl')"
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: ${{ matrix.nodejs_version }} node-version: ${{ matrix.nodejs_version }}
architecture: ${{ matrix.nodejs_arch }} architecture: ${{ matrix.nodejs_arch }}
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install - run: npm install
run: npm install --build-from-source - run: npm run build
- name: Test - run: npm run test-unit
run: npm test - if: matrix.package
- name: Populate npm package
if: matrix.package
run: npm run package-from-local-build run: npm run package-from-local-build
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: matrix.package if: matrix.package
@ -172,6 +184,7 @@ jobs:
build-linuxmusl-arm64: build-linuxmusl-arm64:
permissions: permissions:
contents: read contents: read
needs: lint
name: "build-linuxmusl-arm64 [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}" name: "build-linuxmusl-arm64 [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
runs-on: ubuntu-24.04-arm runs-on: ubuntu-24.04-arm
container: container:
@ -199,12 +212,10 @@ jobs:
- name: Dependencies - name: Dependencies
run: apk add build-base git python3 font-noto --update-cache run: apk add build-base git python3 font-noto --update-cache
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install - run: npm install
run: npm install --build-from-source - run: npm run build
- name: Test - run: npm run test-unit
run: npm test - if: matrix.package
- name: Populate npm package
if: matrix.package
run: npm run package-from-local-build run: npm run package-from-local-build
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: matrix.package if: matrix.package
@ -216,6 +227,7 @@ jobs:
build-qemu: build-qemu:
permissions: permissions:
contents: read contents: read
needs: lint
name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] [package]" name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] [package]"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
strategy: strategy:
@ -223,40 +235,49 @@ jobs:
matrix: matrix:
include: include:
- platform: linux-arm - platform: linux-arm
distro: bullseye base_image: "balenalib/rpi-raspbian:bullseye"
run_on_arch: armv6
nodejs_arch: armv6l nodejs_arch: armv6l
nodejs_hostname: unofficial-builds.nodejs.org nodejs_hostname: unofficial-builds.nodejs.org
nodejs_version: "18.17.0" nodejs_version: "18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
- platform: linux-s390x - platform: linux-s390x
distro: bookworm base_image: "--platform=linux/s390x s390x/debian:bookworm"
run_on_arch: s390x
nodejs_arch: s390x nodejs_arch: s390x
nodejs_hostname: nodejs.org nodejs_hostname: nodejs.org
nodejs_version: "18.17.0" nodejs_version: "18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
- platform: linux-ppc64 - platform: linux-ppc64
distro: bookworm base_image: "--platform=linux/ppc64le ppc64le/debian:bookworm"
run_on_arch: ppc64le
nodejs_arch: ppc64le nodejs_arch: ppc64le
nodejs_hostname: nodejs.org nodejs_hostname: nodejs.org
nodejs_version: "18.17.0" nodejs_version: "18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
- 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: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3 - uses: uraimo/run-on-arch-action@v3
with: with:
arch: ${{ matrix.run_on_arch }} arch: none
distro: ${{ matrix.distro }} distro: none
base_image: ${{ matrix.base_image }}
env: |
CFLAGS: "${{ matrix.compiler_flags }}"
CXXFLAGS: "${{ matrix.compiler_flags }}"
run: | run: |
apt-get update apt-get update
apt-get install -y curl g++ git libatomic1 make python3 xz-utils apt-get install -y curl g++ git libatomic1 make python3 xz-utils
mkdir /opt/nodejs 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 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 export PATH=$PATH:/opt/nodejs/bin
npm install --build-from-source npm install
npx mocha --no-config --spec=test/unit/io.js --timeout=30000 npm run build
node --test test/unit/io.js
npm run package-from-local-build npm run package-from-local-build
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
@ -267,19 +288,20 @@ jobs:
build-emscripten: build-emscripten:
permissions: permissions:
contents: read contents: read
needs: lint
name: "build-wasm32 [package]" name: "build-wasm32 [package]"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
container: "emscripten/emsdk:4.0.14" container: "emscripten/emsdk:4.0.18"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Dependencies - name: Dependencies
run: apt-get update && apt-get install -y pkg-config run: apt-get update && apt-get install -y pkg-config
- name: Dependencies (Node.js) - name: Dependencies (Node.js)
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: "20" node-version: "20"
- name: Install - run: npm install
run: emmake npm install --build-from-source - run: emmake npm run build
- name: Verify emscripten versions match - name: Verify emscripten versions match
run: | run: |
EMSCRIPTEN_VERSION_LIBVIPS=$(node -p "require('@img/sharp-libvips-dev-wasm32/versions').emscripten") EMSCRIPTEN_VERSION_LIBVIPS=$(node -p "require('@img/sharp-libvips-dev-wasm32/versions').emscripten")
@ -287,10 +309,8 @@ jobs:
echo "libvips built with emscripten $EMSCRIPTEN_VERSION_LIBVIPS" echo "libvips built with emscripten $EMSCRIPTEN_VERSION_LIBVIPS"
echo "sharp built with emscripten $EMSCRIPTEN_VERSION_SHARP" echo "sharp built with emscripten $EMSCRIPTEN_VERSION_SHARP"
test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP" test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP"
- name: Test - run: emmake npm run test-unit
run: emmake npm test - run: emmake npm run package-from-local-build
- name: Populate npm package
run: emmake npm run package-from-local-build
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: wasm32 name: wasm32
@ -314,7 +334,7 @@ jobs:
path: npm path: npm
- name: Create npm workspace tarball - name: Create npm workspace tarball
run: tar -vcaf npm-workspace.tar.xz --directory npm --exclude=from-local-build.js . run: tar -vcaf npm-workspace.tar.xz --directory npm --exclude=from-local-build.js .
- uses: actions/setup-node@v4 - uses: actions/setup-node@v5
with: with:
node-version: '24' node-version: '24'
- name: Create release notes - name: Create release notes

View File

@ -43,30 +43,30 @@ jobs:
runtime: bun runtime: bun
- name: darwin-x64-node-npm - name: darwin-x64-node-npm
runs-on: macos-13 runs-on: macos-15-intel
runtime: node runtime: node
package-manager: npm package-manager: npm
- name: darwin-x64-node-pnpm - name: darwin-x64-node-pnpm
runs-on: macos-13 runs-on: macos-15-intel
runtime: node runtime: node
package-manager: pnpm package-manager: pnpm
- name: darwin-x64-node-yarn - name: darwin-x64-node-yarn
runs-on: macos-13 runs-on: macos-15-intel
runtime: node runtime: node
package-manager: yarn package-manager: yarn
- name: darwin-x64-node-yarn-pnp - name: darwin-x64-node-yarn-pnp
runs-on: macos-13 runs-on: macos-15-intel
runtime: node runtime: node
package-manager: yarn-pnp package-manager: yarn-pnp
- name: darwin-x64-node-yarn-v1 - name: darwin-x64-node-yarn-v1
runs-on: macos-13 runs-on: macos-15-intel
runtime: node runtime: node
package-manager: yarn-v1 package-manager: yarn-v1
- name: darwin-x64-deno - name: darwin-x64-deno
runs-on: macos-13 runs-on: macos-15-intel
runtime: deno runtime: deno
- name: darwin-x64-bun - name: darwin-x64-bun
runs-on: macos-13 runs-on: macos-15-intel
runtime: bun runtime: bun
- name: win32-x64-node-npm - name: win32-x64-node-npm
@ -185,7 +185,9 @@ jobs:
- name: Run with Deno - name: Run with Deno
if: ${{ matrix.runtime == '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 - name: Run with Bun
if: ${{ matrix.runtime == 'bun' }} if: ${{ matrix.runtime == 'bun' }}

View File

@ -1,7 +0,0 @@
{
"parallel": true,
"slow": 1000,
"timeout": 30000,
"require": "./test/beforeEach.js",
"spec": "./test/unit/*.js"
}

26
biome.json Normal file
View File

@ -0,0 +1,26 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.4/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"
}
}
}

View File

@ -1,6 +1,6 @@
// @ts-check // @ts-check
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight'; import starlight from '@astrojs/starlight';
import { defineConfig } from 'astro/config';
import starlightAutoSidebar from 'starlight-auto-sidebar'; import starlightAutoSidebar from 'starlight-auto-sidebar';
import { version } from '../package.json'; import { version } from '../package.json';

View File

@ -1,7 +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
'use strict'; */
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import path from 'node:path'; import path from 'node:path';

View File

@ -11,8 +11,8 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/starlight": "^0.35.2", "@astrojs/starlight": "^0.36.2",
"astro": "^5.13.5", "astro": "^5.15.3",
"starlight-auto-sidebar": "^0.1.2" "starlight-auto-sidebar": "^0.1.3"
} }
} }

View File

@ -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. 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** **Example**

View File

@ -80,7 +80,7 @@ as defined by [toColourspace](#tocolourspace).
| Param | Type | Description | | 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** **Example**
```js ```js
@ -123,7 +123,7 @@ By default output image will be web-friendly sRGB, with additional channels inte
| Param | Type | Description | | 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** **Example**
```js ```js

View File

@ -21,7 +21,7 @@ The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
`hard-light`, `soft-light`, `difference`, `exclusion`. `hard-light`, `soft-light`, `difference`, `exclusion`.
More information about blend modes can be found at 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/ and https://www.cairographics.org/operators/
@ -64,8 +64,8 @@ and https://www.cairographics.org/operators/
| [images[].raw.height] | <code>Number</code> | | | | [images[].raw.height] | <code>Number</code> | | |
| [images[].raw.channels] | <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[].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>&quot;&#x27;warning&#x27;&quot;</code> | @see [constructor parameters](/api-constructor#parameters) | | [images[].failOn] | <code>string</code> | <code>&quot;&#x27;warning&#x27;&quot;</code> | @see [constructor parameters](/api-constructor/) |
| [images[].limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | @see [constructor parameters](/api-constructor#parameters) | | [images[].limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | @see [constructor parameters](/api-constructor/) |
**Example** **Example**
```js ```js

View File

@ -13,7 +13,7 @@ It does not take into consideration any operations to be applied to the output i
such as resize or rotate. such as resize or rotate.
Dimensions in the response will respect the `page` and `pages` properties of the 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. A `Promise` is returned when `callback` is not provided.
@ -21,9 +21,9 @@ A `Promise` is returned when `callback` is not provided.
- `size`: Total size of image in bytes, for Stream and Buffer input only - `size`: Total size of image in bytes, for Stream and Buffer input only
- `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below) - `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) - `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 - `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 - `density`: Number of pixels per inch (DPI), if present
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan

View File

@ -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. 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. 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**: **Throws**:

View File

@ -201,7 +201,7 @@ const dataWithMergedExif = await sharp(inputWithExif)
Keep ICC profile from the input image in the output image. 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 **Since**: 0.33.0
@ -211,6 +211,14 @@ const outputWithIccProfile = await sharp(inputWithIccProfile)
.keepIccProfile() .keepIccProfile()
.toBuffer(); .toBuffer();
``` ```
**Example**
```js
const cmykOutputWithIccProfile = await sharp(cmykInputWithIccProfile)
.pipelineColourspace('cmyk')
.toColourspace('cmyk')
.keepIccProfile()
.toBuffer();
```
## withIccProfile ## withIccProfile
@ -430,7 +438,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. Set `palette` to `true` for slower, indexed PNG output.
For 16 bits per pixel output, convert to `rgb16` via For 16 bits per pixel output, convert to `rgb16` via
[toColourspace](/api-colour#tocolourspace). [toColourspace](/api-colour/#tocolourspace).
**Throws**: **Throws**:
@ -589,7 +597,7 @@ Use these JP2 options for output image.
Requires libvips compiled with support for OpenJPEG. Requires libvips compiled with support for OpenJPEG.
The prebuilt binaries do not include this - see 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**: **Throws**:
@ -646,6 +654,7 @@ instead of providing `xres` and `yres` in pixels/mm.
| [options.quality] | <code>number</code> | <code>80</code> | quality, integer 1-100 | | [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.force] | <code>boolean</code> | <code>true</code> | force TIFF output, otherwise attempt to use input format |
| [options.compression] | <code>string</code> | <code>&quot;&#x27;jpeg&#x27;&quot;</code> | compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k | | [options.compression] | <code>string</code> | <code>&quot;&#x27;jpeg&#x27;&quot;</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>&quot;&#x27;horizontal&#x27;&quot;</code> | compression predictor options: none, horizontal, float | | [options.predictor] | <code>string</code> | <code>&quot;&#x27;horizontal&#x27;&quot;</code> | compression predictor options: none, horizontal, float |
| [options.pyramid] | <code>boolean</code> | <code>false</code> | write an image pyramid | | [options.pyramid] | <code>boolean</code> | <code>false</code> | write an image pyramid |
| [options.tile] | <code>boolean</code> | <code>false</code> | write a tiled tiff | | [options.tile] | <code>boolean</code> | <code>false</code> | write a tiled tiff |
@ -753,7 +762,7 @@ This feature is experimental, please do not use in production systems.
Requires libvips compiled with support for libjxl. Requires libvips compiled with support for libjxl.
The prebuilt binaries do not include this - see 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**: **Throws**:

View File

@ -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. and these are independent of the value set here.
:::note :::note
Further [control over performance](/performance) is available. Further [control over performance](/performance/) is available.
::: :::

View File

@ -1,5 +1,5 @@
--- ---
title: v0.34.4 - TBD title: v0.34.4 - 17th September 2025
slug: changelog/v0.34.4 slug: changelog/v0.34.4
--- ---

View 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)

View File

@ -20,7 +20,7 @@ npm install sharp
pnpm add sharp pnpm add sharp
``` ```
When using `pnpm`, you may need to add `sharp` to When using `pnpm`, add `sharp` to
[ignoredBuiltDependencies](https://pnpm.io/settings#ignoredbuiltdependencies) [ignoredBuiltDependencies](https://pnpm.io/settings#ignoredbuiltdependencies)
to silence warnings. to silence warnings.
@ -33,7 +33,8 @@ bun add sharp
``` ```
```sh frame="none" ```sh frame="none"
deno run --allow-ffi ... deno add --quiet npm:sharp
deno run --allow-env --allow-ffi --allow-read --allow-sys ...
``` ```
## Prerequisites ## Prerequisites
@ -48,6 +49,7 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
* macOS ARM64 * macOS ARM64
* Linux ARM (glibc >= 2.31) * Linux ARM (glibc >= 2.31)
* Linux ARM64 (glibc >= 2.26, musl >= 1.2.2) * Linux ARM64 (glibc >= 2.26, musl >= 1.2.2)
* Linux RISC-V 64-bit (glibc >= 2.41)
* Linux ppc64 (glibc >= 2.36) * Linux ppc64 (glibc >= 2.36)
* Linux s390x (glibc >= 2.36) * Linux s390x (glibc >= 2.36)
* Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2) * Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
@ -110,10 +112,11 @@ and on macOS when running Node.js under Rosetta.
## Building from source ## Building from source
This module will be compiled from source at `npm install` time when: This module will be compiled from source when:
* a globally-installed libvips is detected, or * a globally-installed libvips is detected, or
* when the `npm install --build-from-source` flag is used. * using `npm explore sharp -- npm run build`, or
* using the deprecated `npm run --build-from-source` at `npm install` time.
The logic to detect a globally-installed libvips can be skipped by setting the The logic to detect a globally-installed libvips can be skipped by setting the
`SHARP_IGNORE_GLOBAL_LIBVIPS` (never try to use it) or `SHARP_IGNORE_GLOBAL_LIBVIPS` (never try to use it) or

View File

@ -17,7 +17,8 @@ before the Node.js process starts to increase the thread pool size.
export UV_THREADPOOL_SIZE="$(lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l)" 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, 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. except when using glibc-based Linux without jemalloc, where the default is `1` to help reduce memory fragmentation.
@ -25,7 +26,7 @@ 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. 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 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. environment variable before the Node.js process starts to reduce the number of memory pools.
```sh frame="none" ```sh frame="none"

38
install/build.js Normal file
View 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('Attempting to build from source via node-gyp');
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(`Detected globally-installed libvips v${globalLibvipsVersion()}`);
}
const status = spawnRebuild();
if (status !== 0) {
process.exit(status);
}

View File

@ -1,39 +1,12 @@
// 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'; */
try { try {
const { useGlobalLibvips, globalLibvipsVersion, log, spawnRebuild } = require('../lib/libvips'); const { useGlobalLibvips } = require('../lib/libvips');
if (useGlobalLibvips() || process.env.npm_config_build_from_source) {
const buildFromSource = (msg) => { process.exit(1);
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) { } catch (err) {
const summary = err.message.split(/\n/).slice(0, 1); const summary = err.message.split(/\n/).slice(0, 1);

View File

@ -1,7 +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
'use strict'; */
const is = require('./is'); 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. * 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 * @example
* sharp('rgba.png') * sharp('rgba.png')
@ -163,7 +163,7 @@ function bandbool (boolOp) {
* @module Sharp * @module Sharp
* @private * @private
*/ */
module.exports = function (Sharp) { module.exports = (Sharp) => {
Object.assign(Sharp.prototype, { Object.assign(Sharp.prototype, {
// Public instance functions // Public instance functions
removeAlpha, removeAlpha,

View File

@ -1,7 +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
'use strict'; */
const color = require('@img/colour'); const color = require('@img/colour');
const is = require('./is'); 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. * 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, * 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 * @since 0.29.0
* *
@ -80,7 +80,7 @@ function grayscale (grayscale) {
* .toColourspace('srgb') * .toColourspace('srgb')
* .toFile('16bpc-pipeline-to-8bpc-output.png') * .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} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
@ -112,7 +112,7 @@ function pipelineColorspace (colorspace) {
* .toColourspace('rgb16') * .toColourspace('rgb16')
* .toFile('16-bpp.png') * .toFile('16-bpp.png')
* *
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/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} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
@ -141,7 +141,10 @@ function toColorspace (colorspace) {
* @throws {Error} Invalid value * @throws {Error} Invalid value
*/ */
function _getBackgroundColourOption (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); const colour = color(value);
return [ return [
colour.red(), colour.red(),
@ -172,7 +175,7 @@ function _setBackgroundColourOption (key, value) {
* @module Sharp * @module Sharp
* @private * @private
*/ */
module.exports = function (Sharp) { module.exports = (Sharp) => {
Object.assign(Sharp.prototype, { Object.assign(Sharp.prototype, {
// Public // Public
tint, tint,

View File

@ -1,7 +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
'use strict'; */
const is = require('./is'); const is = require('./is');
@ -56,7 +56,7 @@ const blend = {
* `hard-light`, `soft-light`, `difference`, `exclusion`. * `hard-light`, `soft-light`, `difference`, `exclusion`.
* *
* More information about blend modes can be found at * 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/ * and https://www.cairographics.org/operators/
* *
* @since 0.22.0 * @since 0.22.0
@ -123,8 +123,8 @@ const blend = {
* @param {Number} [images[].raw.height] * @param {Number} [images[].raw.height]
* @param {Number} [images[].raw.channels] * @param {Number} [images[].raw.channels]
* @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image. * @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 {string} [images[].failOn='warning'] - @see {@link /api-constructor/ constructor parameters}
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters} * @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor/ constructor parameters}
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
@ -206,7 +206,7 @@ function composite (images) {
* @module Sharp * @module Sharp
* @private * @private
*/ */
module.exports = function (Sharp) { module.exports = (Sharp) => {
Sharp.prototype.composite = composite; Sharp.prototype.composite = composite;
Sharp.blend = blend; Sharp.blend = blend;
}; };

View File

@ -1,7 +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
'use strict'; */
const util = require('node:util'); const util = require('node:util');
const stream = require('node:stream'); const stream = require('node:stream');
@ -12,6 +12,10 @@ require('./sharp');
// Use NODE_DEBUG=sharp to enable libvips warnings // Use NODE_DEBUG=sharp to enable libvips warnings
const debuglog = util.debuglog('sharp'); const debuglog = util.debuglog('sharp');
const queueListener = (queueLength) => {
Sharp.queue.emit('change', queueLength);
};
/** /**
* Constructor factory to create an instance of `sharp`, to which further methods are chained. * Constructor factory to create an instance of `sharp`, to which further methods are chained.
* *
@ -205,6 +209,7 @@ const debuglog = util.debuglog('sharp');
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const Sharp = function (input, options) { const Sharp = function (input, options) {
// biome-ignore lint/complexity/noArguments: constructor factory
if (arguments.length === 1 && !is.defined(input)) { if (arguments.length === 1 && !is.defined(input)) {
throw new Error('Invalid input'); throw new Error('Invalid input');
} }
@ -353,6 +358,7 @@ const Sharp = function (input, options) {
gifProgressive: false, gifProgressive: false,
tiffQuality: 80, tiffQuality: 80,
tiffCompression: 'jpeg', tiffCompression: 'jpeg',
tiffBigtiff: false,
tiffPredictor: 'horizontal', tiffPredictor: 'horizontal',
tiffPyramid: false, tiffPyramid: false,
tiffMiniswhite: false, tiffMiniswhite: false,
@ -396,9 +402,7 @@ const Sharp = function (input, options) {
debuglog(warning); debuglog(warning);
}, },
// Function to notify of queue length changes // Function to notify of queue length changes
queueListener: function (queueLength) { queueListener
Sharp.queue.emit('change', queueLength);
}
}; };
this.options.input = this._createInputDescriptor(input, options, { allowStream: true }); this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
return this; return this;

25
lib/index.d.ts vendored
View File

@ -27,7 +27,7 @@
/// <reference types="node" /> /// <reference types="node" />
import { Duplex } from 'stream'; import type { Duplex } from 'node:stream';
//#region Constructor functions //#region Constructor functions
@ -860,6 +860,7 @@ declare namespace sharp {
| JxlOptions | JxlOptions
| GifOptions | GifOptions
| Jp2Options | Jp2Options
| RawOptions
| TiffOptions, | TiffOptions,
): Sharp; ): Sharp;
@ -1027,11 +1028,11 @@ declare namespace sharp {
openSlide?: OpenSlideInputOptions | undefined; openSlide?: OpenSlideInputOptions | undefined;
/** JPEG 2000 specific input options */ /** JPEG 2000 specific input options */
jp2?: Jp2InputOptions | undefined; jp2?: Jp2InputOptions | undefined;
/** Deprecated: use tiff.subifd instead */ /** @deprecated Use {@link SharpOptions.tiff} instead */
subifd?: number | undefined; subifd?: number | undefined;
/** Deprecated: use pdf.background instead */ /** @deprecated Use {@link SharpOptions.pdf} instead */
pdfBackground?: Colour | Color | undefined; pdfBackground?: Colour | Color | undefined;
/** Deprecated: use openSlide.level instead */ /** @deprecated Use {@link SharpOptions.openSlide} instead */
level?: number | undefined; level?: number | undefined;
/** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */ /** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */
animated?: boolean | undefined; animated?: boolean | undefined;
@ -1181,6 +1182,10 @@ declare namespace sharp {
'IFD3'?: ExifDir; 'IFD3'?: ExifDir;
} }
type HeifCompression = 'av1' | 'hevc';
type Unit = 'inch' | 'cm';
interface WriteableMetadata { interface WriteableMetadata {
/** Number of pixels per inch (DPI) */ /** Number of pixels per inch (DPI) */
density?: number | undefined; density?: number | undefined;
@ -1259,7 +1264,7 @@ declare namespace sharp {
/** Buffer containing raw TIFFTAG_PHOTOSHOP data, if present */ /** Buffer containing raw TIFFTAG_PHOTOSHOP data, if present */
tifftagPhotoshop?: Buffer | undefined; tifftagPhotoshop?: Buffer | undefined;
/** The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC) */ /** The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC) */
compression?: 'av1' | 'hevc'; compression?: HeifCompression | undefined;
/** Default background colour, if present, for PNG (bKGD) and GIF images */ /** Default background colour, if present, for PNG (bKGD) and GIF images */
background?: { r: number; g: number; b: number } | { gray: number }; 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 */ /** Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide */
@ -1267,7 +1272,7 @@ declare namespace sharp {
/** Number of Sub Image File Directories in an OME-TIFF image */ /** Number of Sub Image File Directories in an OME-TIFF image */
subifds?: number | undefined; subifds?: number | undefined;
/** The unit of resolution (density) */ /** The unit of resolution (density) */
resolutionUnit?: 'inch' | 'cm' | undefined; resolutionUnit?: Unit | undefined;
/** String containing format for images loaded via *magick */ /** String containing format for images loaded via *magick */
formatMagick?: string | undefined; formatMagick?: string | undefined;
/** Array of keyword/text pairs representing PNG text blocks, if present. */ /** Array of keyword/text pairs representing PNG text blocks, if present. */
@ -1423,7 +1428,7 @@ declare namespace sharp {
/** quality, integer 1-100 (optional, default 50) */ /** quality, integer 1-100 (optional, default 50) */
quality?: number | undefined; quality?: number | undefined;
/** compression format: av1, hevc (optional, default 'av1') */ /** compression format: av1, hevc (optional, default 'av1') */
compression?: 'av1' | 'hevc' | undefined; compression?: HeifCompression | undefined;
/** use lossless compression (optional, default false) */ /** use lossless compression (optional, default false) */
lossless?: boolean | undefined; lossless?: boolean | undefined;
/** Level of CPU effort to reduce file size, between 0 (fastest) and 9 (slowest) (optional, default 4) */ /** Level of CPU effort to reduce file size, between 0 (fastest) and 9 (slowest) (optional, default 4) */
@ -1460,6 +1465,8 @@ declare namespace sharp {
quality?: number | undefined; quality?: number | undefined;
/** Compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k (optional, default 'jpeg') */ /** Compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k (optional, default 'jpeg') */
compression?: string | undefined; 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') */ /** Compression predictor options: none, horizontal, float (optional, default 'horizontal') */
predictor?: string | undefined; predictor?: string | undefined;
/** Write an image pyramid (optional, default false) */ /** Write an image pyramid (optional, default false) */
@ -1479,7 +1486,7 @@ declare namespace sharp {
/** Write 1-bit images as miniswhite (optional, default false) */ /** Write 1-bit images as miniswhite (optional, default false) */
miniswhite?: boolean | undefined; miniswhite?: boolean | undefined;
/** Resolution unit options: inch, cm (optional, default 'inch') */ /** Resolution unit options: inch, cm (optional, default 'inch') */
resolutionUnit?: 'inch' | 'cm' | undefined; resolutionUnit?: Unit | undefined;
} }
interface PngOptions extends OutputOptions { interface PngOptions extends OutputOptions {
@ -1604,7 +1611,7 @@ declare namespace sharp {
} }
interface RawOptions { 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 */ /** 1 for grayscale, 2 for grayscale + alpha, 3 for sRGB, 4 for CMYK or RGBA */

View File

@ -1,7 +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
'use strict'; */
const Sharp = require('./constructor'); const Sharp = require('./constructor');
require('./input')(Sharp); require('./input')(Sharp);

View File

@ -1,7 +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
'use strict'; */
const is = require('./is'); const is = require('./is');
const sharp = require('./sharp'); const sharp = require('./sharp');
@ -54,7 +54,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = { const inputDescriptor = {
autoOrient: false, autoOrient: false,
failOn: 'warning', failOn: 'warning',
limitInputPixels: Math.pow(0x3FFF, 2), limitInputPixels: 0x3FFF ** 2,
ignoreIcc: false, ignoreIcc: false,
unlimited: false, unlimited: false,
sequentialRead: true sequentialRead: true
@ -150,7 +150,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
if (is.defined(inputOptions.limitInputPixels)) { if (is.defined(inputOptions.limitInputPixels)) {
if (is.bool(inputOptions.limitInputPixels)) { if (is.bool(inputOptions.limitInputPixels)) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels inputDescriptor.limitInputPixels = inputOptions.limitInputPixels
? Math.pow(0x3FFF, 2) ? 0x3FFF ** 2
: 0; : 0;
} else if (is.integer(inputOptions.limitInputPixels) && is.inRange(inputOptions.limitInputPixels, 0, Number.MAX_SAFE_INTEGER)) { } else if (is.integer(inputOptions.limitInputPixels) && is.inRange(inputOptions.limitInputPixels, 0, Number.MAX_SAFE_INTEGER)) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels; inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
@ -513,7 +513,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
} }
} }
} else if (is.defined(inputOptions)) { } else if (is.defined(inputOptions)) {
throw new Error('Invalid input options ' + inputOptions); throw new Error(`Invalid input options ${inputOptions}`);
} }
return inputDescriptor; return inputDescriptor;
} }
@ -525,10 +525,8 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
* @param {string} encoding - unused * @param {string} encoding - unused
* @param {Function} callback * @param {Function} callback
*/ */
function _write (chunk, encoding, callback) { function _write (chunk, _encoding, callback) {
/* istanbul ignore else */
if (Array.isArray(this.options.input.buffer)) { if (Array.isArray(this.options.input.buffer)) {
/* istanbul ignore else */
if (is.buffer(chunk)) { if (is.buffer(chunk)) {
if (this.options.input.buffer.length === 0) { if (this.options.input.buffer.length === 0) {
this.on('finish', () => { this.on('finish', () => {
@ -572,7 +570,7 @@ function _isStreamInput () {
* such as resize or rotate. * such as resize or rotate.
* *
* Dimensions in the response will respect the `page` and `pages` properties of the * 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. * A `Promise` is returned when `callback` is not provided.
* *
@ -580,9 +578,9 @@ function _isStreamInput () {
* - `size`: Total size of image in bytes, for Stream and Buffer input only * - `size`: Total size of image in bytes, for Stream and Buffer input only
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below) * - `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) * - `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 * - `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 * - `density`: Number of pixels per inch (DPI), if present
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK * - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan * - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
@ -794,7 +792,7 @@ function stats (callback) {
* @module Sharp * @module Sharp
* @private * @private
*/ */
module.exports = function (Sharp) { module.exports = (Sharp) => {
Object.assign(Sharp.prototype, { Object.assign(Sharp.prototype, {
// Private // Private
_inputOptionsFromObject, _inputOptionsFromObject,

View File

@ -1,61 +1,49 @@
// 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'; */
/** /**
* Is this value defined and not null? * Is this value defined and not null?
* @private * @private
*/ */
const defined = function (val) { const defined = (val) => typeof val !== 'undefined' && val !== null;
return typeof val !== 'undefined' && val !== null;
};
/** /**
* Is this value an object? * Is this value an object?
* @private * @private
*/ */
const object = function (val) { const object = (val) => typeof val === 'object';
return typeof val === 'object';
};
/** /**
* Is this value a plain object? * Is this value a plain object?
* @private * @private
*/ */
const plainObject = function (val) { const plainObject = (val) => Object.prototype.toString.call(val) === '[object Object]';
return Object.prototype.toString.call(val) === '[object Object]';
};
/** /**
* Is this value a function? * Is this value a function?
* @private * @private
*/ */
const fn = function (val) { const fn = (val) => typeof val === 'function';
return typeof val === 'function';
};
/** /**
* Is this value a boolean? * Is this value a boolean?
* @private * @private
*/ */
const bool = function (val) { const bool = (val) => typeof val === 'boolean';
return typeof val === 'boolean';
};
/** /**
* Is this value a Buffer object? * Is this value a Buffer object?
* @private * @private
*/ */
const buffer = function (val) { const buffer = (val) => val instanceof Buffer;
return val instanceof Buffer;
};
/** /**
* Is this value a typed array object?. E.g. Uint8Array or Uint8ClampedArray? * Is this value a typed array object?. E.g. Uint8Array or Uint8ClampedArray?
* @private * @private
*/ */
const typedArray = function (val) { const typedArray = (val) => {
if (defined(val)) { if (defined(val)) {
switch (val.constructor) { switch (val.constructor) {
case Uint8Array: case Uint8Array:
@ -78,49 +66,37 @@ const typedArray = function (val) {
* Is this value an ArrayBuffer object? * Is this value an ArrayBuffer object?
* @private * @private
*/ */
const arrayBuffer = function (val) { const arrayBuffer = (val) => val instanceof ArrayBuffer;
return val instanceof ArrayBuffer;
};
/** /**
* Is this value a non-empty string? * Is this value a non-empty string?
* @private * @private
*/ */
const string = function (val) { const string = (val) => typeof val === 'string' && val.length > 0;
return typeof val === 'string' && val.length > 0;
};
/** /**
* Is this value a real number? * Is this value a real number?
* @private * @private
*/ */
const number = function (val) { const number = (val) => typeof val === 'number' && !Number.isNaN(val);
return typeof val === 'number' && !Number.isNaN(val);
};
/** /**
* Is this value an integer? * Is this value an integer?
* @private * @private
*/ */
const integer = function (val) { const integer = (val) => Number.isInteger(val);
return Number.isInteger(val);
};
/** /**
* Is this value within an inclusive given range? * Is this value within an inclusive given range?
* @private * @private
*/ */
const inRange = function (val, min, max) { const inRange = (val, min, max) => val >= min && val <= max;
return val >= min && val <= max;
};
/** /**
* Is this value within the elements of an array? * Is this value within the elements of an array?
* @private * @private
*/ */
const inArray = function (val, list) { const inArray = (val, list) => list.includes(val);
return list.includes(val);
};
/** /**
* Create an Error with a message relating to an invalid parameter. * 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. * @returns {Error} Containing the formatted message.
* @private * @private
*/ */
const invalidParameterError = function (name, expected, actual) { const invalidParameterError = (name, expected, actual) => new Error(
return new Error(
`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}` `Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`
); );
};
/** /**
* Ensures an Error from C++ contains a JS stack. * 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. * @returns {Error} Error with message and stack.
* @private * @private
*/ */
const nativeError = function (native, context) { const nativeError = (native, context) => {
context.message = native.message; context.message = native.message;
return context; return context;
}; };

View File

@ -1,7 +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
'use strict'; */
const { spawnSync } = require('node:child_process'); const { spawnSync } = require('node:child_process');
const { createHash } = require('node:crypto'); const { createHash } = require('node:crypto');
@ -12,13 +12,13 @@ const detectLibc = require('detect-libc');
const { config, engines, optionalDependencies } = require('../package.json'); const { config, engines, optionalDependencies } = require('../package.json');
const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || /* istanbul ignore next */ /* node:coverage ignore next */
config.libvips; const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || config.libvips;
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version; const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
const prebuiltPlatforms = [ const prebuiltPlatforms = [
'darwin-arm64', 'darwin-x64', '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', 'linuxmusl-arm64', 'linuxmusl-x64',
'win32-arm64', 'win32-ia32', 'win32-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 runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`; const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
/* istanbul ignore next */
const buildPlatformArch = () => { const buildPlatformArch = () => {
/* node:coverage ignore next 3 */
if (isEmscripten()) { if (isEmscripten()) {
return 'wasm32'; return 'wasm32';
} }
/* eslint camelcase: ["error", { allow: ["^npm_config_"] }] */
const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env; const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
const libc = typeof npm_config_libc === 'string' ? npm_config_libc : runtimeLibc(); const libc = typeof npm_config_libc === 'string' ? npm_config_libc : runtimeLibc();
return `${npm_config_platform || process.platform}${libc}-${npm_config_arch || process.arch}`; return `${npm_config_platform || process.platform}${libc}-${npm_config_arch || process.arch}`;
@ -56,19 +55,19 @@ const buildSharpLibvipsIncludeDir = () => {
try { try {
return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/include`); return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/include`);
} catch { } catch {
/* node:coverage ignore next 5 */
try { try {
return require('@img/sharp-libvips-dev/include'); return require('@img/sharp-libvips-dev/include');
} catch {} } catch {}
} }
/* istanbul ignore next */
return ''; return '';
}; };
const buildSharpLibvipsCPlusPlusDir = () => { const buildSharpLibvipsCPlusPlusDir = () => {
/* node:coverage ignore next 4 */
try { try {
return require('@img/sharp-libvips-dev/cplusplus'); return require('@img/sharp-libvips-dev/cplusplus');
} catch {} } catch {}
/* istanbul ignore next */
return ''; return '';
}; };
@ -76,16 +75,17 @@ const buildSharpLibvipsLibDir = () => {
try { try {
return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/lib`); return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/lib`);
} catch { } catch {
/* node:coverage ignore next 5 */
try { try {
return require(`@img/sharp-libvips-${buildPlatformArch()}/lib`); return require(`@img/sharp-libvips-${buildPlatformArch()}/lib`);
} catch {} } catch {}
} }
/* istanbul ignore next */
return ''; return '';
}; };
/* node:coverage disable */
const isUnsupportedNodeRuntime = () => { const isUnsupportedNodeRuntime = () => {
/* istanbul ignore next */
if (process.release?.name === 'node' && process.versions) { if (process.release?.name === 'node' && process.versions) {
if (!semverSatisfies(process.versions.node, engines.node)) { if (!semverSatisfies(process.versions.node, engines.node)) {
return { found: process.versions.node, expected: engines.node }; return { found: process.versions.node, expected: engines.node };
@ -93,14 +93,12 @@ const isUnsupportedNodeRuntime = () => {
} }
}; };
/* istanbul ignore next */
const isEmscripten = () => { const isEmscripten = () => {
const { CC } = process.env; const { CC } = process.env;
return Boolean(CC && CC.endsWith('/emcc')); return Boolean(CC?.endsWith('/emcc'));
}; };
const isRosetta = () => { const isRosetta = () => {
/* istanbul ignore next */
if (process.platform === 'darwin' && process.arch === 'x64') { if (process.platform === 'darwin' && process.arch === 'x64') {
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout; const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
return (translated || '').trim() === 'sysctl.proc_translated: 1'; return (translated || '').trim() === 'sysctl.proc_translated: 1';
@ -108,6 +106,8 @@ const isRosetta = () => {
return false; return false;
}; };
/* node:coverage enable */
const sha512 = (s) => createHash('sha512').update(s).digest('hex'); const sha512 = (s) => createHash('sha512').update(s).digest('hex');
const yarnLocator = () => { const yarnLocator = () => {
@ -121,7 +121,8 @@ const yarnLocator = () => {
return ''; return '';
}; };
/* istanbul ignore next */ /* node:coverage disable */
const spawnRebuild = () => const spawnRebuild = () =>
spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, { spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, {
...spawnSyncOptions, ...spawnSyncOptions,
@ -137,16 +138,17 @@ const globalLibvipsVersion = () => {
PKG_CONFIG_PATH: pkgConfigPath() PKG_CONFIG_PATH: pkgConfigPath()
} }
}).stdout; }).stdout;
/* istanbul ignore next */
return (globalLibvipsVersion || '').trim(); return (globalLibvipsVersion || '').trim();
} else { } else {
return ''; return '';
} }
}; };
/* istanbul ignore next */ /* node:coverage enable */
const pkgConfigPath = () => { const pkgConfigPath = () => {
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
/* node:coverage ignore next 4 */
const brewPkgConfigPath = spawnSync( const brewPkgConfigPath = spawnSync(
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2', 'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
spawnSyncOptions spawnSyncOptions
@ -178,13 +180,13 @@ const useGlobalLibvips = (logger) => {
if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) { if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS', logger); return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS', logger);
} }
/* istanbul ignore next */ /* node:coverage ignore next 3 */
if (isRosetta()) { if (isRosetta()) {
return skipSearch(false, 'Rosetta', logger); return skipSearch(false, 'Rosetta', logger);
} }
const globalVipsVersion = globalLibvipsVersion(); const globalVipsVersion = globalLibvipsVersion();
return !!globalVipsVersion && /* istanbul ignore next */ /* node:coverage ignore next */
semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion); return !!globalVipsVersion && semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
}; };
module.exports = { module.exports = {

View File

@ -1,7 +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
'use strict'; */
const is = require('./is'); 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. * 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. * 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 * @example
* const data = await sharp(input).sharpen().toBuffer(); * const data = await sharp(input).sharpen().toBuffer();
@ -485,7 +485,7 @@ function erode (width) {
/** /**
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel. * Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
* *
* See also {@link /api-channel#removealpha|removeAlpha}. * See also {@link /api-channel#removealpha removeAlpha}.
* *
* @example * @example
* await sharp(rgbaInput) * await sharp(rgbaInput)
@ -660,7 +660,7 @@ function normalize (options) {
/** /**
* Perform contrast limiting adaptive histogram equalization * 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. * This will, in general, enhance the clarity of the image by bringing out darker details.
* *
@ -742,9 +742,7 @@ function convolve (kernel) {
} }
// Default scale is sum of kernel values // Default scale is sum of kernel values
if (!is.integer(kernel.scale)) { if (!is.integer(kernel.scale)) {
kernel.scale = kernel.kernel.reduce(function (a, b) { kernel.scale = kernel.kernel.reduce((a, b) => a + b, 0);
return a + b;
}, 0);
} }
// Clip scale to a minimum value of 1 // Clip scale to a minimum value of 1
if (kernel.scale < 1) { if (kernel.scale < 1) {
@ -989,7 +987,7 @@ function modulate (options) {
* @module Sharp * @module Sharp
* @private * @private
*/ */
module.exports = function (Sharp) { module.exports = (Sharp) => {
Object.assign(Sharp.prototype, { Object.assign(Sharp.prototype, {
autoOrient, autoOrient,
rotate, rotate,

View File

@ -1,7 +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
'use strict'; */
const path = require('node:path'); const path = require('node:path');
const is = require('./is'); 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. * Note that raw pixel data is only supported for buffer output.
* *
* By default all metadata will be removed, which includes EXIF-based orientation. * 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. * The caller is responsible for ensuring directory structures and permissions exist.
* *
@ -97,12 +97,12 @@ function toFile (fileOut, callback) {
* Write output to a Buffer. * Write output to a Buffer.
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported. * JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
* *
* 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. * 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. * 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: * `callback`, if present, gets three arguments `(err, data, info)` where:
* - `err` is an error, if any. * - `err` is an error, if any.
@ -256,7 +256,7 @@ function withExifMerge (exif) {
/** /**
* Keep ICC profile from the input image in the output image. * 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 * @since 0.33.0
* *
@ -265,6 +265,13 @@ function withExifMerge (exif) {
* .keepIccProfile() * .keepIccProfile()
* .toBuffer(); * .toBuffer();
* *
* @example
* const cmykOutputWithIccProfile = await sharp(cmykInputWithIccProfile)
* .pipelineColourspace('cmyk')
* .toColourspace('cmyk')
* .keepIccProfile()
* .toBuffer();
*
* @returns {Sharp} * @returns {Sharp}
*/ */
function keepIccProfile () { function keepIccProfile () {
@ -565,7 +572,7 @@ function jpeg (options) {
* Set `palette` to `true` for slower, indexed PNG output. * Set `palette` to `true` for slower, indexed PNG output.
* *
* For 16 bits per pixel output, convert to `rgb16` via * For 16 bits per pixel output, convert to `rgb16` via
* {@link /api-colour#tocolourspace|toColourspace}. * {@link /api-colour/#tocolourspace toColourspace}.
* *
* @example * @example
* // Convert any input to full colour PNG output * // Convert any input to full colour PNG output
@ -845,13 +852,12 @@ function gif (options) {
return this._updateFormatOut('gif', options); return this._updateFormatOut('gif', options);
} }
/* istanbul ignore next */
/** /**
* Use these JP2 options for output image. * Use these JP2 options for output image.
* *
* Requires libvips compiled with support for OpenJPEG. * Requires libvips compiled with support for OpenJPEG.
* The prebuilt binaries do not include this - see * 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 * @example
* // Convert any input to lossless JP2 output * // Convert any input to lossless JP2 output
@ -880,6 +886,7 @@ function gif (options) {
* @throws {Error} Invalid options * @throws {Error} Invalid options
*/ */
function jp2 (options) { function jp2 (options) {
/* node:coverage ignore next 41 */
if (!this.constructor.format.jp2k.output.buffer) { if (!this.constructor.format.jp2k.output.buffer) {
throw errJp2Save(); throw errJp2Save();
} }
@ -959,7 +966,7 @@ function trySetAnimationOptions (source, target) {
/** /**
* Use these TIFF options for output image. * 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. * instead of providing `xres` and `yres` in pixels/mm.
* *
* @example * @example
@ -976,6 +983,7 @@ function trySetAnimationOptions (source, target) {
* @param {number} [options.quality=80] - quality, integer 1-100 * @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format * @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 {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 {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
* @param {boolean} [options.pyramid=false] - write an image pyramid * @param {boolean} [options.pyramid=false] - write an image pyramid
* @param {boolean} [options.tile=false] - write a tiled tiff * @param {boolean} [options.tile=false] - write a tiled tiff
@ -1054,6 +1062,10 @@ function tiff (options) {
throw is.invalidParameterError('compression', 'one of: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k', options.compression); 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 // predictor
if (is.defined(options.predictor)) { if (is.defined(options.predictor)) {
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) { if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
@ -1189,7 +1201,7 @@ function heif (options) {
* *
* Requires libvips compiled with support for libjxl. * Requires libvips compiled with support for libjxl.
* The prebuilt binaries do not include this - see * 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 * @since 0.31.3
* *
@ -1502,7 +1514,6 @@ function _setBooleanOption (key, val) {
* @private * @private
*/ */
function _read () { function _read () {
/* istanbul ignore else */
if (!this.options.streamOut) { if (!this.options.streamOut) {
this.options.streamOut = true; this.options.streamOut = true;
const stack = Error(); const stack = Error();
@ -1619,7 +1630,7 @@ function _pipeline (callback, stack) {
* @module Sharp * @module Sharp
* @private * @private
*/ */
module.exports = function (Sharp) { module.exports = (Sharp) => {
Object.assign(Sharp.prototype, { Object.assign(Sharp.prototype, {
// Public // Public
toFile, toFile,

View File

@ -1,7 +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
'use strict'; */
const is = require('./is'); const is = require('./is');
@ -579,7 +579,7 @@ function trim (options) {
* @module Sharp * @module Sharp
* @private * @private
*/ */
module.exports = function (Sharp) { module.exports = (Sharp) => {
Object.assign(Sharp.prototype, { Object.assign(Sharp.prototype, {
resize, resize,
extend, extend,

View File

@ -1,7 +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
'use strict'; */
// Inspects the runtime environment and exports the relevant sharp.node binary // Inspects the runtime environment and exports the relevant sharp.node binary
@ -17,6 +17,8 @@ const paths = [
'@img/sharp-wasm32/sharp.node' '@img/sharp-wasm32/sharp.node'
]; ];
/* node:coverage disable */
let path, sharp; let path, sharp;
const errors = []; const errors = [];
for (path of paths) { for (path of paths) {
@ -24,12 +26,10 @@ for (path of paths) {
sharp = require(path); sharp = require(path);
break; break;
} catch (err) { } catch (err) {
/* istanbul ignore next */
errors.push(err); errors.push(err);
} }
} }
/* istanbul ignore next */
if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2()) { if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2()) {
const err = new Error('Prebuilt binaries for linux-x64 require v2 microarchitecture'); const err = new Error('Prebuilt binaries for linux-x64 require v2 microarchitecture');
err.code = 'Unsupported CPU'; err.code = 'Unsupported CPU';
@ -37,7 +37,6 @@ if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2())
sharp = null; sharp = null;
} }
/* istanbul ignore next */
if (sharp) { if (sharp) {
module.exports = sharp; module.exports = sharp;
} else { } else {
@ -88,7 +87,7 @@ if (sharp) {
` Found ${libcFound}`, ` Found ${libcFound}`,
` Requires ${libcRequires}` ` Requires ${libcRequires}`
); );
} catch (errEngines) {} } catch (_errEngines) {}
} }
if (isLinux && /\/snap\/core[0-9]{2}/.test(messages)) { if (isLinux && /\/snap\/core[0-9]{2}/.test(messages)) {
help.push( help.push(

View File

@ -1,7 +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
'use strict'; */
const events = require('node:events'); const events = require('node:events');
const detectLibc = require('detect-libc'); const detectLibc = require('detect-libc');
@ -57,7 +57,7 @@ const interpolators = {
let versions = { let versions = {
vips: libvipsVersion.semver vips: libvipsVersion.semver
}; };
/* istanbul ignore next */ /* node:coverage ignore next 15 */
if (!libvipsVersion.isGlobal) { if (!libvipsVersion.isGlobal) {
if (!libvipsVersion.isWasm) { if (!libvipsVersion.isWasm) {
try { try {
@ -75,7 +75,7 @@ if (!libvipsVersion.isGlobal) {
} }
versions.sharp = require('../package.json').version; versions.sharp = require('../package.json').version;
/* istanbul ignore next */ /* node:coverage ignore next 5 */
if (versions.heif && format.heif) { if (versions.heif && format.heif) {
// Prebuilt binaries provide AV1 // Prebuilt binaries provide AV1
format.heif.input.fileSuffix = ['.avif']; format.heif.input.fileSuffix = ['.avif'];
@ -136,7 +136,7 @@ cache(true);
* and these are independent of the value set here. * and these are independent of the value set here.
* *
* :::note * :::note
* Further {@link /performance|control over performance} is available. * Further {@link /performance/ control over performance} is available.
* ::: * :::
* *
* @example * @example
@ -150,7 +150,7 @@ cache(true);
function concurrency (concurrency) { function concurrency (concurrency) {
return sharp.concurrency(is.integer(concurrency) ? concurrency : null); return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
} }
/* istanbul ignore next */ /* node:coverage ignore next 7 */
if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) { if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
// Reduce default concurrency to 1 when using glibc memory allocator // Reduce default concurrency to 1 when using glibc memory allocator
sharp.concurrency(1); sharp.concurrency(1);
@ -277,7 +277,7 @@ function unblock (options) {
* @module Sharp * @module Sharp
* @private * @private
*/ */
module.exports = function (Sharp) { module.exports = (Sharp) => {
Sharp.cache = cache; Sharp.cache = cache;
Sharp.concurrency = concurrency; Sharp.concurrency = concurrency;
Sharp.counters = counters; Sharp.counters = counters;

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-darwin-arm64", "name": "@img/sharp-darwin-arm64",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with macOS 64-bit ARM", "description": "Prebuilt sharp for use with macOS 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-darwin-arm64": "1.2.3" "@img/sharp-libvips-darwin-arm64": "1.2.4"
}, },
"files": [ "files": [
"lib" "lib"

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-darwin-x64", "name": "@img/sharp-darwin-x64",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with macOS x64", "description": "Prebuilt sharp for use with macOS x64",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-darwin-x64": "1.2.3" "@img/sharp-libvips-darwin-x64": "1.2.4"
}, },
"files": [ "files": [
"lib" "lib"

View File

@ -1,7 +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
'use strict'; */
// Populate the npm package for the current platform with the local build // Populate the npm package for the current platform with the local build

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-arm", "name": "@img/sharp-linux-arm",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)", "description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linux-arm": "1.2.3" "@img/sharp-libvips-linux-arm": "1.2.4"
}, },
"files": [ "files": [
"lib" "lib"

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-arm64", "name": "@img/sharp-linux-arm64",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Linux (glibc) 64-bit ARM", "description": "Prebuilt sharp for use with Linux (glibc) 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linux-arm64": "1.2.3" "@img/sharp-libvips-linux-arm64": "1.2.4"
}, },
"files": [ "files": [
"lib" "lib"

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-ppc64", "name": "@img/sharp-linux-ppc64",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Linux (glibc) ppc64", "description": "Prebuilt sharp for use with Linux (glibc) ppc64",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linux-ppc64": "1.2.3" "@img/sharp-libvips-linux-ppc64": "1.2.4"
}, },
"files": [ "files": [
"lib" "lib"

View File

@ -0,0 +1,46 @@
{
"name": "@img/sharp-linux-riscv64",
"version": "0.34.5",
"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.2.4"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-linux-riscv64.node",
"./package": "./package.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"config": {
"glibc": ">=2.41"
},
"os": [
"linux"
],
"libc": [
"glibc"
],
"cpu": [
"riscv64"
]
}

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-s390x", "name": "@img/sharp-linux-s390x",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Linux (glibc) s390x", "description": "Prebuilt sharp for use with Linux (glibc) s390x",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linux-s390x": "1.2.3" "@img/sharp-libvips-linux-s390x": "1.2.4"
}, },
"files": [ "files": [
"lib" "lib"

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-x64", "name": "@img/sharp-linux-x64",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Linux (glibc) x64", "description": "Prebuilt sharp for use with Linux (glibc) x64",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linux-x64": "1.2.3" "@img/sharp-libvips-linux-x64": "1.2.4"
}, },
"files": [ "files": [
"lib" "lib"

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linuxmusl-arm64", "name": "@img/sharp-linuxmusl-arm64",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Linux (musl) 64-bit ARM", "description": "Prebuilt sharp for use with Linux (musl) 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linuxmusl-arm64": "1.2.3" "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
}, },
"files": [ "files": [
"lib" "lib"

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linuxmusl-x64", "name": "@img/sharp-linuxmusl-x64",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Linux (musl) x64", "description": "Prebuilt sharp for use with Linux (musl) x64",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -15,7 +15,7 @@
}, },
"preferUnplugged": true, "preferUnplugged": true,
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-libvips-linuxmusl-x64": "1.2.3" "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
}, },
"files": [ "files": [
"lib" "lib"

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp", "name": "@img/sharp",
"version": "0.34.4-rc.4", "version": "0.34.5",
"private": "true", "private": "true",
"workspaces": [ "workspaces": [
"darwin-arm64", "darwin-arm64",
@ -8,6 +8,7 @@
"linux-arm", "linux-arm",
"linux-arm64", "linux-arm64",
"linux-ppc64", "linux-ppc64",
"linux-riscv64",
"linux-s390x", "linux-s390x",
"linux-x64", "linux-x64",
"linuxmusl-arm64", "linuxmusl-arm64",

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-wasm32", "name": "@img/sharp-wasm32",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with wasm32", "description": "Prebuilt sharp for use with wasm32",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
@ -31,7 +31,7 @@
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
"dependencies": { "dependencies": {
"@emnapi/runtime": "^1.5.0" "@emnapi/runtime": "^1.7.0"
}, },
"cpu": [ "cpu": [
"wasm32" "wasm32"

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-win32-arm64", "name": "@img/sharp-win32-arm64",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Windows 64-bit ARM", "description": "Prebuilt sharp for use with Windows 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-win32-ia32", "name": "@img/sharp-win32-ia32",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Windows x86 (32-bit)", "description": "Prebuilt sharp for use with Windows x86 (32-bit)",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",

View File

@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-win32-x64", "name": "@img/sharp-win32-x64",
"version": "0.34.4-rc.4", "version": "0.34.5",
"description": "Prebuilt sharp for use with Windows x64", "description": "Prebuilt sharp for use with Windows x64",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",

View File

@ -1,7 +1,7 @@
{ {
"name": "sharp", "name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.34.4-rc.4", "version": "0.34.5",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
"contributors": [ "contributors": [
@ -92,14 +92,16 @@
"Don Denton <don@happycollision.com>" "Don Denton <don@happycollision.com>"
], ],
"scripts": { "scripts": {
"install": "node install/check.js", "build": "node install/build.js",
"install": "node install/check.js || npm run build",
"clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*", "clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*",
"test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types", "test": "npm run lint && npm run test-unit",
"test-lint": "semistandard && cpplint", "lint": "npm run lint-cpp && npm run lint-js && npm run lint-types",
"test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha", "lint-cpp": "cpplint --quiet src/*.h src/*.cc",
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"", "lint-js": "biome lint",
"lint-types": "tsd --files ./test/types/sharp.test-d.ts",
"test-leak": "./test/leak/leak.sh", "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-from-local-build": "node npm/from-local-build.js",
"package-release-notes": "node npm/release-notes.js", "package-release-notes": "node npm/release-notes.js",
"docs-build": "node docs/build.mjs", "docs-build": "node docs/build.mjs",
@ -138,53 +140,52 @@
], ],
"dependencies": { "dependencies": {
"@img/colour": "^1.0.0", "@img/colour": "^1.0.0",
"detect-libc": "^2.1.0", "detect-libc": "^2.1.2",
"semver": "^7.7.2" "semver": "^7.7.3"
}, },
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-darwin-arm64": "0.34.4-rc.4", "@img/sharp-darwin-arm64": "0.34.5",
"@img/sharp-darwin-x64": "0.34.4-rc.4", "@img/sharp-darwin-x64": "0.34.5",
"@img/sharp-libvips-darwin-arm64": "1.2.3", "@img/sharp-libvips-darwin-arm64": "1.2.4",
"@img/sharp-libvips-darwin-x64": "1.2.3", "@img/sharp-libvips-darwin-x64": "1.2.4",
"@img/sharp-libvips-linux-arm": "1.2.3", "@img/sharp-libvips-linux-arm": "1.2.4",
"@img/sharp-libvips-linux-arm64": "1.2.3", "@img/sharp-libvips-linux-arm64": "1.2.4",
"@img/sharp-libvips-linux-ppc64": "1.2.3", "@img/sharp-libvips-linux-ppc64": "1.2.4",
"@img/sharp-libvips-linux-s390x": "1.2.3", "@img/sharp-libvips-linux-riscv64": "1.2.4",
"@img/sharp-libvips-linux-x64": "1.2.3", "@img/sharp-libvips-linux-s390x": "1.2.4",
"@img/sharp-libvips-linuxmusl-arm64": "1.2.3", "@img/sharp-libvips-linux-x64": "1.2.4",
"@img/sharp-libvips-linuxmusl-x64": "1.2.3", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
"@img/sharp-linux-arm": "0.34.4-rc.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
"@img/sharp-linux-arm64": "0.34.4-rc.4", "@img/sharp-linux-arm": "0.34.5",
"@img/sharp-linux-ppc64": "0.34.4-rc.4", "@img/sharp-linux-arm64": "0.34.5",
"@img/sharp-linux-s390x": "0.34.4-rc.4", "@img/sharp-linux-ppc64": "0.34.5",
"@img/sharp-linux-x64": "0.34.4-rc.4", "@img/sharp-linux-riscv64": "0.34.5",
"@img/sharp-linuxmusl-arm64": "0.34.4-rc.4", "@img/sharp-linux-s390x": "0.34.5",
"@img/sharp-linuxmusl-x64": "0.34.4-rc.4", "@img/sharp-linux-x64": "0.34.5",
"@img/sharp-wasm32": "0.34.4-rc.4", "@img/sharp-linuxmusl-arm64": "0.34.5",
"@img/sharp-win32-arm64": "0.34.4-rc.4", "@img/sharp-linuxmusl-x64": "0.34.5",
"@img/sharp-win32-ia32": "0.34.4-rc.4", "@img/sharp-wasm32": "0.34.5",
"@img/sharp-win32-x64": "0.34.4-rc.4" "@img/sharp-win32-arm64": "0.34.5",
"@img/sharp-win32-ia32": "0.34.5",
"@img/sharp-win32-x64": "0.34.5"
}, },
"devDependencies": { "devDependencies": {
"@emnapi/runtime": "^1.5.0", "@biomejs/biome": "^2.3.4",
"@img/sharp-libvips-dev": "1.2.3", "@cpplint/cli": "^0.1.0",
"@img/sharp-libvips-dev-wasm32": "1.2.3", "@emnapi/runtime": "^1.7.0",
"@img/sharp-libvips-win32-arm64": "1.2.3", "@img/sharp-libvips-dev": "1.2.4",
"@img/sharp-libvips-win32-ia32": "1.2.3", "@img/sharp-libvips-dev-wasm32": "1.2.4",
"@img/sharp-libvips-win32-x64": "1.2.3", "@img/sharp-libvips-win32-arm64": "1.2.4",
"@img/sharp-libvips-win32-ia32": "1.2.4",
"@img/sharp-libvips-win32-x64": "1.2.4",
"@types/node": "*", "@types/node": "*",
"cc": "^3.0.1", "emnapi": "^1.7.0",
"emnapi": "^1.5.0",
"exif-reader": "^2.0.2", "exif-reader": "^2.0.2",
"extract-zip": "^2.0.1", "extract-zip": "^2.0.1",
"icc": "^3.0.0", "icc": "^3.0.0",
"jsdoc-to-markdown": "^9.1.2", "jsdoc-to-markdown": "^9.1.3",
"license-checker": "^25.0.1",
"mocha": "^11.7.2",
"node-addon-api": "^8.5.0", "node-addon-api": "^8.5.0",
"node-gyp": "^11.4.2", "node-gyp": "^11.5.0",
"nyc": "^17.1.0",
"semistandard": "^17.0.0",
"tar-fs": "^3.1.1", "tar-fs": "^3.1.1",
"tsd": "^0.33.0" "tsd": "^0.33.0"
}, },
@ -193,28 +194,9 @@
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
"config": { "config": {
"libvips": ">=8.17.2" "libvips": ">=8.17.3"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "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
View 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

View File

@ -120,7 +120,7 @@
'conditions': [ 'conditions': [
['use_global_libvips == "true"', { ['use_global_libvips == "true"', {
# Use pkg-config for include and lib # Use pkg-config for include and lib
'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s\/-I//g)'], 'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s/-I//g)'],
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)'], 'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)'],
'defines': [ 'defines': [
'SHARP_USE_GLOBAL_LIBVIPS' 'SHARP_USE_GLOBAL_LIBVIPS'

View File

@ -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 <cstdlib>
#include <string>
#include <string.h>
#include <vector>
#include <queue>
#include <map> #include <map>
#include <mutex> // NOLINT(build/c++11) #include <mutex>
#include <queue>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include <napi.h> #include <napi.h>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "./common.h"
using vips::VImage; using vips::VImage;

View File

@ -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_ #ifndef SRC_COMMON_H_
#define SRC_COMMON_H_ #define SRC_COMMON_H_
#include <atomic>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <utility>
#include <vector> #include <vector>
#include <atomic>
#include <napi.h> #include <napi.h>
#include <vips/vips8> #include <vips/vips8>
@ -16,8 +19,8 @@
#if (VIPS_MAJOR_VERSION < 8) || \ #if (VIPS_MAJOR_VERSION < 8) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 17) || \ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 17) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 17 && VIPS_MICRO_VERSION < 2) (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 17 && VIPS_MICRO_VERSION < 3)
#error "libvips version 8.17.2+ is required - please see https://sharp.pixelplumbing.com/install" #error "libvips version 8.17.3+ is required - please see https://sharp.pixelplumbing.com/install"
#endif #endif
#if defined(__has_include) #if defined(__has_include)
@ -30,7 +33,7 @@ using vips::VImage;
namespace sharp { namespace sharp {
struct InputDescriptor { // NOLINT(runtime/indentation_namespace) struct InputDescriptor {
std::string name; std::string name;
std::string file; std::string file;
bool autoOrient; bool autoOrient;

View File

@ -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 */ /* global Module, ENV, _vips_shutdown, _uv_library_shutdown */

View File

@ -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 <cmath>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
#include <napi.h> #include <napi.h>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "./common.h"
#include "metadata.h" #include "./metadata.h"
static void* readPNGComment(VipsImage *image, const char *field, GValue *value, void *p); static void* readPNGComment(VipsImage *image, const char *field, GValue *value, void *p);
@ -215,10 +219,10 @@ class MetadataWorker : public Napi::AsyncWorker {
if (!baton->levels.empty()) { if (!baton->levels.empty()) {
int i = 0; int i = 0;
Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size())); 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); Napi::Object level = Napi::Object::New(env);
level.Set("width", l.first); level.Set("width", width);
level.Set("height", l.second); level.Set("height", height);
levels.Set(i++, level); levels.Set(i++, level);
} }
info.Set("levels", levels); info.Set("levels", levels);
@ -275,10 +279,10 @@ class MetadataWorker : public Napi::AsyncWorker {
if (baton->comments.size() > 0) { if (baton->comments.size() > 0) {
int i = 0; int i = 0;
Napi::Array comments = Napi::Array::New(env, baton->comments.size()); 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); Napi::Object comment = Napi::Object::New(env);
comment.Set("keyword", c.first); comment.Set("keyword", keyword);
comment.Set("text", c.second); comment.Set("text", text);
comments.Set(i++, comment); comments.Set(i++, comment);
} }
info.Set("comments", comments); info.Set("comments", comments);

View File

@ -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_ #ifndef SRC_METADATA_H_
#define SRC_METADATA_H_ #define SRC_METADATA_H_
#include <string> #include <string>
#include <vector>
#include <napi.h> #include <napi.h>
#include "./common.h" #include "./common.h"

View File

@ -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 <algorithm>
#include <functional> #include <functional>
@ -8,8 +10,8 @@
#include <vector> #include <vector>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "./common.h"
#include "operations.h" #include "./operations.h"
using vips::VImage; using vips::VImage;
using vips::VError; using vips::VError;

View File

@ -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_ #ifndef SRC_OPERATIONS_H_
#define SRC_OPERATIONS_H_ #define SRC_OPERATIONS_H_
@ -8,6 +10,7 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <tuple> #include <tuple>
#include <vector>
#include <vips/vips8> #include <vips/vips8>
using vips::VImage; using vips::VImage;

View File

@ -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 <algorithm>
#include <cmath> #include <cmath>
#include <filesystem> #include <filesystem> // NOLINT(build/c++17)
#include <map> #include <map>
#include <memory> #include <memory>
#include <numeric> #include <numeric>
@ -17,9 +19,9 @@
#include <vips/vips8> #include <vips/vips8>
#include <napi.h> #include <napi.h>
#include "common.h" #include "./common.h"
#include "operations.h" #include "./operations.h"
#include "pipeline.h" #include "./pipeline.h"
class PipelineWorker : public Napi::AsyncWorker { class PipelineWorker : public Napi::AsyncWorker {
public: public:
@ -453,12 +455,10 @@ class PipelineWorker : public Napi::AsyncWorker {
std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground, shouldPremultiplyAlpha); std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground, shouldPremultiplyAlpha);
// Embed // Embed
int left; const auto& [left, top] = sharp::CalculateEmbedPosition(
int top;
std::tie(left, top) = sharp::CalculateEmbedPosition(
inputWidth, inputHeight, baton->width, baton->height, baton->position); inputWidth, inputHeight, baton->width, baton->height, baton->position);
int width = std::max(inputWidth, baton->width); const int width = std::max(inputWidth, baton->width);
int height = std::max(inputHeight, baton->height); const int height = std::max(inputHeight, baton->height);
image = nPages > 1 image = nPages > 1
? sharp::EmbedMultiPage(image, ? sharp::EmbedMultiPage(image,
@ -477,13 +477,10 @@ class PipelineWorker : public Napi::AsyncWorker {
// Crop // Crop
if (baton->position < 9) { if (baton->position < 9) {
// Gravity-based crop // Gravity-based crop
int left; const auto& [left, top] = sharp::CalculateCrop(
int top;
std::tie(left, top) = sharp::CalculateCrop(
inputWidth, inputHeight, baton->width, baton->height, baton->position); inputWidth, inputHeight, baton->width, baton->height, baton->position);
int width = std::min(inputWidth, baton->width); const int width = std::min(inputWidth, baton->width);
int height = std::min(inputHeight, baton->height); const int height = std::min(inputHeight, baton->height);
image = nPages > 1 image = nPages > 1
? sharp::CropMultiPage(image, ? sharp::CropMultiPage(image,
@ -795,20 +792,14 @@ class PipelineWorker : public Napi::AsyncWorker {
image = sharp::EnsureAlpha(image, baton->ensureAlpha); image = sharp::EnsureAlpha(image, baton->ensureAlpha);
} }
// Convert image to sRGB, if not already // Ensure output colour space
if (sharp::Is16Bit(image.interpretation())) { if (sharp::Is16Bit(image.interpretation())) {
image = image.cast(VIPS_FORMAT_USHORT); image = image.cast(VIPS_FORMAT_USHORT);
} }
if (image.interpretation() != baton->colourspace) { 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())); image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
// Transform colours from embedded profile to output profile if (inputProfile.first != nullptr && baton->withIccProfile.empty()) {
if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) && baton->colourspacePipeline != VIPS_INTERPRETATION_CMYK && image = sharp::SetProfile(image, inputProfile);
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));
} }
} }
@ -843,8 +834,6 @@ class PipelineWorker : public Napi::AsyncWorker {
} catch(...) { } catch(...) {
sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid profile", nullptr); 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 // Negate the colours in the image
@ -866,8 +855,8 @@ class PipelineWorker : public Napi::AsyncWorker {
if (!baton->withExifMerge) { if (!baton->withExifMerge) {
image = sharp::RemoveExif(image); image = sharp::RemoveExif(image);
} }
for (const auto& s : baton->withExif) { for (const auto& [key, value] : baton->withExif) {
image.set(s.first.data(), s.second.data()); image.set(key.c_str(), value.c_str());
} }
} }
// XMP buffer // XMP buffer
@ -1009,6 +998,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("Q", baton->tiffQuality) ->set("Q", baton->tiffQuality)
->set("bitdepth", baton->tiffBitdepth) ->set("bitdepth", baton->tiffBitdepth)
->set("compression", baton->tiffCompression) ->set("compression", baton->tiffCompression)
->set("bigtiff", baton->tiffBigtiff)
->set("miniswhite", baton->tiffMiniswhite) ->set("miniswhite", baton->tiffMiniswhite)
->set("predictor", baton->tiffPredictor) ->set("predictor", baton->tiffPredictor)
->set("pyramid", baton->tiffPyramid) ->set("pyramid", baton->tiffPyramid)
@ -1211,6 +1201,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("Q", baton->tiffQuality) ->set("Q", baton->tiffQuality)
->set("bitdepth", baton->tiffBitdepth) ->set("bitdepth", baton->tiffBitdepth)
->set("compression", baton->tiffCompression) ->set("compression", baton->tiffCompression)
->set("bigtiff", baton->tiffBigtiff)
->set("miniswhite", baton->tiffMiniswhite) ->set("miniswhite", baton->tiffMiniswhite)
->set("predictor", baton->tiffPredictor) ->set("predictor", baton->tiffPredictor)
->set("pyramid", baton->tiffPyramid) ->set("pyramid", baton->tiffPyramid)
@ -1275,10 +1266,15 @@ class PipelineWorker : public Napi::AsyncWorker {
char const *what = err.what(); char const *what = err.what();
if (what && what[0]) { if (what && what[0]) {
(baton->err).append(what); (baton->err).append(what);
} else {
if (baton->input->failOn == VIPS_FAIL_ON_WARNING) {
(baton->err).append("Warning treated as error due to failOn setting");
baton->errUseWarning = true;
} else { } else {
(baton->err).append("Unknown error"); (baton->err).append("Unknown error");
} }
} }
}
// Clean up libvips' per-request data and threads // Clean up libvips' per-request data and threads
vips_error_clear(); vips_error_clear();
vips_thread_shutdown(); vips_thread_shutdown();
@ -1291,7 +1287,11 @@ class PipelineWorker : public Napi::AsyncWorker {
// Handle warnings // Handle warnings
std::string warning = sharp::VipsWarningPop(); std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) { while (!warning.empty()) {
if (baton->errUseWarning) {
(baton->err).append("\n").append(warning);
} else {
debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) }); debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
}
warning = sharp::VipsWarningPop(); warning = sharp::VipsWarningPop();
} }
@ -1435,11 +1435,11 @@ class PipelineWorker : public Napi::AsyncWorker {
std::string std::string
AssembleSuffixString(std::string extname, std::vector<std::pair<std::string, std::string>> options) { AssembleSuffixString(std::string extname, std::vector<std::pair<std::string, std::string>> options) {
std::string argument; std::string argument;
for (auto const &option : options) { for (const auto& [key, value] : options) {
if (!argument.empty()) { if (!argument.empty()) {
argument += ","; argument += ",";
} }
argument += option.first + "=" + option.second; argument += key + "=" + value;
} }
return extname + "[" + argument + "]"; return extname + "[" + argument + "]";
} }
@ -1750,6 +1750,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->gifReuse = sharp::AttrAsBool(options, "gifReuse"); baton->gifReuse = sharp::AttrAsBool(options, "gifReuse");
baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive"); baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality"); baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
baton->tiffBigtiff = sharp::AttrAsBool(options, "tiffBigtiff");
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid"); baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
baton->tiffMiniswhite = sharp::AttrAsBool(options, "tiffMiniswhite"); baton->tiffMiniswhite = sharp::AttrAsBool(options, "tiffMiniswhite");
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth"); baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");

View File

@ -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_ #ifndef SRC_PIPELINE_H_
#define SRC_PIPELINE_H_ #define SRC_PIPELINE_H_
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector>
#include <unordered_map> #include <unordered_map>
#include <vector>
#include <napi.h> #include <napi.h>
#include <vips/vips8> #include <vips/vips8>
@ -175,6 +177,7 @@ struct PipelineBaton {
bool gifProgressive; bool gifProgressive;
int tiffQuality; int tiffQuality;
VipsForeignTiffCompression tiffCompression; VipsForeignTiffCompression tiffCompression;
bool tiffBigtiff;
VipsForeignTiffPredictor tiffPredictor; VipsForeignTiffPredictor tiffPredictor;
bool tiffPyramid; bool tiffPyramid;
int tiffBitdepth; int tiffBitdepth;
@ -197,6 +200,7 @@ struct PipelineBaton {
bool jxlLossless; bool jxlLossless;
VipsBandFormat rawDepth; VipsBandFormat rawDepth;
std::string err; std::string err;
bool errUseWarning;
int keepMetadata; int keepMetadata;
int withMetadataOrientation; int withMetadataOrientation;
double withMetadataDensity; double withMetadataDensity;
@ -350,6 +354,7 @@ struct PipelineBaton {
gifProgressive(false), gifProgressive(false),
tiffQuality(80), tiffQuality(80),
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG), tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
tiffBigtiff(false),
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL), tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
tiffPyramid(false), tiffPyramid(false),
tiffBitdepth(8), tiffBitdepth(8),
@ -371,6 +376,7 @@ struct PipelineBaton {
jxlEffort(7), jxlEffort(7),
jxlLossless(false), jxlLossless(false),
rawDepth(VIPS_FORMAT_UCHAR), rawDepth(VIPS_FORMAT_UCHAR),
errUseWarning(false),
keepMetadata(0), keepMetadata(0),
withMetadataOrientation(-1), withMetadataOrientation(-1),
withMetadataDensity(0.0), withMetadataDensity(0.0),

View File

@ -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 <napi.h>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "./common.h"
#include "metadata.h" #include "./metadata.h"
#include "pipeline.h" #include "./pipeline.h"
#include "utilities.h" #include "./stats.h"
#include "stats.h" #include "./utilities.h"
Napi::Object init(Napi::Env env, Napi::Object exports) { Napi::Object init(Napi::Env env, Napi::Object exports) {
static std::once_flag sharp_vips_init_once; static std::once_flag sharp_vips_init_once;

View File

@ -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 <iostream>
#include <numeric>
#include <string>
#include <vector>
#include <napi.h> #include <napi.h>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "./common.h"
#include "stats.h" #include "./stats.h"
class StatsWorker : public Napi::AsyncWorker { class StatsWorker : public Napi::AsyncWorker {
public: public:

View File

@ -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_ #ifndef SRC_STATS_H_
#define SRC_STATS_H_ #define SRC_STATS_H_
#include <string> #include <string>
#include <vector>
#include <napi.h> #include <napi.h>
#include "./common.h" #include "./common.h"
@ -24,7 +27,7 @@ struct ChannelStats {
ChannelStats(int minVal, int maxVal, double sumVal, double squaresSumVal, ChannelStats(int minVal, int maxVal, double sumVal, double squaresSumVal,
double meanVal, double stdevVal, int minXVal, int minYVal, int maxXVal, int maxYVal): 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) {} mean(meanVal), stdev(stdevVal), minX(minXVal), minY(minYVal), maxX(maxXVal), maxY(maxYVal) {}
}; };

View File

@ -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 <cmath>
#include <string>
#include <cstdio> #include <cstdio>
#include <string>
#include <napi.h> #include <napi.h>
#include <vips/vips8> #include <vips/vips8>
#include <vips/vector.h> #include <vips/vector.h>
#include "common.h" #include "./common.h"
#include "operations.h" #include "./operations.h"
#include "utilities.h" #include "./utilities.h"
/* /*
Get and set cache limits Get and set cache limits

View File

@ -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_ #ifndef SRC_UTILITIES_H_
#define SRC_UTILITIES_H_ #define SRC_UTILITIES_H_

View File

@ -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();
}
}
};

View File

@ -15,7 +15,7 @@ RUN apt-get install -y imagemagick libmagick++-dev graphicsmagick
# Install sharp # Install sharp
RUN mkdir /tmp/sharp RUN mkdir /tmp/sharp
RUN cd /tmp && git clone --single-branch --branch $BRANCH https://github.com/lovell/sharp.git 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 # Install benchmark test
RUN cd /tmp/sharp/test/bench && npm install --omit optional RUN cd /tmp/sharp/test/bench && npm install --omit optional

View File

@ -1,11 +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
'use strict'; */
process.env.UV_THREADPOOL_SIZE = 64; process.env.UV_THREADPOOL_SIZE = 64;
const assert = require('assert'); const assert = require('node:assert');
const async = require('async'); const async = require('async');
const sharp = require('../../'); const sharp = require('../../');
@ -16,32 +16,29 @@ const height = 480;
sharp.concurrency(1); sharp.concurrency(1);
const timer = setInterval(function () { const timer = setInterval(() => {
console.dir(sharp.counters()); console.dir(sharp.counters());
}, 100); }, 100);
async.mapSeries([1, 1, 2, 4, 8, 16, 32, 64], function (parallelism, next) { async.mapSeries([1, 1, 2, 4, 8, 16, 32, 64], (parallelism, next) => {
const start = new Date().getTime(); const start = Date.now();
async.times(parallelism, async.times(parallelism,
function (id, callback) { (_id, callback) => {
/* jslint unused: false */ sharp(fixtures.inputJpg).resize(width, height).toBuffer((err, buffer) => {
sharp(fixtures.inputJpg).resize(width, height).toBuffer(function (err, buffer) {
buffer = null; buffer = null;
callback(err, new Date().getTime() - start); callback(err, Date.now() - start);
}); });
}, },
function (err, ids) { (err, ids) => {
assert(!err); assert(!err);
assert(ids.length === parallelism); assert(ids.length === parallelism);
ids.sort(); ids.sort();
const mean = ids.reduce(function (a, b) { const mean = ids.reduce((a, b) => a + b) / ids.length;
return a + b; console.log(`${parallelism} parallel calls: fastest=${ids[0]}ms slowest=${ids[ids.length - 1]}ms mean=${mean}ms`);
}) / ids.length;
console.log(parallelism + ' parallel calls: fastest=' + ids[0] + 'ms slowest=' + ids[ids.length - 1] + 'ms mean=' + mean + 'ms');
next(); next();
} }
); );
}, function () { }, () => {
clearInterval(timer); clearInterval(timer);
console.dir(sharp.counters()); console.dir(sharp.counters());
}); });

View File

@ -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('node:fs');
const { execSync } = require('node:child_process');
const fs = require('fs');
const { execSync } = require('child_process');
const async = require('async'); const async = require('async');
const Benchmark = require('benchmark'); const Benchmark = require('benchmark');
@ -12,7 +12,7 @@ const Benchmark = require('benchmark');
const safeRequire = (name) => { const safeRequire = (name) => {
try { try {
return require(name); return require(name);
} catch (err) {} } catch (_err) {}
return null; return null;
}; };
@ -45,13 +45,13 @@ console.log(`Detected ${physicalCores} physical cores`);
sharp.concurrency(physicalCores); sharp.concurrency(physicalCores);
async.series({ async.series({
jpeg: function (callback) { jpeg: (callback) => {
const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg); const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
const jpegSuite = new Benchmark.Suite('jpeg'); const jpegSuite = new Benchmark.Suite('jpeg');
// jimp // jimp
jpegSuite.add('jimp-buffer-buffer', { jpegSuite.add('jimp-buffer-buffer', {
defer: true, defer: true,
fn: async function (deferred) { fn: async (deferred) => {
const image = await Jimp.read(inputJpgBuffer); const image = await Jimp.read(inputJpgBuffer);
await image await image
.resize({ w: width, h: height, mode: Jimp.RESIZE_BICUBIC }) .resize({ w: width, h: height, mode: Jimp.RESIZE_BICUBIC })
@ -60,7 +60,7 @@ async.series({
} }
}).add('jimp-file-file', { }).add('jimp-file-file', {
defer: true, defer: true,
fn: async function (deferred) { fn: async (deferred) => {
const image = await Jimp.read(fixtures.inputJpg); const image = await Jimp.read(fixtures.inputJpg);
await image await image
.resize({ w: width, h: height, mode: Jimp.RESIZE_BICUBIC }) .resize({ w: width, h: height, mode: Jimp.RESIZE_BICUBIC })
@ -71,14 +71,14 @@ async.series({
// mapnik // mapnik
mapnik && jpegSuite.add('mapnik-file-file', { mapnik && jpegSuite.add('mapnik-file-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
mapnik.Image.open(fixtures.inputJpg, function (err, img) { mapnik.Image.open(fixtures.inputJpg, (err, img) => {
if (err) throw err; if (err) throw err;
img img
.resize(width, height, { .resize(width, height, {
scaling_method: mapnik.imageScaling.lanczos scaling_method: mapnik.imageScaling.lanczos
}) })
.save(outputJpg, 'jpeg:quality=80', function (err) { .save(outputJpg, 'jpeg:quality=80', (err) => {
if (err) throw err; if (err) throw err;
deferred.resolve(); deferred.resolve();
}); });
@ -86,14 +86,14 @@ async.series({
} }
}).add('mapnik-buffer-buffer', { }).add('mapnik-buffer-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
mapnik.Image.fromBytes(inputJpgBuffer, { max_size: 3000 }, function (err, img) { mapnik.Image.fromBytes(inputJpgBuffer, { max_size: 3000 }, (err, img) => {
if (err) throw err; if (err) throw err;
img img
.resize(width, height, { .resize(width, height, {
scaling_method: mapnik.imageScaling.lanczos scaling_method: mapnik.imageScaling.lanczos
}) })
.encode('jpeg:quality=80', function (err) { .encode('jpeg:quality=80', (err) => {
if (err) throw err; if (err) throw err;
deferred.resolve(); deferred.resolve();
}); });
@ -103,7 +103,7 @@ async.series({
// imagemagick // imagemagick
jpegSuite.add('imagemagick-file-file', { jpegSuite.add('imagemagick-file-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
imagemagick.resize({ imagemagick.resize({
srcPath: fixtures.inputJpg, srcPath: fixtures.inputJpg,
dstPath: outputJpg, dstPath: outputJpg,
@ -112,7 +112,7 @@ async.series({
height, height,
format: 'jpg', format: 'jpg',
filter: 'Lanczos' filter: 'Lanczos'
}, function (err) { }, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -124,12 +124,12 @@ async.series({
// gm // gm
jpegSuite.add('gm-buffer-file', { jpegSuite.add('gm-buffer-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
gm(inputJpgBuffer) gm(inputJpgBuffer)
.filter('Lanczos') .filter('Lanczos')
.resize(width, height) .resize(width, height)
.quality(80) .quality(80)
.write(outputJpg, function (err) { .write(outputJpg, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -139,12 +139,12 @@ async.series({
} }
}).add('gm-buffer-buffer', { }).add('gm-buffer-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
gm(inputJpgBuffer) gm(inputJpgBuffer)
.filter('Lanczos') .filter('Lanczos')
.resize(width, height) .resize(width, height)
.quality(80) .quality(80)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -154,12 +154,12 @@ async.series({
} }
}).add('gm-file-file', { }).add('gm-file-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
gm(fixtures.inputJpg) gm(fixtures.inputJpg)
.filter('Lanczos') .filter('Lanczos')
.resize(width, height) .resize(width, height)
.quality(80) .quality(80)
.write(outputJpg, function (err) { .write(outputJpg, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -169,12 +169,12 @@ async.series({
} }
}).add('gm-file-buffer', { }).add('gm-file-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
gm(fixtures.inputJpg) gm(fixtures.inputJpg)
.filter('Lanczos') .filter('Lanczos')
.resize(width, height) .resize(width, height)
.quality(80) .quality(80)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -186,17 +186,17 @@ async.series({
// tfjs // tfjs
tfjs && jpegSuite.add('tfjs-node-buffer-buffer', { tfjs && jpegSuite.add('tfjs-node-buffer-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
const decoded = tfjs.node.decodeJpeg(inputJpgBuffer); const decoded = tfjs.node.decodeJpeg(inputJpgBuffer);
const resized = tfjs.image.resizeBilinear(decoded, [height, width]); const resized = tfjs.image.resizeBilinear(decoded, [height, width]);
tfjs tfjs
.node .node
.encodeJpeg(resized, 'rgb', 80) .encodeJpeg(resized, 'rgb', 80)
.then(function () { .then(() => {
deferred.resolve(); deferred.resolve();
tfjs.disposeVariables(); tfjs.disposeVariables();
}) })
.catch(function (err) { .catch((err) => {
throw err; throw err;
}); });
} }
@ -204,10 +204,10 @@ async.series({
// sharp // sharp
jpegSuite.add('sharp-buffer-file', { jpegSuite.add('sharp-buffer-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.toFile(outputJpg, function (err) { .toFile(outputJpg, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -217,10 +217,10 @@ async.series({
} }
}).add('sharp-buffer-buffer', { }).add('sharp-buffer-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -230,10 +230,10 @@ async.series({
} }
}).add('sharp-file-file', { }).add('sharp-file-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(width, height) .resize(width, height)
.toFile(outputJpg, function (err) { .toFile(outputJpg, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -243,10 +243,10 @@ async.series({
} }
}).add('sharp-stream-stream', { }).add('sharp-stream-stream', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
const readable = fs.createReadStream(fixtures.inputJpg); const readable = fs.createReadStream(fixtures.inputJpg);
const writable = fs.createWriteStream(outputJpg); const writable = fs.createWriteStream(outputJpg);
writable.on('finish', function () { writable.on('finish', () => {
deferred.resolve(); deferred.resolve();
}); });
const pipeline = sharp() const pipeline = sharp()
@ -255,10 +255,10 @@ async.series({
} }
}).add('sharp-file-buffer', { }).add('sharp-file-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(width, height) .resize(width, height)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -268,34 +268,34 @@ async.series({
} }
}).add('sharp-promise', { }).add('sharp-promise', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.toBuffer() .toBuffer()
.then(function () { .then(() => {
deferred.resolve(); deferred.resolve();
}) })
.catch(function (err) { .catch((err) => {
throw err; throw err;
}); });
} }
}).on('cycle', function (event) { }).on('cycle', (event) => {
console.log('jpeg ' + String(event.target)); console.log(`jpeg ${String(event.target)}`);
}).on('complete', function () { }).on('complete', function () {
callback(null, this.filter('fastest').map('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
// Effect of applying operations // Effect of applying operations
operations: function (callback) { operations: (callback) => {
const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg); const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
const operationsSuite = new Benchmark.Suite('operations'); const operationsSuite = new Benchmark.Suite('operations');
operationsSuite.add('sharp-sharpen-mild', { operationsSuite.add('sharp-sharpen-mild', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.sharpen() .sharpen()
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -305,11 +305,11 @@ async.series({
} }
}).add('sharp-sharpen-radius', { }).add('sharp-sharpen-radius', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.sharpen(3, 1, 3) .sharpen(3, 1, 3)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -319,11 +319,11 @@ async.series({
} }
}).add('sharp-blur-mild', { }).add('sharp-blur-mild', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.blur() .blur()
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -333,11 +333,11 @@ async.series({
} }
}).add('sharp-blur-radius', { }).add('sharp-blur-radius', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.blur(3) .blur(3)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -347,11 +347,11 @@ async.series({
} }
}).add('sharp-gamma', { }).add('sharp-gamma', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.gamma() .gamma()
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -361,11 +361,11 @@ async.series({
} }
}).add('sharp-normalise', { }).add('sharp-normalise', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.normalise() .normalise()
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -375,11 +375,11 @@ async.series({
} }
}).add('sharp-greyscale', { }).add('sharp-greyscale', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.greyscale() .greyscale()
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -389,12 +389,12 @@ async.series({
} }
}).add('sharp-greyscale-gamma', { }).add('sharp-greyscale-gamma', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.gamma() .gamma()
.greyscale() .greyscale()
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -404,11 +404,11 @@ async.series({
} }
}).add('sharp-progressive', { }).add('sharp-progressive', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.jpeg({ progressive: true }) .jpeg({ progressive: true })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -418,11 +418,11 @@ async.series({
} }
}).add('sharp-without-chroma-subsampling', { }).add('sharp-without-chroma-subsampling', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.jpeg({ chromaSubsampling: '4:4:4' }) .jpeg({ chromaSubsampling: '4:4:4' })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -432,11 +432,11 @@ async.series({
} }
}).add('sharp-rotate', { }).add('sharp-rotate', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.rotate(90) .rotate(90)
.resize(width, height) .resize(width, height)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -446,11 +446,11 @@ async.series({
} }
}).add('sharp-without-simd', { }).add('sharp-without-simd', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp.simd(false); sharp.simd(false);
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height) .resize(width, height)
.toBuffer(function (err) { .toBuffer((err) => {
sharp.simd(true); sharp.simd(true);
if (err) { if (err) {
throw err; throw err;
@ -461,10 +461,10 @@ async.series({
} }
}).add('sharp-random-access-read', { }).add('sharp-random-access-read', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer, { sequentialRead: false }) sharp(inputJpgBuffer, { sequentialRead: false })
.resize(width, height) .resize(width, height)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -474,13 +474,13 @@ async.series({
} }
}).add('sharp-crop-entropy', { }).add('sharp-crop-entropy', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height, { .resize(width, height, {
fit: 'cover', fit: 'cover',
position: sharp.strategy.entropy position: sharp.strategy.entropy
}) })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -490,13 +490,13 @@ async.series({
} }
}).add('sharp-crop-attention', { }).add('sharp-crop-attention', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height, { .resize(width, height, {
fit: 'cover', fit: 'cover',
position: sharp.strategy.attention position: sharp.strategy.attention
}) })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -504,21 +504,21 @@ async.series({
} }
}); });
} }
}).on('cycle', function (event) { }).on('cycle', (event) => {
console.log('operations ' + String(event.target)); console.log(`operations ${String(event.target)}`);
}).on('complete', function () { }).on('complete', function () {
callback(null, this.filter('fastest').map('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
// Comparative speed of kernels // Comparative speed of kernels
kernels: function (callback) { kernels: (callback) => {
const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg); const inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
(new Benchmark.Suite('kernels')).add('sharp-cubic', { (new Benchmark.Suite('kernels')).add('sharp-cubic', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'cubic' }) .resize(width, height, { kernel: 'cubic' })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -528,10 +528,10 @@ async.series({
} }
}).add('sharp-lanczos2', { }).add('sharp-lanczos2', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'lanczos2' }) .resize(width, height, { kernel: 'lanczos2' })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -541,10 +541,10 @@ async.series({
} }
}).add('sharp-lanczos3', { }).add('sharp-lanczos3', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'lanczos3' }) .resize(width, height, { kernel: 'lanczos3' })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -554,10 +554,10 @@ async.series({
} }
}).add('sharp-mks2013', { }).add('sharp-mks2013', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'mks2013' }) .resize(width, height, { kernel: 'mks2013' })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -567,10 +567,10 @@ async.series({
} }
}).add('sharp-mks2021', { }).add('sharp-mks2021', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputJpgBuffer) sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'mks2021' }) .resize(width, height, { kernel: 'mks2021' })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -578,21 +578,21 @@ async.series({
} }
}); });
} }
}).on('cycle', function (event) { }).on('cycle', (event) => {
console.log('kernels ' + String(event.target)); console.log(`kernels ${String(event.target)}`);
}).on('complete', function () { }).on('complete', function () {
callback(null, this.filter('fastest').map('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
// PNG // PNG
png: function (callback) { png: (callback) => {
const inputPngBuffer = fs.readFileSync(fixtures.inputPngAlphaPremultiplicationLarge); const inputPngBuffer = fs.readFileSync(fixtures.inputPngAlphaPremultiplicationLarge);
const pngSuite = new Benchmark.Suite('png'); const pngSuite = new Benchmark.Suite('png');
const minSamples = 64; const minSamples = 64;
// jimp // jimp
pngSuite.add('jimp-buffer-buffer', { pngSuite.add('jimp-buffer-buffer', {
defer: true, defer: true,
fn: async function (deferred) { fn: async (deferred) => {
const image = await Jimp.read(inputPngBuffer); const image = await Jimp.read(inputPngBuffer);
await image await image
.resize({ w: width, h: heightPng, mode: Jimp.RESIZE_BICUBIC }) .resize({ w: width, h: heightPng, mode: Jimp.RESIZE_BICUBIC })
@ -601,7 +601,7 @@ async.series({
} }
}).add('jimp-file-file', { }).add('jimp-file-file', {
defer: true, defer: true,
fn: async function (deferred) { fn: async (deferred) => {
const image = await Jimp.read(fixtures.inputPngAlphaPremultiplicationLarge); const image = await Jimp.read(fixtures.inputPngAlphaPremultiplicationLarge);
await image await image
.resize({ w: width, h: heightPng, mode: Jimp.RESIZE_BICUBIC }) .resize({ w: width, h: heightPng, mode: Jimp.RESIZE_BICUBIC })
@ -612,18 +612,18 @@ async.series({
// mapnik // mapnik
mapnik && pngSuite.add('mapnik-file-file', { mapnik && pngSuite.add('mapnik-file-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
mapnik.Image.open(fixtures.inputPngAlphaPremultiplicationLarge, function (err, img) { mapnik.Image.open(fixtures.inputPngAlphaPremultiplicationLarge, (err, img) => {
if (err) throw err; if (err) throw err;
img.premultiply(function (err, img) { img.premultiply((err, img) => {
if (err) throw err; if (err) throw err;
img.resize(width, heightPng, { img.resize(width, heightPng, {
scaling_method: mapnik.imageScaling.lanczos scaling_method: mapnik.imageScaling.lanczos
}, function (err, img) { }, (err, img) => {
if (err) throw err; if (err) throw err;
img.demultiply(function (err, img) { img.demultiply((err, img) => {
if (err) throw err; 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; if (err) throw err;
deferred.resolve(); deferred.resolve();
}); });
@ -634,18 +634,18 @@ async.series({
} }
}).add('mapnik-buffer-buffer', { }).add('mapnik-buffer-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
mapnik.Image.fromBytes(inputPngBuffer, { max_size: 3000 }, function (err, img) { mapnik.Image.fromBytes(inputPngBuffer, { max_size: 3000 }, (err, img) => {
if (err) throw err; if (err) throw err;
img.premultiply(function (err, img) { img.premultiply((err, img) => {
if (err) throw err; if (err) throw err;
img.resize(width, heightPng, { img.resize(width, heightPng, {
scaling_method: mapnik.imageScaling.lanczos scaling_method: mapnik.imageScaling.lanczos
}, function (err, img) { }, (err, img) => {
if (err) throw err; if (err) throw err;
img.demultiply(function (err, img) { img.demultiply((err, img) => {
if (err) throw err; 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; if (err) throw err;
deferred.resolve(); deferred.resolve();
}); });
@ -658,7 +658,7 @@ async.series({
// imagemagick // imagemagick
pngSuite.add('imagemagick-file-file', { pngSuite.add('imagemagick-file-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
imagemagick.resize({ imagemagick.resize({
srcPath: fixtures.inputPngAlphaPremultiplicationLarge, srcPath: fixtures.inputPngAlphaPremultiplicationLarge,
dstPath: outputPng, dstPath: outputPng,
@ -669,7 +669,7 @@ async.series({
'-define', 'PNG:compression-level=6', '-define', 'PNG:compression-level=6',
'-define', 'PNG:compression-filter=0' '-define', 'PNG:compression-filter=0'
] ]
}, function (err) { }, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -681,13 +681,13 @@ async.series({
// gm // gm
pngSuite.add('gm-file-file', { pngSuite.add('gm-file-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
gm(fixtures.inputPngAlphaPremultiplicationLarge) gm(fixtures.inputPngAlphaPremultiplicationLarge)
.filter('Lanczos') .filter('Lanczos')
.resize(width, heightPng) .resize(width, heightPng)
.define('PNG:compression-level=6') .define('PNG:compression-level=6')
.define('PNG:compression-filter=0') .define('PNG:compression-filter=0')
.write(outputPng, function (err) { .write(outputPng, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -697,13 +697,13 @@ async.series({
} }
}).add('gm-file-buffer', { }).add('gm-file-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
gm(fixtures.inputPngAlphaPremultiplicationLarge) gm(fixtures.inputPngAlphaPremultiplicationLarge)
.filter('Lanczos') .filter('Lanczos')
.resize(width, heightPng) .resize(width, heightPng)
.define('PNG:compression-level=6') .define('PNG:compression-level=6')
.define('PNG:compression-filter=0') .define('PNG:compression-filter=0')
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -716,11 +716,11 @@ async.series({
pngSuite.add('sharp-buffer-file', { pngSuite.add('sharp-buffer-file', {
defer: true, defer: true,
minSamples, minSamples,
fn: function (deferred) { fn: (deferred) => {
sharp(inputPngBuffer) sharp(inputPngBuffer)
.resize(width, heightPng) .resize(width, heightPng)
.png({ compressionLevel: 6 }) .png({ compressionLevel: 6 })
.toFile(outputPng, function (err) { .toFile(outputPng, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -731,11 +731,11 @@ async.series({
}).add('sharp-buffer-buffer', { }).add('sharp-buffer-buffer', {
defer: true, defer: true,
minSamples, minSamples,
fn: function (deferred) { fn: (deferred) => {
sharp(inputPngBuffer) sharp(inputPngBuffer)
.resize(width, heightPng) .resize(width, heightPng)
.png({ compressionLevel: 6 }) .png({ compressionLevel: 6 })
.toBuffer(function (err, data) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -746,11 +746,11 @@ async.series({
}).add('sharp-file-file', { }).add('sharp-file-file', {
defer: true, defer: true,
minSamples, minSamples,
fn: function (deferred) { fn: (deferred) => {
sharp(fixtures.inputPngAlphaPremultiplicationLarge) sharp(fixtures.inputPngAlphaPremultiplicationLarge)
.resize(width, heightPng) .resize(width, heightPng)
.png({ compressionLevel: 6 }) .png({ compressionLevel: 6 })
.toFile(outputPng, function (err) { .toFile(outputPng, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -761,11 +761,11 @@ async.series({
}).add('sharp-file-buffer', { }).add('sharp-file-buffer', {
defer: true, defer: true,
minSamples, minSamples,
fn: function (deferred) { fn: (deferred) => {
sharp(fixtures.inputPngAlphaPremultiplicationLarge) sharp(fixtures.inputPngAlphaPremultiplicationLarge)
.resize(width, heightPng) .resize(width, heightPng)
.png({ compressionLevel: 6 }) .png({ compressionLevel: 6 })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -776,11 +776,11 @@ async.series({
}).add('sharp-progressive', { }).add('sharp-progressive', {
defer: true, defer: true,
minSamples, minSamples,
fn: function (deferred) { fn: (deferred) => {
sharp(inputPngBuffer) sharp(inputPngBuffer)
.resize(width, heightPng) .resize(width, heightPng)
.png({ compressionLevel: 6, progressive: true }) .png({ compressionLevel: 6, progressive: true })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -791,11 +791,11 @@ async.series({
}).add('sharp-adaptiveFiltering', { }).add('sharp-adaptiveFiltering', {
defer: true, defer: true,
minSamples, minSamples,
fn: function (deferred) { fn: (deferred) => {
sharp(inputPngBuffer) sharp(inputPngBuffer)
.resize(width, heightPng) .resize(width, heightPng)
.png({ adaptiveFiltering: true, compressionLevel: 6 }) .png({ adaptiveFiltering: true, compressionLevel: 6 })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -806,11 +806,11 @@ async.series({
}).add('sharp-compressionLevel=9', { }).add('sharp-compressionLevel=9', {
defer: true, defer: true,
minSamples, minSamples,
fn: function (deferred) { fn: (deferred) => {
sharp(inputPngBuffer) sharp(inputPngBuffer)
.resize(width, heightPng) .resize(width, heightPng)
.png({ compressionLevel: 9 }) .png({ compressionLevel: 9 })
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -819,21 +819,21 @@ async.series({
}); });
} }
}); });
pngSuite.on('cycle', function (event) { pngSuite.on('cycle', (event) => {
console.log(' png ' + String(event.target)); console.log(` png ${String(event.target)}`);
}).on('complete', function () { }).on('complete', function () {
callback(null, this.filter('fastest').map('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
}, },
// WebP // WebP
webp: function (callback) { webp: (callback) => {
const inputWebPBuffer = fs.readFileSync(fixtures.inputWebP); const inputWebPBuffer = fs.readFileSync(fixtures.inputWebP);
(new Benchmark.Suite('webp')).add('sharp-buffer-file', { (new Benchmark.Suite('webp')).add('sharp-buffer-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputWebPBuffer) sharp(inputWebPBuffer)
.resize(width, height) .resize(width, height)
.toFile(outputWebP, function (err) { .toFile(outputWebP, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -843,10 +843,10 @@ async.series({
} }
}).add('sharp-buffer-buffer', { }).add('sharp-buffer-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(inputWebPBuffer) sharp(inputWebPBuffer)
.resize(width, height) .resize(width, height)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -856,10 +856,10 @@ async.series({
} }
}).add('sharp-file-file', { }).add('sharp-file-file', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(fixtures.inputWebP) sharp(fixtures.inputWebP)
.resize(width, height) .resize(width, height)
.toFile(outputWebP, function (err) { .toFile(outputWebP, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -869,10 +869,10 @@ async.series({
} }
}).add('sharp-file-buffer', { }).add('sharp-file-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(fixtures.inputWebP) sharp(fixtures.inputWebP)
.resize(width, height) .resize(width, height)
.toBuffer(function (err) { .toBuffer((err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -880,19 +880,19 @@ async.series({
} }
}); });
} }
}).on('cycle', function (event) { }).on('cycle', (event) => {
console.log('webp ' + String(event.target)); console.log(`webp ${String(event.target)}`);
}).on('complete', function () { }).on('complete', function () {
callback(null, this.filter('fastest').map('name')); callback(null, this.filter('fastest').map('name'));
}).run(); }).run();
} }
}, function (err, results) { }, (err, results) => {
if (err) { if (err) {
throw err; throw err;
} }
Object.keys(results).forEach(function (format) { Object.keys(results).forEach((format) => {
if (results[format].toString().substr(0, 5) !== 'sharp') { 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()); console.dir(sharp.cache());

View File

@ -1,11 +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
'use strict'; */
const imagemagick = require('imagemagick'); const imagemagick = require('imagemagick');
const gm = require('gm'); const gm = require('gm');
const assert = require('assert'); const assert = require('node:assert');
const Benchmark = require('benchmark'); const Benchmark = require('benchmark');
const sharp = require('../../'); const sharp = require('../../');
@ -16,13 +16,11 @@ sharp.cache(false);
const min = 320; const min = 320;
const max = 960; const max = 960;
const randomDimension = function () { const randomDimension = () => Math.ceil((Math.random() * (max - min)) + min);
return Math.ceil((Math.random() * (max - min)) + min);
};
new Benchmark.Suite('random').add('imagemagick', { new Benchmark.Suite('random').add('imagemagick', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
imagemagick.resize({ imagemagick.resize({
srcPath: fixtures.inputJpg, srcPath: fixtures.inputJpg,
dstPath: fixtures.path('output.jpg'), dstPath: fixtures.path('output.jpg'),
@ -31,7 +29,7 @@ new Benchmark.Suite('random').add('imagemagick', {
height: randomDimension(), height: randomDimension(),
format: 'jpg', format: 'jpg',
filter: 'Lanczos' filter: 'Lanczos'
}, function (err) { }, (err) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -41,12 +39,12 @@ new Benchmark.Suite('random').add('imagemagick', {
} }
}).add('gm', { }).add('gm', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
gm(fixtures.inputJpg) gm(fixtures.inputJpg)
.resize(randomDimension(), randomDimension()) .resize(randomDimension(), randomDimension())
.filter('Lanczos') .filter('Lanczos')
.quality(80) .quality(80)
.toBuffer(function (err, buffer) { .toBuffer((err, buffer) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -57,10 +55,10 @@ new Benchmark.Suite('random').add('imagemagick', {
} }
}).add('sharp', { }).add('sharp', {
defer: true, defer: true,
fn: function (deferred) { fn: (deferred) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(randomDimension(), randomDimension()) .resize(randomDimension(), randomDimension())
.toBuffer(function (err, buffer) { .toBuffer((err, buffer) => {
if (err) { if (err) {
throw err; throw err;
} else { } else {
@ -69,9 +67,9 @@ new Benchmark.Suite('random').add('imagemagick', {
} }
}); });
} }
}).on('cycle', function (event) { }).on('cycle', (event) => {
console.log(String(event.target)); console.log(String(event.target));
}).on('complete', function () { }).on('complete', function () {
const winner = this.filter('fastest').map('name'); 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(); }).run();

BIN
test/fixtures/bonne.geo.tif vendored Normal file

Binary file not shown.

View File

@ -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('node:path');
const path = require('path');
const sharp = require('../../'); const sharp = require('../../');
const maxColourDistance = require('../../lib/sharp')._maxColourDistance; const maxColourDistance = require('../../lib/sharp')._maxColourDistance;
// Helpers // Helpers
const getPath = function (filename) { const getPath = (filename) => path.join(__dirname, filename);
return path.join(__dirname, filename);
};
// Generates a 64-bit-as-binary-string image fingerprint // 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 // 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 }) .resize(9, 8, { fit: sharp.fit.fill })
.raw() .raw()
.toBuffer() .toBuffer()
.then(function (data) { .then((data) => {
let fingerprint = ''; let fingerprint = '';
for (let col = 0; col < 8; col++) { for (let col = 0; col < 8; col++) {
for (let row = 0; row < 8; row++) { for (let row = 0; row < 8; row++) {
@ -115,6 +113,7 @@ module.exports = {
inputTiff8BitDepth: getPath('8bit_depth.tiff'), inputTiff8BitDepth: getPath('8bit_depth.tiff'),
inputTifftagPhotoshop: getPath('tifftag-photoshop.tiff'), // https://github.com/lovell/sharp/issues/1600 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 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 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 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
@ -147,14 +146,12 @@ module.exports = {
path: getPath, path: getPath,
// Path for expected output images // Path for expected output images
expected: function (filename) { expected: (filename) => getPath(path.join('expected', filename)),
return getPath(path.join('expected', filename));
},
// Verify similarity of expected vs actual images via fingerprint // Verify similarity of expected vs actual images via fingerprint
// Specify distance threshold using `options={threshold: 42}`, default // Specify distance threshold using `options={threshold: 42}`, default
// `threshold` is 5; // `threshold` is 5;
assertSimilar: async function (expectedImage, actualImage, options, callback) { assertSimilar: async (expectedImage, actualImage, options, callback) => {
if (typeof options === 'function') { if (typeof options === 'function') {
callback = options; callback = options;
options = {}; options = {};
@ -194,12 +191,12 @@ module.exports = {
} }
}, },
assertMaxColourDistance: function (actualImagePath, expectedImagePath, acceptedDistance) { assertMaxColourDistance: (actualImagePath, expectedImagePath, acceptedDistance) => {
if (typeof actualImagePath !== 'string') { 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') { 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') { if (typeof acceptedDistance !== 'number') {
// Default threshold // Default threshold
@ -207,7 +204,7 @@ module.exports = {
} }
const distance = maxColourDistance(actualImagePath, expectedImagePath); const distance = maxColourDistance(actualImagePath, expectedImagePath);
if (distance > acceptedDistance) { 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}`);
} }
} }

View File

@ -18,5 +18,5 @@ for test in $TESTS; do
--show-leak-kinds=definite,indirect \ --show-leak-kinds=definite,indirect \
--num-callers=20 \ --num-callers=20 \
--trace-children=yes \ --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 done

View File

@ -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 sharp = require('../../');
import { createReadStream, createWriteStream } from 'fs'; import { createReadStream, createWriteStream } from 'node:fs';
const input: Buffer = Buffer.alloc(0); const input: Buffer = Buffer.alloc(0);
const readableStream: NodeJS.ReadableStream = createReadStream(input); const readableStream: NodeJS.ReadableStream = createReadStream(input);
@ -79,7 +82,7 @@ sharp({
let transformer = sharp() let transformer = sharp()
.resize(300) .resize(300)
.on('info', (info: sharp.OutputInfo) => { .on('info', (info: sharp.OutputInfo) => {
console.log('Image height is ' + info.height); console.log(`Image height is ${info.height}`);
}); });
readableStream.pipe(transformer).pipe(writableStream); readableStream.pipe(transformer).pipe(writableStream);
@ -318,7 +321,7 @@ sharp('input.gif')
// From https://sharp.pixelplumbing.com/api-output#examples-9 // From https://sharp.pixelplumbing.com/api-output#examples-9
// Extract raw RGB pixel data from JPEG input // Extract raw RGB pixel data from JPEG input
sharp('input.jpg') sharp('input.jpg')
.raw() .raw({ depth: 'ushort' })
.toBuffer({ resolveWithObject: true }) .toBuffer({ resolveWithObject: true })
.then(({ data, info }) => { .then(({ data, info }) => {
console.log(data); console.log(data);

16
test/unit.mjs Normal file
View 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);

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
const sharp = require('../../'); const sharp = require('../../');
describe('Alpha transparency', function () { describe('Alpha transparency', () => {
it('Flatten to black', function (done) { it('Flatten to black', (_t, done) => {
sharp(fixtures.inputPngWithTransparency) sharp(fixtures.inputPngWithTransparency)
.flatten() .flatten()
.resize(400, 300) .resize(400, 300)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(400, info.width); assert.strictEqual(400, info.width);
assert.strictEqual(300, info.height); 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) sharp(fixtures.inputPngWithTransparency)
.resize(400, 300) .resize(400, 300)
.flatten({ .flatten({
background: { r: 255, g: 102, b: 0 } background: { r: 255, g: 102, b: 0 }
}) })
.jpeg({ chromaSubsampling: '4:4:4' }) .jpeg({ chromaSubsampling: '4:4:4' })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(400, info.width); assert.strictEqual(400, info.width);
assert.strictEqual(300, info.height); 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) sharp(fixtures.inputPngWithTransparency)
.resize(400, 300) .resize(400, 300)
.flatten({ background: '#ff6600' }) .flatten({ background: '#ff6600' })
.jpeg({ chromaSubsampling: '4:4:4' }) .jpeg({ chromaSubsampling: '4:4:4' })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(400, info.width); assert.strictEqual(400, info.width);
assert.strictEqual(300, info.height); 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'); const output = fixtures.path('output.flatten-rgb16-orange.jpg');
sharp(fixtures.inputPngWithTransparency16bit) sharp(fixtures.inputPngWithTransparency16bit)
.flatten({ .flatten({
background: { r: 255, g: 102, b: 0 } background: { r: 255, g: 102, b: 0 }
}) })
.toFile(output, function (err, info) { .toFile(output, (err, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.size > 0); assert.strictEqual(true, info.size > 0);
assert.strictEqual(32, info.width); 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) sharp(fixtures.inputPngWithTransparency)
.flatten(false) .flatten(false)
.toBuffer(function (err, data, info) { .toBuffer((err, _data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(4, info.channels); 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) sharp(fixtures.inputJpg)
.flatten({ background: '#ff0000' }) .flatten({ background: '#ff0000' })
.toBuffer(function (err, data, info) { .toBuffer((err, _data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
@ -98,68 +99,60 @@ describe('Alpha transparency', function () {
}); });
}); });
it('Enlargement with non-nearest neighbor interpolation shouldnt cause dark edges', function () { it('Enlargement with non-nearest neighbor interpolation shouldnt cause dark edges', () => {
const base = 'alpha-premultiply-enlargement-2048x1536-paper.png'; 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); const expected = fixtures.expected(base);
return sharp(fixtures.inputPngAlphaPremultiplicationSmall) return sharp(fixtures.inputPngAlphaPremultiplicationSmall)
.resize(2048, 1536) .resize(2048, 1536)
.toFile(actual) .toFile(actual)
.then(function () { .then(() => {
fixtures.assertMaxColourDistance(actual, expected, 102); fixtures.assertMaxColourDistance(actual, expected, 102);
}); });
}); });
it('Reduction with non-nearest neighbor interpolation shouldnt cause dark edges', function () { it('Reduction with non-nearest neighbor interpolation shouldnt cause dark edges', () => {
const base = 'alpha-premultiply-reduction-1024x768-paper.png'; 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); const expected = fixtures.expected(base);
return sharp(fixtures.inputPngAlphaPremultiplicationLarge) return sharp(fixtures.inputPngAlphaPremultiplicationLarge)
.resize(1024, 768) .resize(1024, 768)
.toFile(actual) .toFile(actual)
.then(function () { .then(() => {
fixtures.assertMaxColourDistance(actual, expected, 102); fixtures.assertMaxColourDistance(actual, expected, 102);
}); });
}); });
it('Removes alpha from fixtures with transparency, ignores those without', function () { it('Removes alpha from fixtures with transparency, ignores those without', () => Promise.all([
return Promise.all([
fixtures.inputPngWithTransparency, fixtures.inputPngWithTransparency,
fixtures.inputPngWithTransparency16bit, fixtures.inputPngWithTransparency16bit,
fixtures.inputWebPWithTransparency, fixtures.inputWebPWithTransparency,
fixtures.inputJpg, fixtures.inputJpg,
fixtures.inputPng, fixtures.inputPng,
fixtures.inputWebP fixtures.inputWebP
].map(function (input) { ].map((input) => sharp(input)
return sharp(input)
.resize(10) .resize(10)
.removeAlpha() .removeAlpha()
.toBuffer({ resolveWithObject: true }) .toBuffer({ resolveWithObject: true })
.then(function (result) { .then((result) => {
assert.strictEqual(3, result.info.channels); assert.strictEqual(3, result.info.channels);
}); }))));
}));
});
it('Ensures alpha from fixtures without transparency, ignores those with', function () { it('Ensures alpha from fixtures without transparency, ignores those with', () => Promise.all([
return Promise.all([
fixtures.inputPngWithTransparency, fixtures.inputPngWithTransparency,
fixtures.inputPngWithTransparency16bit, fixtures.inputPngWithTransparency16bit,
fixtures.inputWebPWithTransparency, fixtures.inputWebPWithTransparency,
fixtures.inputJpg, fixtures.inputJpg,
fixtures.inputPng, fixtures.inputPng,
fixtures.inputWebP fixtures.inputWebP
].map(function (input) { ].map((input) => sharp(input)
return sharp(input)
.resize(10) .resize(10)
.ensureAlpha() .ensureAlpha()
.png() .png()
.toBuffer({ resolveWithObject: true }) .toBuffer({ resolveWithObject: true })
.then(function (result) { .then((result) => {
assert.strictEqual(4, result.info.channels); assert.strictEqual(4, result.info.channels);
}); }))));
}));
});
it('Valid ensureAlpha value used for alpha channel', async () => { it('Valid ensureAlpha value used for alpha channel', async () => {
const background = { r: 255, g: 0, b: 0 }; const background = { r: 255, g: 0, b: 0 };

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const { inputAvif, inputJpg, inputGifAnimated } = require('../fixtures'); const { inputAvif, inputJpg, inputGifAnimated } = require('../fixtures');
@ -20,8 +21,8 @@ describe('AVIF', () => {
.resize(32) .resize(32)
.jpeg() .jpeg()
.toBuffer(); .toBuffer();
const { size, ...metadata } = await sharp(data) const { size, ...metadata } = await sharp(data).metadata();
.metadata(); void size;
assert.deepStrictEqual(metadata, { assert.deepStrictEqual(metadata, {
autoOrient: { autoOrient: {
height: 13, height: 13,
@ -49,8 +50,8 @@ describe('AVIF', () => {
.resize(32) .resize(32)
.avif({ effort: 0 }) .avif({ effort: 0 })
.toBuffer(); .toBuffer();
const { size, ...metadata } = await sharp(data) const { size, ...metadata } = await sharp(data).metadata();
.metadata(); void size;
assert.deepStrictEqual(metadata, { assert.deepStrictEqual(metadata, {
autoOrient: { autoOrient: {
height: 26, height: 26,
@ -77,8 +78,8 @@ describe('AVIF', () => {
const data = await sharp(inputAvif) const data = await sharp(inputAvif)
.resize(32) .resize(32)
.toBuffer(); .toBuffer();
const { size, ...metadata } = await sharp(data) const { size, ...metadata } = await sharp(data).metadata();
.metadata(); void size;
assert.deepStrictEqual(metadata, { assert.deepStrictEqual(metadata, {
autoOrient: { autoOrient: {
height: 13, height: 13,
@ -106,8 +107,8 @@ describe('AVIF', () => {
.resize(10) .resize(10)
.avif({ effort: 0 }) .avif({ effort: 0 })
.toBuffer(); .toBuffer();
const { size, ...metadata } = await sharp(data) const { size, ...metadata } = await sharp(data).metadata();
.metadata(); void size;
assert.deepStrictEqual(metadata, { assert.deepStrictEqual(metadata, {
autoOrient: { autoOrient: {
height: 300, height: 300,
@ -136,8 +137,8 @@ describe('AVIF', () => {
.sharpen() .sharpen()
.avif({ effort: 0 }) .avif({ effort: 0 })
.toBuffer(); .toBuffer();
const { size, ...metadata } = await sharp(data) const { size, ...metadata } = await sharp(data).metadata();
.metadata(); void size;
assert.deepStrictEqual(metadata, { assert.deepStrictEqual(metadata, {
autoOrient: { autoOrient: {
height: 26, height: 26,
@ -174,9 +175,10 @@ describe('AVIF', () => {
) )
); );
it('Invalid bitdepth value throws error', async () => { it('Invalid bitdepth value throws error', () =>
assert.rejects( assert.throws(
() => sharp().avif({ bitdepth: 11 }), () => 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/
}); )
);
}); });

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
const sharp = require('../../'); const sharp = require('../../');
describe('Bandbool per-channel boolean operations', function () { describe('Bandbool per-channel boolean operations', () => {
[ [
sharp.bool.and, sharp.bool.and,
sharp.bool.or, sharp.bool.or,
sharp.bool.eor sharp.bool.eor
] ]
.forEach(function (op) { .forEach((op) => {
it(op + ' operation', function (done) { it(`${op} operation`, (_t, done) => {
sharp(fixtures.inputPngBooleanNoAlpha) sharp(fixtures.inputPngBooleanNoAlpha)
.bandbool(op) .bandbool(op)
.toColourspace('b-w') .toColourspace('b-w')
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(200, info.width); assert.strictEqual(200, info.width);
assert.strictEqual(200, info.height); assert.strictEqual(200, info.height);
assert.strictEqual(1, info.channels); 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) sharp(fixtures.inputJpg)
.bandbool('and') .bandbool('and')
.toBuffer(function (err, data, info) { .toBuffer((err, _data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
done(); done();
}); });
}); });
it('Invalid operation', function () { it('Invalid operation', () => {
assert.throws(function () { assert.throws(() => {
sharp().bandbool('fail'); sharp().bandbool('fail');
}); });
}); });
it('Missing operation', function () { it('Missing operation', () => {
assert.throws(function () { assert.throws(() => {
sharp().bandbool(); sharp().bandbool();
}); });
}); });

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Blur', function () { describe('Blur', () => {
it('specific radius 1', function (done) { it('specific radius 1', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.blur(1) .blur(1)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); 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) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.blur(10) .blur(10)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); 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) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.blur({ sigma: 10 }) .blur({ sigma: 10 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); 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) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.blur(0.3) .blur(0.3)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); 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) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.blur() .blur()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
@ -74,17 +75,17 @@ describe('Blur', function () {
}); });
}); });
it('invalid radius', function () { it('invalid radius', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).blur(0.1); 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) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.blur(false) .blur(false)
.toBuffer(function (err, notBlurred, info) { .toBuffer((err, notBlurred, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, notBlurred.length > 0); assert.strictEqual(true, notBlurred.length > 0);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
@ -93,7 +94,7 @@ describe('Blur', function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.blur(true) .blur(true)
.toBuffer(function (err, blurred, info) { .toBuffer((err, blurred, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, blurred.length > 0); assert.strictEqual(true, blurred.length > 0);
assert.strictEqual(true, blurred.length < notBlurred.length); assert.strictEqual(true, blurred.length < notBlurred.length);
@ -105,18 +106,18 @@ describe('Blur', function () {
}); });
}); });
it('invalid precision', function () { it('invalid precision', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).blur({ sigma: 1, precision: 'invalid' }); sharp(fixtures.inputJpg).blur({ sigma: 1, precision: 'invalid' });
}, /Expected one of: integer, float, approximate for precision but received invalid of type string/); }, /Expected one of: integer, float, approximate for precision but received invalid of type string/);
}); });
it('invalid minAmplitude', function () { it('invalid minAmplitude', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).blur({ sigma: 1, minAmplitude: 0 }); sharp(fixtures.inputJpg).blur({ sigma: 1, minAmplitude: 0 });
}, /Expected number between 0.001 and 1 for minAmplitude but received 0 of type number/); }, /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 }); 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/); }, /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); await fixtures.assertSimilar(fixtures.expected('blur-10.jpg'), minAmplitudeLow);
}); });
it('options.sigma is required if options object is passed', function () { it('options.sigma is required if options object is passed', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).blur({ precision: 'invalid' }); sharp(fixtures.inputJpg).blur({ precision: 'invalid' });
}, /Expected number between 0.3 and 1000 for options.sigma but received undefined of type undefined/); }, /Expected number between 0.3 and 1000 for options.sigma but received undefined of type undefined/);
}); });

View File

@ -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('node:fs');
const { describe, it } = require('node:test');
const fs = require('fs'); const assert = require('node:assert');
const assert = require('assert');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
const sharp = require('../../'); const sharp = require('../../');
describe('Boolean operation between two images', function () { describe('Boolean operation between two images', () => {
const inputJpgBooleanTestBuffer = fs.readFileSync(fixtures.inputJpgBooleanTest); const inputJpgBooleanTestBuffer = fs.readFileSync(fixtures.inputJpgBooleanTest);
[ [
@ -17,63 +18,63 @@ describe('Boolean operation between two images', function () {
sharp.bool.or, sharp.bool.or,
sharp.bool.eor sharp.bool.eor
] ]
.forEach(function (op) { .forEach((op) => {
it(op + ' operation, file', function (done) { it(`${op} operation, file`, (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.boolean(fixtures.inputJpgBooleanTest, op) .boolean(fixtures.inputJpgBooleanTest, op)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); 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) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.boolean(inputJpgBooleanTestBuffer, op) .boolean(inputJpgBooleanTestBuffer, op)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); 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) sharp(fixtures.inputJpgBooleanTest)
.raw() .raw()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.boolean(data, op, { raw: info }) .boolean(data, op, { raw: info })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); 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 () { it('Invalid operation', () => {
assert.throws(function () { assert.throws(() => {
sharp().boolean(fixtures.inputJpgBooleanTest, 'fail'); sharp().boolean(fixtures.inputJpgBooleanTest, 'fail');
}); });
}); });
it('Invalid operation, non-string', function () { it('Invalid operation, non-string', () => {
assert.throws(function () { assert.throws(() => {
sharp().boolean(fixtures.inputJpgBooleanTest, null); sharp().boolean(fixtures.inputJpgBooleanTest, null);
}); });
}); });
it('Missing input', function () { it('Missing input', () => {
assert.throws(function () { assert.throws(() => {
sharp().boolean(); sharp().boolean();
}); });
}); });

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../lib'); const sharp = require('../../lib');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Clahe', function () { describe('Clahe', () => {
it('width 5 width 5 maxSlope 0', function (done) { it('width 5 width 5 maxSlope 0', (_t, done) => {
sharp(fixtures.inputJpgClahe) sharp(fixtures.inputJpgClahe)
.clahe({ width: 5, height: 5, maxSlope: 0 }) .clahe({ width: 5, height: 5, maxSlope: 0 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-5-5-0.jpg'), data, { threshold: 10 }, done); 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) sharp(fixtures.inputJpgClahe)
.clahe({ width: 5, height: 5, maxSlope: 5 }) .clahe({ width: 5, height: 5, maxSlope: 5 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-5-5-5.jpg'), data, done); 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) sharp(fixtures.inputJpgClahe)
.clahe({ width: 11, height: 25, maxSlope: 14 }) .clahe({ width: 11, height: 25, maxSlope: 14 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-11-25-14.jpg'), data, done); 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) sharp(fixtures.inputJpgClahe)
.clahe({ width: 50, height: 50, maxSlope: 0 }) .clahe({ width: 50, height: 50, maxSlope: 0 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-50-50-0.jpg'), data, done); 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) sharp(fixtures.inputJpgClahe)
.clahe({ width: 50, height: 50, maxSlope: 14 }) .clahe({ width: 50, height: 50, maxSlope: 14 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-50-50-14.jpg'), data, done); 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) sharp(fixtures.inputJpgClahe)
.clahe({ width: 100, height: 50, maxSlope: 3 }) .clahe({ width: 100, height: 50, maxSlope: 3 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done); 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) sharp(fixtures.inputJpgClahe)
.clahe({ width: 100, height: 100, maxSlope: 0 }) .clahe({ width: 100, height: 100, maxSlope: 0 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-100-100-0.jpg'), data, done); fixtures.assertSimilar(fixtures.expected('clahe-100-100-0.jpg'), data, done);
}); });
}); });
it('invalid maxSlope', function () { it('invalid maxSlope', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: -5 }); 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 }); 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 }); 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' }); sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 'a string' });
}); });
}); });
it('invalid width', function () { it('invalid width', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe({ width: 100.5, height: 100 }); sharp(fixtures.inputJpgClahe).clahe({ width: 100.5, height: 100 });
}); });
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe({ width: -5, height: 100 }); sharp(fixtures.inputJpgClahe).clahe({ width: -5, height: 100 });
}); });
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe({ width: true, height: 100 }); sharp(fixtures.inputJpgClahe).clahe({ width: true, height: 100 });
}); });
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe({ width: 'string test', height: 100 }); sharp(fixtures.inputJpgClahe).clahe({ width: 'string test', height: 100 });
}); });
}); });
it('invalid height', function () { it('invalid height', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100.5 }); sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100.5 });
}); });
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: -5 }); sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: -5 });
}); });
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: true }); sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: true });
}); });
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 'string test' }); sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 'string test' });
}); });
}); });
it('invalid options object', function () { it('invalid options object', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgClahe).clahe(100, 100, 5); 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) sharp(fixtures.inputJpgClahe)
.clahe({ width: 100, height: 50 }) .clahe({ width: 100, height: 50 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done); fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done);

View File

@ -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('node:fs');
const { afterEach, beforeEach, describe, it } = require('node:test');
const fs = require('fs'); const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Clone', function () { describe('Clone', () => {
beforeEach(function () { beforeEach(() => {
sharp.cache(false); sharp.cache(false);
}); });
afterEach(function () { afterEach(() => {
sharp.cache(true); 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; let finishEventsExpected = 2;
// Output stream 1 // Output stream 1
const output1 = fixtures.path('output.multi-stream.1.jpg'); const output1 = fixtures.path('output.multi-stream.1.jpg');
const writable1 = fs.createWriteStream(output1); const writable1 = fs.createWriteStream(output1);
writable1.on('finish', function () { writable1.on('finish', () => {
sharp(output1).toBuffer(function (err, data, info) { sharp(output1).toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size); assert.strictEqual(data.length, info.size);
@ -40,8 +41,8 @@ describe('Clone', function () {
// Output stream 2 // Output stream 2
const output2 = fixtures.path('output.multi-stream.2.jpg'); const output2 = fixtures.path('output.multi-stream.2.jpg');
const writable2 = fs.createWriteStream(output2); const writable2 = fs.createWriteStream(output2);
writable2.on('finish', function () { writable2.on('finish', () => {
sharp(output2).toBuffer(function (err, data, info) { sharp(output2).toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size); assert.strictEqual(data.length, info.size);
@ -64,14 +65,14 @@ describe('Clone', function () {
fs.createReadStream(fixtures.inputJpg).pipe(rotator); 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 original = sharp();
const clone = original.clone(); const clone = original.clone();
assert.strictEqual(1, original.listenerCount('finish')); assert.strictEqual(1, original.listenerCount('finish'));
assert.strictEqual(0, clone.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 original = sharp(fixtures.inputJpg);
const clone = original.clone(); const clone = original.clone();
assert.strictEqual(0, original.listenerCount('finish')); assert.strictEqual(0, original.listenerCount('finish'));

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Colour space conversion', function () { describe('Colour space conversion', () => {
it('To greyscale', function (done) { it('To greyscale', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.greyscale() .greyscale()
.toFile(fixtures.path('output.greyscale-gamma-0.0.jpg'), done); .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) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.gamma() .gamma()
@ -24,19 +25,19 @@ describe('Colour space conversion', function () {
.toFile(fixtures.path('output.greyscale-gamma-2.2.jpg'), done); .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) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.greyscale(false) .greyscale(false)
.toFile(fixtures.path('output.greyscale-not.jpg'), done); .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) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.greyscale() .greyscale()
.toColourspace('b-w') .toColourspace('b-w')
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(1, info.channels); assert.strictEqual(1, info.channels);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
@ -55,10 +56,10 @@ describe('Colour space conversion', function () {
assert.strictEqual(format, 'webp'); assert.strictEqual(format, 'webp');
}); });
it('From CMYK to sRGB', function (done) { it('From CMYK to sRGB', (_t, done) => {
sharp(fixtures.inputJpgWithCmykProfile) sharp(fixtures.inputJpgWithCmykProfile)
.resize(320) .resize(320)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format); 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) sharp(fixtures.inputJpgWithCmykProfile)
.resize(320, 240, { .resize(320, 240, {
fit: sharp.fit.contain, fit: sharp.fit.contain,
background: 'white' background: 'white'
}) })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); 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) sharp(fixtures.inputJpgWithCmykNoProfile)
.resize(320) .resize(320)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); 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) sharp(fixtures.inputTiffFogra)
.resize(320, 240) .resize(320, 240)
.toColourspace('cmyk') .toColourspace('cmyk')
.pipelineColourspace('cmyk') .pipelineColourspace('cmyk')
.withIccProfile(fixtures.path('XCMYK 2017.icc')) .withIccProfile(fixtures.path('XCMYK 2017.icc'))
.negate() .negate()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('tiff', info.format); assert.strictEqual('tiff', info.format);
assert.strictEqual(320, info.width); 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) sharp(fixtures.inputPngGradients)
.pipelineColourspace('rgb16') .pipelineColourspace('rgb16')
.resize(320) .resize(320)
.gamma() .gamma()
.toColourspace('srgb') .toColourspace('srgb')
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
fixtures.assertSimilar(fixtures.expected('colourspace-gradients-gamma-resize.png'), data, { fixtures.assertSimilar(fixtures.expected('colourspace-gradients-gamma-resize.png'), data, {
@ -177,15 +178,15 @@ describe('Colour space conversion', function () {
assert.strictEqual(b, 34); assert.strictEqual(b, 34);
}); });
it('Invalid pipelineColourspace input', function () { it('Invalid pipelineColourspace input', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.pipelineColorspace(null); .pipelineColorspace(null);
}, /Expected string for colourspace but received null of type object/); }, /Expected string for colourspace but received null of type object/);
}); });
it('Invalid toColourspace input', function () { it('Invalid toColourspace input', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.toColourspace(null); .toColourspace(null);
}); });

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
const sharp = require('../../'); const sharp = require('../../');
@ -322,7 +323,7 @@ describe('composite', () => {
describe('string gravity', () => { describe('string gravity', () => {
Object.keys(sharp.gravity).forEach(gravity => { Object.keys(sharp.gravity).forEach(gravity => {
it(gravity, done => { it(gravity, done => {
const expected = fixtures.expected('overlay-gravity-' + gravity + '.jpg'); const expected = fixtures.expected(`overlay-gravity-${gravity}.jpg`);
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(80) .resize(80)
.composite([{ .composite([{
@ -344,7 +345,7 @@ describe('composite', () => {
describe('tile and gravity', () => { describe('tile and gravity', () => {
Object.keys(sharp.gravity).forEach(gravity => { Object.keys(sharp.gravity).forEach(gravity => {
it(gravity, done => { it(gravity, done => {
const expected = fixtures.expected('overlay-tile-gravity-' + gravity + '.jpg'); const expected = fixtures.expected(`overlay-tile-gravity-${gravity}.jpg`);
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(80) .resize(80)
.composite([{ .composite([{

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Convolve', function () { describe('Convolve', () => {
it('specific convolution kernel 1', function (done) { it('specific convolution kernel 1', (_t, done) => {
sharp(fixtures.inputPngStripesV) sharp(fixtures.inputPngStripesV)
.convolve({ .convolve({
width: 3, width: 3,
@ -22,7 +23,7 @@ describe('Convolve', function () {
10, 20, 10 10, 20, 10
] ]
}) })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
@ -31,7 +32,7 @@ describe('Convolve', function () {
}); });
}); });
it('specific convolution kernel 2', function (done) { it('specific convolution kernel 2', (_t, done) => {
sharp(fixtures.inputPngStripesH) sharp(fixtures.inputPngStripesH)
.convolve({ .convolve({
width: 3, width: 3,
@ -42,7 +43,7 @@ describe('Convolve', function () {
1, 0, 1 1, 0, 1
] ]
}) })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
@ -51,7 +52,7 @@ describe('Convolve', function () {
}); });
}); });
it('horizontal Sobel operator', function (done) { it('horizontal Sobel operator', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
.convolve({ .convolve({
@ -63,7 +64,7 @@ describe('Convolve', function () {
-1, 0, 1 -1, 0, 1
] ]
}) })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
@ -72,14 +73,14 @@ describe('Convolve', function () {
}); });
}); });
describe('invalid kernel specification', function () { describe('invalid kernel specification', () => {
it('missing', function () { it('missing', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).convolve({}); sharp(fixtures.inputJpg).convolve({});
}); });
}); });
it('incorrect data format', function () { it('incorrect data format', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).convolve({ sharp(fixtures.inputJpg).convolve({
width: 3, width: 3,
height: 3, height: 3,
@ -87,8 +88,8 @@ describe('Convolve', function () {
}); });
}); });
}); });
it('incorrect dimensions', function () { it('incorrect dimensions', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).convolve({ sharp(fixtures.inputJpg).convolve({
width: 3, width: 3,
height: 4, height: 4,

View File

@ -1,15 +1,14 @@
'use strict'; const { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Dilate', function () { describe('Dilate', () => {
it('dilate 1 png', function (done) { it('dilate 1 png', (_t, done) => {
sharp(fixtures.inputPngDotAndLines) sharp(fixtures.inputPngDotAndLines)
.dilate(1) .dilate(1)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width); assert.strictEqual(100, info.width);
@ -18,10 +17,10 @@ describe('Dilate', function () {
}); });
}); });
it('dilate 1 png - default width', function (done) { it('dilate 1 png - default width', (_t, done) => {
sharp(fixtures.inputPngDotAndLines) sharp(fixtures.inputPngDotAndLines)
.dilate() .dilate()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width); assert.strictEqual(100, info.width);
@ -30,8 +29,8 @@ describe('Dilate', function () {
}); });
}); });
it('invalid dilation width', function () { it('invalid dilation width', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).dilate(-1); sharp(fixtures.inputJpg).dilate(-1);
}); });
}); });

View File

@ -1,15 +1,14 @@
'use strict'; const { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Erode', function () { describe('Erode', () => {
it('erode 1 png', function (done) { it('erode 1 png', (_t, done) => {
sharp(fixtures.inputPngDotAndLines) sharp(fixtures.inputPngDotAndLines)
.erode(1) .erode(1)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width); assert.strictEqual(100, info.width);
@ -18,10 +17,10 @@ describe('Erode', function () {
}); });
}); });
it('erode 1 png - default width', function (done) { it('erode 1 png - default width', (_t, done) => {
sharp(fixtures.inputPngDotAndLines) sharp(fixtures.inputPngDotAndLines)
.erode() .erode()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width); assert.strictEqual(100, info.width);
@ -30,8 +29,8 @@ describe('Erode', function () {
}); });
}); });
it('invalid erosion width', function () { it('invalid erosion width', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).erode(-1); sharp(fixtures.inputJpg).erode(-1);
}); });
}); });

View File

@ -1,20 +1,21 @@
// 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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Extend', function () { describe('Extend', () => {
describe('extend all sides equally via a single value', function () { describe('extend all sides equally via a single value', () => {
it('JPEG', function (done) { it('JPEG', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(120) .resize(120)
.extend(10) .extend(10)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(140, info.width); assert.strictEqual(140, info.width);
assert.strictEqual(118, info.height); assert.strictEqual(118, info.height);
@ -22,11 +23,11 @@ describe('Extend', function () {
}); });
}); });
it('Animated WebP', function (done) { it('Animated WebP', (_t, done) => {
sharp(fixtures.inputWebPAnimated, { pages: -1 }) sharp(fixtures.inputWebPAnimated, { pages: -1 })
.resize(120) .resize(120)
.extend(10) .extend(10)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(140, info.width); assert.strictEqual(140, info.width);
assert.strictEqual(140 * 9, info.height); assert.strictEqual(140 * 9, info.height);
@ -36,7 +37,7 @@ describe('Extend', function () {
}); });
['background', 'copy', 'mirror', 'repeat'].forEach(extendWith => { ['background', 'copy', 'mirror', 'repeat'].forEach(extendWith => {
it(`extends all sides with animated WebP (${extendWith})`, function (done) { it(`extends all sides with animated WebP (${extendWith})`, (_t, done) => {
sharp(fixtures.inputWebPAnimated, { pages: -1 }) sharp(fixtures.inputWebPAnimated, { pages: -1 })
.resize(120) .resize(120)
.extend({ .extend({
@ -46,7 +47,7 @@ describe('Extend', function () {
left: 40, left: 40,
right: 40 right: 40
}) })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(200, info.width); assert.strictEqual(200, info.width);
assert.strictEqual(200 * 9, info.height); assert.strictEqual(200 * 9, info.height);
@ -54,7 +55,7 @@ describe('Extend', function () {
}); });
}); });
it(`extend all sides equally with RGB (${extendWith})`, function (done) { it(`extend all sides equally with RGB (${extendWith})`, (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(120) .resize(120)
.extend({ .extend({
@ -65,7 +66,7 @@ describe('Extend', function () {
right: 10, right: 10,
background: { r: 255, g: 0, b: 0 } background: { r: 255, g: 0, b: 0 }
}) })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(140, info.width); assert.strictEqual(140, info.width);
assert.strictEqual(118, info.height); assert.strictEqual(118, info.height);
@ -73,7 +74,7 @@ describe('Extend', function () {
}); });
}); });
it(`extend sides unequally with RGBA (${extendWith})`, function (done) { it(`extend sides unequally with RGBA (${extendWith})`, (_t, done) => {
sharp(fixtures.inputPngWithTransparency16bit) sharp(fixtures.inputPngWithTransparency16bit)
.resize(120) .resize(120)
.extend({ .extend({
@ -83,7 +84,7 @@ describe('Extend', function () {
right: 35, right: 35,
background: { r: 0, g: 0, b: 0, alpha: 0 } background: { r: 0, g: 0, b: 0, alpha: 0 }
}) })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(165, info.width); assert.strictEqual(165, info.width);
assert.strictEqual(170, info.height); assert.strictEqual(170, info.height);
@ -91,7 +92,7 @@ describe('Extend', function () {
}); });
}); });
it(`PNG with 2 channels (${extendWith})`, function (done) { it(`PNG with 2 channels (${extendWith})`, (_t, done) => {
sharp(fixtures.inputPngWithGreyAlpha) sharp(fixtures.inputPngWithGreyAlpha)
.extend({ .extend({
extendWith, extendWith,
@ -101,7 +102,7 @@ describe('Extend', function () {
right: 80, right: 80,
background: 'transparent' background: 'transparent'
}) })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
@ -146,13 +147,13 @@ describe('Extend', function () {
assert.strictEqual(1470, height); assert.strictEqual(1470, height);
}); });
it('missing parameter fails', function () { it('missing parameter fails', () => {
assert.throws(function () { assert.throws(() => {
sharp().extend(); sharp().extend();
}); });
}); });
it('negative fails', function () { it('negative fails', () => {
assert.throws(function () { assert.throws(() => {
sharp().extend(-1); sharp().extend(-1);
}); });
}); });
@ -190,7 +191,7 @@ describe('Extend', function () {
assert.doesNotThrow(() => sharp().extend({ top: 1, left: 2, bottom: 3 })); assert.doesNotThrow(() => sharp().extend({ top: 1, left: 2, bottom: 3 }));
}); });
it('should add alpha channel before extending with a transparent Background', function (done) { it('should add alpha channel before extending with a transparent Background', (_t, done) => {
sharp(fixtures.inputJpgWithLandscapeExif1) sharp(fixtures.inputJpgWithLandscapeExif1)
.extend({ .extend({
bottom: 10, bottom: 10,
@ -198,7 +199,7 @@ describe('Extend', function () {
background: { r: 0, g: 0, b: 0, alpha: 0 } background: { r: 0, g: 0, b: 0, alpha: 0 }
}) })
.toFormat(sharp.format.png) .toFormat(sharp.format.png)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(610, info.width); assert.strictEqual(610, info.width);
assert.strictEqual(460, info.height); assert.strictEqual(460, info.height);

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Partial image extraction', function () { describe('Partial image extraction', () => {
it('JPEG', function (done) { it('JPEG', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extract({ left: 2, top: 2, width: 20, height: 20 }) .extract({ left: 2, top: 2, width: 20, height: 20 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(20, info.width); assert.strictEqual(20, info.width);
assert.strictEqual(20, info.height); assert.strictEqual(20, info.height);
@ -20,10 +21,10 @@ describe('Partial image extraction', function () {
}); });
}); });
it('PNG', function (done) { it('PNG', (_t, done) => {
sharp(fixtures.inputPng) sharp(fixtures.inputPng)
.extract({ left: 200, top: 300, width: 400, height: 200 }) .extract({ left: 200, top: 300, width: 400, height: 200 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(400, info.width); assert.strictEqual(400, info.width);
assert.strictEqual(200, info.height); assert.strictEqual(200, info.height);
@ -31,10 +32,10 @@ describe('Partial image extraction', function () {
}); });
}); });
it('WebP', function (done) { it('WebP', (_t, done) => {
sharp(fixtures.inputWebP) sharp(fixtures.inputWebP)
.extract({ left: 100, top: 50, width: 125, height: 200 }) .extract({ left: 100, top: 50, width: 125, height: 200 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(125, info.width); assert.strictEqual(125, info.width);
assert.strictEqual(200, info.height); assert.strictEqual(200, info.height);
@ -42,12 +43,12 @@ describe('Partial image extraction', function () {
}); });
}); });
describe('Animated WebP', function () { describe('Animated WebP', () => {
it('Before resize', function (done) { it('Before resize', (_t, done) => {
sharp(fixtures.inputWebPAnimated, { pages: -1 }) sharp(fixtures.inputWebPAnimated, { pages: -1 })
.extract({ left: 0, top: 30, width: 80, height: 20 }) .extract({ left: 0, top: 30, width: 80, height: 20 })
.resize(320, 80) .resize(320, 80)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(80 * 9, info.height); assert.strictEqual(80 * 9, info.height);
@ -55,11 +56,11 @@ describe('Partial image extraction', function () {
}); });
}); });
it('After resize', function (done) { it('After resize', (_t, done) => {
sharp(fixtures.inputWebPAnimated, { pages: -1 }) sharp(fixtures.inputWebPAnimated, { pages: -1 })
.resize(320, 320) .resize(320, 320)
.extract({ left: 0, top: 120, width: 320, height: 80 }) .extract({ left: 0, top: 120, width: 320, height: 80 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(80 * 9, info.height); assert.strictEqual(80 * 9, info.height);
@ -68,10 +69,10 @@ describe('Partial image extraction', function () {
}); });
}); });
it('TIFF', function (done) { it('TIFF', (_t, done) => {
sharp(fixtures.inputTiff) sharp(fixtures.inputTiff)
.extract({ left: 34, top: 63, width: 341, height: 529 }) .extract({ left: 34, top: 63, width: 341, height: 529 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(341, info.width); assert.strictEqual(341, info.width);
assert.strictEqual(529, info.height); assert.strictEqual(529, info.height);
@ -79,11 +80,11 @@ describe('Partial image extraction', function () {
}); });
}); });
it('Before resize', function (done) { it('Before resize', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extract({ left: 10, top: 10, width: 10, height: 500 }) .extract({ left: 10, top: 10, width: 10, height: 500 })
.resize(100, 100) .resize(100, 100)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(100, info.width); assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height); assert.strictEqual(100, info.height);
@ -91,13 +92,13 @@ describe('Partial image extraction', function () {
}); });
}); });
it('After resize and crop', function (done) { it('After resize and crop', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(500, 500, { .resize(500, 500, {
position: sharp.gravity.north position: sharp.gravity.north
}) })
.extract({ left: 10, top: 10, width: 100, height: 100 }) .extract({ left: 10, top: 10, width: 100, height: 100 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(100, info.width); assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height); assert.strictEqual(100, info.height);
@ -105,14 +106,14 @@ describe('Partial image extraction', function () {
}); });
}); });
it('Before and after resize and crop', function (done) { it('Before and after resize and crop', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extract({ left: 0, top: 0, width: 700, height: 700 }) .extract({ left: 0, top: 0, width: 700, height: 700 })
.resize(500, 500, { .resize(500, 500, {
position: sharp.gravity.north position: sharp.gravity.north
}) })
.extract({ left: 10, top: 10, width: 100, height: 100 }) .extract({ left: 10, top: 10, width: 100, height: 100 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(100, info.width); assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height); assert.strictEqual(100, info.height);
@ -120,12 +121,12 @@ describe('Partial image extraction', function () {
}); });
}); });
it('Extract then rotate', function (done) { it('Extract then rotate', (_t, done) => {
sharp(fixtures.inputPngWithGreyAlpha) sharp(fixtures.inputPngWithGreyAlpha)
.extract({ left: 20, top: 10, width: 380, height: 280 }) .extract({ left: 20, top: 10, width: 380, height: 280 })
.rotate(90) .rotate(90)
.jpeg() .jpeg()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(280, info.width); assert.strictEqual(280, info.width);
assert.strictEqual(380, info.height); assert.strictEqual(380, info.height);
@ -133,11 +134,11 @@ describe('Partial image extraction', function () {
}); });
}); });
it('Rotate then extract', function (done) { it('Rotate then extract', (_t, done) => {
sharp(fixtures.inputPngWithGreyAlpha) sharp(fixtures.inputPngWithGreyAlpha)
.rotate(90) .rotate(90)
.extract({ left: 20, top: 10, width: 280, height: 380 }) .extract({ left: 20, top: 10, width: 280, height: 380 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(280, info.width); assert.strictEqual(280, info.width);
assert.strictEqual(380, info.height); assert.strictEqual(380, info.height);
@ -145,12 +146,12 @@ describe('Partial image extraction', function () {
}); });
}); });
it('Extract then rotate then extract', function (done) { it('Extract then rotate then extract', (_t, done) => {
sharp(fixtures.inputPngWithGreyAlpha) sharp(fixtures.inputPngWithGreyAlpha)
.extract({ left: 20, top: 10, width: 180, height: 280 }) .extract({ left: 20, top: 10, width: 180, height: 280 })
.rotate(90) .rotate(90)
.extract({ left: 20, top: 10, width: 200, height: 100 }) .extract({ left: 20, top: 10, width: 200, height: 100 })
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(200, info.width); assert.strictEqual(200, info.width);
assert.strictEqual(100, info.height); assert.strictEqual(100, info.height);
@ -158,12 +159,12 @@ describe('Partial image extraction', function () {
}); });
}); });
it('Extract then rotate non-90 anagle', function (done) { it('Extract then rotate non-90 anagle', (_t, done) => {
sharp(fixtures.inputPngWithGreyAlpha) sharp(fixtures.inputPngWithGreyAlpha)
.extract({ left: 20, top: 10, width: 380, height: 280 }) .extract({ left: 20, top: 10, width: 380, height: 280 })
.rotate(45) .rotate(45)
.jpeg() .jpeg()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(467, info.width); assert.strictEqual(467, info.width);
assert.strictEqual(467, info.height); assert.strictEqual(467, info.height);
@ -171,12 +172,12 @@ describe('Partial image extraction', function () {
}); });
}); });
it('Rotate then extract non-90 angle', function (done) { it('Rotate then extract non-90 angle', (_t, done) => {
sharp(fixtures.inputPngWithGreyAlpha) sharp(fixtures.inputPngWithGreyAlpha)
.rotate(45) .rotate(45)
.extract({ left: 20, top: 10, width: 380, height: 280 }) .extract({ left: 20, top: 10, width: 380, height: 280 })
.jpeg() .jpeg()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(380, info.width); assert.strictEqual(380, info.width);
assert.strictEqual(280, info.height); assert.strictEqual(280, info.height);
@ -219,11 +220,11 @@ describe('Partial image extraction', function () {
image: fixtures.inputJpgWithLandscapeExif8 image: fixtures.inputJpgWithLandscapeExif8
} }
].forEach(({ name, image }) => { ].forEach(({ name, image }) => {
it(name, function (done) { it(name, (_t, done) => {
sharp(image) sharp(image)
.rotate() .rotate()
.extract({ left: 0, top: 208, width: 60, height: 40 }) .extract({ left: 0, top: 208, width: 60, height: 40 })
.toBuffer(function (err, data) { .toBuffer((err, data) => {
if (err) throw err; if (err) throw err;
fixtures.assertSimilar(fixtures.expected('rotate-mirror-extract.jpg'), data, done); fixtures.assertSimilar(fixtures.expected('rotate-mirror-extract.jpg'), data, done);
}); });
@ -231,67 +232,67 @@ describe('Partial image extraction', function () {
}); });
}); });
describe('Invalid parameters', function () { describe('Invalid parameters', () => {
describe('using the legacy extract(top,left,width,height) syntax', function () { describe('using the legacy extract(top,left,width,height) syntax', () => {
it('String top', function () { it('String top', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).extract('spoons', 10, 10, 10); sharp(fixtures.inputJpg).extract('spoons', 10, 10, 10);
}); });
}); });
it('Non-integral left', function () { it('Non-integral left', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).extract(10, 10.2, 10, 10); sharp(fixtures.inputJpg).extract(10, 10.2, 10, 10);
}); });
}); });
it('Negative width - negative', function () { it('Negative width - negative', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).extract(10, 10, -10, 10); sharp(fixtures.inputJpg).extract(10, 10, -10, 10);
}); });
}); });
it('Null height', function () { it('Null height', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).extract(10, 10, 10, null); sharp(fixtures.inputJpg).extract(10, 10, 10, null);
}); });
}); });
}); });
it('Undefined', function () { it('Undefined', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).extract(); sharp(fixtures.inputJpg).extract();
}); });
}); });
it('String top', function () { it('String top', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).extract({ left: 10, top: 'spoons', width: 10, height: 10 }); sharp(fixtures.inputJpg).extract({ left: 10, top: 'spoons', width: 10, height: 10 });
}); });
}); });
it('Non-integral left', function () { it('Non-integral left', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).extract({ left: 10.2, top: 10, width: 10, height: 10 }); sharp(fixtures.inputJpg).extract({ left: 10.2, top: 10, width: 10, height: 10 });
}); });
}); });
it('Negative width - negative', function () { it('Negative width - negative', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).extract({ left: 10, top: 10, width: -10, height: 10 }); sharp(fixtures.inputJpg).extract({ left: 10, top: 10, width: -10, height: 10 });
}); });
}); });
it('Null height', function () { it('Null height', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg).extract({ left: 10, top: 10, width: 10, height: null }); sharp(fixtures.inputJpg).extract({ left: 10, top: 10, width: 10, height: null });
}); });
}); });
it('Bad image area', function (done) { it('Bad image area', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extract({ left: 3000, top: 10, width: 10, height: 10 }) .extract({ left: 3000, top: 10, width: 10, height: 10 })
.toBuffer(function (err) { .toBuffer((err) => {
assert(err instanceof Error); assert(err instanceof Error);
assert.strictEqual(err.message, 'extract_area: bad extract area'); assert.strictEqual(err.message, 'extract_area: bad extract area');
done(); done();
@ -301,7 +302,7 @@ describe('Partial image extraction', function () {
it('Multiple extract emits warning', () => { it('Multiple extract emits warning', () => {
let warningMessage = ''; let warningMessage = '';
const s = sharp(); const s = sharp();
s.on('warning', function (msg) { warningMessage = msg; }); s.on('warning', (msg) => { warningMessage = msg; });
const options = { top: 0, left: 0, width: 1, height: 1 }; const options = { top: 0, left: 0, width: 1, height: 1 };
s.extract(options).extract(options); s.extract(options).extract(options);
assert.strictEqual(warningMessage, ''); assert.strictEqual(warningMessage, '');
@ -312,7 +313,7 @@ describe('Partial image extraction', function () {
it('Multiple rotate+extract emits warning', () => { it('Multiple rotate+extract emits warning', () => {
let warningMessage = ''; let warningMessage = '';
const s = sharp().rotate(); const s = sharp().rotate();
s.on('warning', function (msg) { warningMessage = msg; }); s.on('warning', (msg) => { warningMessage = msg; });
const options = { top: 0, left: 0, width: 1, height: 1 }; const options = { top: 0, left: 0, width: 1, height: 1 };
s.extract(options).extract(options); s.extract(options).extract(options);
assert.strictEqual(warningMessage, ''); assert.strictEqual(warningMessage, '');
@ -323,7 +324,7 @@ describe('Partial image extraction', function () {
it('Multiple extract+resize emits warning', () => { it('Multiple extract+resize emits warning', () => {
let warningMessage = ''; let warningMessage = '';
const s = sharp(); const s = sharp();
s.on('warning', function (msg) { warningMessage = msg; }); s.on('warning', (msg) => { warningMessage = msg; });
const options = { top: 0, left: 0, width: 1, height: 1 }; const options = { top: 0, left: 0, width: 1, height: 1 };
s.extract(options).extract(options); s.extract(options).extract(options);
assert.strictEqual(warningMessage, ''); assert.strictEqual(warningMessage, '');

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Image channel extraction', function () { describe('Image channel extraction', () => {
it('Red channel', function (done) { it('Red channel', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extractChannel('red') .extractChannel('red')
.resize(320, 240) .resize(320, 240)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@ -21,11 +22,11 @@ describe('Image channel extraction', function () {
}); });
}); });
it('Green channel', function (done) { it('Green channel', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extractChannel('green') .extractChannel('green')
.resize(320, 240) .resize(320, 240)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@ -33,11 +34,11 @@ describe('Image channel extraction', function () {
}); });
}); });
it('Blue channel', function (done) { it('Blue channel', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extractChannel('blue') .extractChannel('blue')
.resize(320, 240) .resize(320, 240)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@ -45,11 +46,11 @@ describe('Image channel extraction', function () {
}); });
}); });
it('Blue channel by number', function (done) { it('Blue channel by number', (_t, done) => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extractChannel(2) .extractChannel(2)
.resize(320, 240) .resize(320, 240)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@ -66,23 +67,23 @@ describe('Image channel extraction', function () {
assert.strictEqual(chroma, 104); assert.strictEqual(chroma, 104);
}); });
it('Alpha from 16-bit PNG', function (done) { it('Alpha from 16-bit PNG', (_t, done) => {
const output = fixtures.path('output.extract-alpha-16bit.png'); const output = fixtures.path('output.extract-alpha-16bit.png');
sharp(fixtures.inputPngWithTransparency16bit) sharp(fixtures.inputPngWithTransparency16bit)
.resize(16) .resize(16)
.extractChannel(3) .extractChannel(3)
.toFile(output, function (err) { .toFile(output, (err) => {
if (err) throw err; if (err) throw err;
fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-16bit.png')); fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-16bit.png'));
done(); done();
}); });
}); });
it('Alpha from 2-channel input', function (done) { it('Alpha from 2-channel input', (_t, done) => {
const output = fixtures.path('output.extract-alpha-2-channel.png'); const output = fixtures.path('output.extract-alpha-2-channel.png');
sharp(fixtures.inputPngWithGreyAlpha) sharp(fixtures.inputPngWithGreyAlpha)
.extractChannel('alpha') .extractChannel('alpha')
.toFile(output, function (err, info) { .toFile(output, (err, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(1, info.channels); assert.strictEqual(1, info.channels);
fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-2-channel.png')); fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-2-channel.png'));
@ -90,15 +91,15 @@ describe('Image channel extraction', function () {
}); });
}); });
it('Invalid channel number', function () { it('Invalid channel number', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extractChannel(-1); .extractChannel(-1);
}); });
}); });
it('No arguments', function () { it('No arguments', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.extractChannel(); .extractChannel();
}); });

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert'); const fs = require('node:fs');
const fs = require('fs');
const sharp = require('../../lib'); const sharp = require('../../lib');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('failOn', () => { describe('failOn', () => {
it('handles truncated JPEG', function (done) { it('handles truncated JPEG', (_t, done) => {
sharp(fixtures.inputJpgTruncated, { failOn: 'none' }) sharp(fixtures.inputJpgTruncated, { failOn: 'none' })
.resize(32, 24) .resize(32, 24)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(32, info.width); assert.strictEqual(32, info.width);
@ -22,17 +23,17 @@ describe('failOn', () => {
}); });
}); });
it('handles truncated PNG, emits warnings', function (done) { it('handles truncated PNG, emits warnings', (_t, done) => {
let isWarningEmitted = false; let isWarningEmitted = false;
sharp(fixtures.inputPngTruncated, { failOn: 'none' }) sharp(fixtures.inputPngTruncated, { failOn: 'none' })
.on('warning', function (warning) { .on('warning', (warning) => {
assert.ok( assert.ok(
['read gave 2 warnings', 'not enough data', 'end of stream'] ['read gave 2 warnings', 'not enough data', 'end of stream']
.some(m => warning.includes(m))); .some(m => warning.includes(m)));
isWarningEmitted = true; isWarningEmitted = true;
}) })
.resize(32, 24) .resize(32, 24)
.toBuffer(function (err, data, info) { .toBuffer((err, _data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, isWarningEmitted); assert.strictEqual(true, isWarningEmitted);
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
@ -70,8 +71,8 @@ describe('failOn', () => {
); );
}); });
it('returns errors to callback for truncated JPEG', function (done) { it('returns errors to callback for truncated JPEG', (_t, done) => {
sharp(fixtures.inputJpgTruncated, { failOn: 'truncated' }).toBuffer(function (err, data, info) { sharp(fixtures.inputJpgTruncated, { failOn: 'truncated' }).toBuffer((err, data, info) => {
assert.ok(err.message.includes('VipsJpeg: premature end of'), err); assert.ok(err.message.includes('VipsJpeg: premature end of'), err);
assert.strictEqual(data, undefined); assert.strictEqual(data, undefined);
assert.strictEqual(info, undefined); assert.strictEqual(info, undefined);
@ -79,8 +80,8 @@ describe('failOn', () => {
}); });
}); });
it('returns errors to callback for truncated PNG', function (done) { it('returns errors to callback for truncated PNG', (_t, done) => {
sharp(fixtures.inputPngTruncated, { failOn: 'truncated' }).toBuffer(function (err, data, info) { sharp(fixtures.inputPngTruncated, { failOn: 'truncated' }).toBuffer((err, data, info) => {
assert.ok(err.message.includes('read error'), err); assert.ok(err.message.includes('read error'), err);
assert.strictEqual(data, undefined); assert.strictEqual(data, undefined);
assert.strictEqual(info, undefined); assert.strictEqual(info, undefined);
@ -88,7 +89,7 @@ describe('failOn', () => {
}); });
}); });
it('rejects promises for truncated JPEG', function (done) { it('rejects promises for truncated JPEG', (_t, done) => {
sharp(fixtures.inputJpgTruncated, { failOn: 'error' }) sharp(fixtures.inputJpgTruncated, { failOn: 'error' })
.toBuffer() .toBuffer()
.then(() => { .then(() => {
@ -104,4 +105,11 @@ describe('failOn', () => {
fs.createReadStream(fixtures.inputJpgTruncated).pipe(writable); fs.createReadStream(fixtures.inputJpgTruncated).pipe(writable);
return writable.toBuffer(); return writable.toBuffer();
}); });
it('converts warnings to error for GeoTIFF', async () => {
await assert.rejects(
sharp(fixtures.inputTiffGeo).toBuffer(),
/Tag 34737/
);
});
}); });

View File

@ -1,28 +1,29 @@
// 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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Test fixtures', function () { describe('Test fixtures', () => {
describe('assertMaxColourDistance', function () { describe('assertMaxColourDistance', () => {
it('should throw an Error when images have a different number of channels', function () { it('should throw an Error when images have a different number of channels', () => {
assert.throws(function () { assert.throws(() => {
fixtures.assertMaxColourDistance(fixtures.inputPngOverlayLayer1, fixtures.inputJpg); fixtures.assertMaxColourDistance(fixtures.inputPngOverlayLayer1, fixtures.inputJpg);
}); });
}); });
it('should throw an Error when images have different dimensions', function () { it('should throw an Error when images have different dimensions', () => {
assert.throws(function () { assert.throws(() => {
fixtures.assertMaxColourDistance(fixtures.inputJpg, fixtures.inputJpgWithExif); fixtures.assertMaxColourDistance(fixtures.inputJpg, fixtures.inputJpgWithExif);
}); });
}); });
it('should accept a zero threshold when comparing an image to itself', function () { it('should accept a zero threshold when comparing an image to itself', () => {
const image = fixtures.inputPngOverlayLayer0; const image = fixtures.inputPngOverlayLayer0;
fixtures.assertMaxColourDistance(image, image, 0); fixtures.assertMaxColourDistance(image, image, 0);
}); });
it('should accept a numeric threshold for two different images', function () { it('should accept a numeric threshold for two different images', () => {
fixtures.assertMaxColourDistance(fixtures.inputPngOverlayLayer0, fixtures.inputPngOverlayLayer1, 100); fixtures.assertMaxColourDistance(fixtures.inputPngOverlayLayer0, fixtures.inputPngOverlayLayer1, 100);
}); });
}); });

View File

@ -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 { describe, it } = require('node:test');
const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
describe('Gamma correction', function () { describe('Gamma correction', () => {
it('value of 0.0 (disabled)', function (done) { it('value of 0.0 (disabled)', (_t, done) => {
sharp(fixtures.inputJpgWithGammaHoliness) sharp(fixtures.inputJpgWithGammaHoliness)
.resize(129, 111) .resize(129, 111)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(129, info.width); assert.strictEqual(129, info.width);
@ -21,11 +22,11 @@ describe('Gamma correction', function () {
}); });
}); });
it('value of 2.2 (default)', function (done) { it('value of 2.2 (default)', (_t, done) => {
sharp(fixtures.inputJpgWithGammaHoliness) sharp(fixtures.inputJpgWithGammaHoliness)
.resize(129, 111) .resize(129, 111)
.gamma() .gamma()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(129, info.width); assert.strictEqual(129, info.width);
@ -34,11 +35,11 @@ describe('Gamma correction', function () {
}); });
}); });
it('value of 3.0', function (done) { it('value of 3.0', (_t, done) => {
sharp(fixtures.inputJpgWithGammaHoliness) sharp(fixtures.inputJpgWithGammaHoliness)
.resize(129, 111) .resize(129, 111)
.gamma(3) .gamma(3)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(129, info.width); assert.strictEqual(129, info.width);
@ -47,11 +48,11 @@ describe('Gamma correction', function () {
}); });
}); });
it('input value of 2.2, output value of 3.0', function (done) { it('input value of 2.2, output value of 3.0', (_t, done) => {
sharp(fixtures.inputJpgWithGammaHoliness) sharp(fixtures.inputJpgWithGammaHoliness)
.resize(129, 111) .resize(129, 111)
.gamma(2.2, 3.0) .gamma(2.2, 3.0)
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(129, info.width); assert.strictEqual(129, info.width);
@ -60,12 +61,12 @@ describe('Gamma correction', function () {
}); });
}); });
it('alpha transparency', function (done) { it('alpha transparency', (_t, done) => {
sharp(fixtures.inputPngOverlayLayer1) sharp(fixtures.inputPngOverlayLayer1)
.resize(320) .resize(320)
.gamma() .gamma()
.jpeg() .jpeg()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
@ -73,14 +74,14 @@ describe('Gamma correction', function () {
}); });
}); });
it('invalid first parameter value', function () { it('invalid first parameter value', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgWithGammaHoliness).gamma(4); sharp(fixtures.inputJpgWithGammaHoliness).gamma(4);
}); });
}); });
it('invalid second parameter value', function () { it('invalid second parameter value', () => {
assert.throws(function () { assert.throws(() => {
sharp(fixtures.inputJpgWithGammaHoliness).gamma(2.2, 4); sharp(fixtures.inputJpgWithGammaHoliness).gamma(2.2, 4);
}); });
}); });

View File

@ -1,10 +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
*/
'use strict'; const fs = require('node:fs');
const { describe, it } = require('node:test');
const fs = require('fs'); const assert = require('node:assert');
const assert = require('assert');
const sharp = require('../../'); const sharp = require('../../');
const fixtures = require('../fixtures'); const fixtures = require('../fixtures');
@ -198,11 +199,11 @@ describe('GIF input', () => {
); );
}); });
it('should work with streams when only animated is set', function (done) { it('should work with streams when only animated is set', (_t, done) => {
fs.createReadStream(fixtures.inputGifAnimated) fs.createReadStream(fixtures.inputGifAnimated)
.pipe(sharp({ animated: true })) .pipe(sharp({ animated: true }))
.gif() .gif()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual('gif', info.format); assert.strictEqual('gif', info.format);
@ -210,11 +211,11 @@ describe('GIF input', () => {
}); });
}); });
it('should work with streams when only pages is set', function (done) { it('should work with streams when only pages is set', (_t, done) => {
fs.createReadStream(fixtures.inputGifAnimated) fs.createReadStream(fixtures.inputGifAnimated)
.pipe(sharp({ pages: -1 })) .pipe(sharp({ pages: -1 }))
.gif() .gif()
.toBuffer(function (err, data, info) { .toBuffer((err, data, info) => {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual('gif', info.format); assert.strictEqual('gif', info.format);

Some files were not shown because too many files have changed in this diff Show More