Compare commits

..

No commits in common. "main" and "v0.34.4-rc.3" have entirely different histories.

137 changed files with 2846 additions and 3021 deletions

View File

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

View File

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

View File

@ -6,6 +6,8 @@ Hello, thank you for your interest in helping!
Please create a [new issue](https://github.com/lovell/sharp/issues/new) containing the steps to reproduce the problem.
If you're having installation problems, please include the output of running `npm install --verbose sharp`.
New bugs are assigned a `triage` label whilst under investigation.
## Submit a new feature request
@ -14,7 +16,7 @@ If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exist
it's probably fastest to add a comment to it about your requirement.
Implementation is usually straightforward if libvips
[already supports](https://www.libvips.org/API/current/function-list.html)
[already supports](https://www.libvips.org/API/current/func-list.html)
the feature you need.
## Submit a Pull Request to fix a bug
@ -25,11 +27,12 @@ Please select the `main` branch as the destination for your Pull Request so your
Please squash your changes into a single commit using a command like `git rebase -i upstream/main`.
To test C++ changes, you can compile the module using `npm run build` and then run the tests using `npm test`.
To test C++ changes, you can compile the module using `npm install --build-from-source` and then run the tests using `npm test`.
## Submit a Pull Request with a new feature
Please add JavaScript [unit tests](https://github.com/lovell/sharp/tree/main/test/unit) to cover your new feature.
A test coverage report for the JavaScript code is generated in the `coverage/lcov-report` directory.
Please also update the [TypeScript definitions](https://github.com/lovell/sharp/tree/main/lib/index.d.ts), along with the [type definition tests](https://github.com/lovell/sharp/tree/main/test/types/sharp.test-d.ts).
Where possible, the functional tests use gradient-based perceptual hashes

View File

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

View File

@ -43,30 +43,30 @@ jobs:
runtime: bun
- name: darwin-x64-node-npm
runs-on: macos-15-intel
runs-on: macos-13
runtime: node
package-manager: npm
- name: darwin-x64-node-pnpm
runs-on: macos-15-intel
runs-on: macos-13
runtime: node
package-manager: pnpm
- name: darwin-x64-node-yarn
runs-on: macos-15-intel
runs-on: macos-13
runtime: node
package-manager: yarn
- name: darwin-x64-node-yarn-pnp
runs-on: macos-15-intel
runs-on: macos-13
runtime: node
package-manager: yarn-pnp
- name: darwin-x64-node-yarn-v1
runs-on: macos-15-intel
runs-on: macos-13
runtime: node
package-manager: yarn-v1
- name: darwin-x64-deno
runs-on: macos-15-intel
runs-on: macos-13
runtime: deno
- name: darwin-x64-bun
runs-on: macos-15-intel
runs-on: macos-13
runtime: bun
- name: win32-x64-node-npm
@ -96,7 +96,7 @@ jobs:
steps:
- name: Install Node.js
if: ${{ matrix.runtime == 'node' }}
uses: actions/setup-node@v5
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install pnpm
@ -106,9 +106,9 @@ jobs:
version: 8
- name: Install Deno
if: ${{ matrix.runtime == 'deno' }}
uses: denoland/setup-deno@v2
uses: denoland/setup-deno@v1
with:
deno-version: v2.x
deno-version: v1.x
- name: Install Bun
if: ${{ matrix.runtime == 'bun' }}
uses: oven-sh/setup-bun@v2
@ -117,7 +117,7 @@ jobs:
- name: Version
id: version
uses: actions/github-script@v8
uses: actions/github-script@v7
with:
script: |
core.setOutput('semver', context.ref.replace('refs/tags/v',''))
@ -185,9 +185,7 @@ jobs:
- name: Run with Deno
if: ${{ matrix.runtime == 'deno' }}
run: |
deno install
deno run --allow-env --allow-ffi --allow-read --allow-sys release.mjs
run: deno run --allow-read --allow-ffi release.mjs
- name: Run with Bun
if: ${{ matrix.runtime == 'bun' }}

7
.mocharc.jsonc Normal file
View File

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

View File

@ -1,26 +0,0 @@
{
"$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
import starlight from '@astrojs/starlight';
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import starlightAutoSidebar from 'starlight-auto-sidebar';
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 path from 'node:path';

View File

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

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.
See also [flatten](/api-operation/#flatten).
See also [flatten](/api-operation#flatten).
**Example**

View File

@ -80,7 +80,7 @@ as defined by [toColourspace](#tocolourspace).
| Param | Type | Description |
| --- | --- | --- |
| [colourspace] | <code>string</code> | pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://www.libvips.org/API/current/enum.Interpretation.html) |
| [colourspace] | <code>string</code> | pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://github.com/libvips/libvips/blob/41cff4e9d0838498487a00623462204eb10ee5b8/libvips/iofuncs/enumtypes.c#L774) |
**Example**
```js
@ -123,7 +123,7 @@ By default output image will be web-friendly sRGB, with additional channels inte
| Param | Type | Description |
| --- | --- | --- |
| [colourspace] | <code>string</code> | output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html) |
| [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) |
**Example**
```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`.
More information about blend modes can be found at
https://www.libvips.org/API/current/enum.BlendMode.html
https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
and https://www.cairographics.org/operators/
@ -64,8 +64,8 @@ and https://www.cairographics.org/operators/
| [images[].raw.height] | <code>Number</code> | | |
| [images[].raw.channels] | <code>Number</code> | | |
| [images[].animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image. |
| [images[].failOn] | <code>string</code> | <code>&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/) |
| [images[].failOn] | <code>string</code> | <code>&quot;&#x27;warning&#x27;&quot;</code> | @see [constructor parameters](/api-constructor#parameters) |
| [images[].limitInputPixels] | <code>number</code> \| <code>boolean</code> | <code>268402689</code> | @see [constructor parameters](/api-constructor#parameters) |
**Example**
```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.
Dimensions in the response will respect the `page` and `pages` properties of the
[constructor parameters](/api-constructor/).
[constructor parameters](/api-constructor#parameters).
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
- `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
- `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
- `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
- `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
- `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/enum.BandFormat.html)
- `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
- `density`: Number of pixels per inch (DPI), if present
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan

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.
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/method.Image.sharpen.html) operation.
See [libvips sharpen](https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen) operation.
**Throws**:

View File

@ -201,7 +201,7 @@ const dataWithMergedExif = await sharp(inputWithExif)
Keep ICC profile from the input image in the output image.
When input and output colour spaces differ, use with [toColourspace](/api-colour/#tocolourspace) and optionally [pipelineColourspace](/api-colour/#pipelinecolourspace).
Where necessary, will attempt to convert the output colour space to match the profile.
**Since**: 0.33.0
@ -211,14 +211,6 @@ const outputWithIccProfile = await sharp(inputWithIccProfile)
.keepIccProfile()
.toBuffer();
```
**Example**
```js
const cmykOutputWithIccProfile = await sharp(cmykInputWithIccProfile)
.pipelineColourspace('cmyk')
.toColourspace('cmyk')
.keepIccProfile()
.toBuffer();
```
## withIccProfile
@ -438,7 +430,7 @@ Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
Set `palette` to `true` for slower, indexed PNG output.
For 16 bits per pixel output, convert to `rgb16` via
[toColourspace](/api-colour/#tocolourspace).
[toColourspace](/api-colour#tocolourspace).
**Throws**:
@ -597,7 +589,7 @@ Use these JP2 options for output image.
Requires libvips compiled with support for OpenJPEG.
The prebuilt binaries do not include this - see
[installing a custom libvips](/install#custom-libvips).
[installing a custom libvips](https://sharp.pixelplumbing.com/install#custom-libvips).
**Throws**:
@ -654,7 +646,6 @@ instead of providing `xres` and `yres` in pixels/mm.
| [options.quality] | <code>number</code> | <code>80</code> | quality, integer 1-100 |
| [options.force] | <code>boolean</code> | <code>true</code> | force TIFF output, otherwise attempt to use input format |
| [options.compression] | <code>string</code> | <code>&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.pyramid] | <code>boolean</code> | <code>false</code> | write an image pyramid |
| [options.tile] | <code>boolean</code> | <code>false</code> | write a tiled tiff |
@ -762,7 +753,7 @@ This feature is experimental, please do not use in production systems.
Requires libvips compiled with support for libjxl.
The prebuilt binaries do not include this - see
[installing a custom libvips](/install/#custom-libvips).
[installing a custom libvips](https://sharp.pixelplumbing.com/install#custom-libvips).
**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.
:::note
Further [control over performance](/performance/) is available.
Further [control over performance](/performance) is available.
:::

View File

@ -1,12 +1,10 @@
---
title: v0.34.4 - 17th September 2025
title: v0.34.4 - TBD
slug: changelog/v0.34.4
---
* Upgrade to libvips v8.17.2 for upstream bug fixes.
* Ensure TIFF `subifd` and OpenSlide `level` input options are respected (regression in 0.34.3).
* Ensure `autoOrient` occurs before non-90 angle rotation.
[#4425](https://github.com/lovell/sharp/issues/4425)

View File

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

View File

@ -17,8 +17,7 @@ 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)"
```
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.
libvips uses a glib-managed thread pool to avoid the overhead of spawning new threads.
The default number of threads used to concurrently process each image is the same as the number of CPU cores,
except when using glibc-based Linux without jemalloc, where the default is `1` to help reduce memory fragmentation.
@ -26,7 +25,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.
To reduce memory fragmentation when using the default Linux glibc memory allocator, set the
[`MALLOC_ARENA_MAX`](https://sourceware.org/glibc/manual/latest/html_node/Memory-Allocation-Tunables.html)
[`MALLOC_ARENA_MAX`](https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html)
environment variable before the Node.js process starts to reduce the number of memory pools.
```sh frame="none"

View File

@ -1,38 +0,0 @@
/*!
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,12 +1,39 @@
/*!
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 {
const { useGlobalLibvips } = require('../lib/libvips');
if (useGlobalLibvips() || process.env.npm_config_build_from_source) {
process.exit(1);
const { useGlobalLibvips, globalLibvipsVersion, log, spawnRebuild } = require('../lib/libvips');
const buildFromSource = (msg) => {
log(msg);
log('Attempting to build from source via node-gyp');
try {
const addonApi = require('node-addon-api');
log(`Found node-addon-api ${addonApi.version || ''}`);
} catch (err) {
log('Please add node-addon-api to your dependencies');
return;
}
try {
const gyp = require('node-gyp');
log(`Found node-gyp ${gyp().version}`);
} catch (err) {
log('Please add node-gyp to your dependencies');
return;
}
log('See https://sharp.pixelplumbing.com/install#building-from-source');
const status = spawnRebuild();
if (status !== 0) {
process.exit(status);
}
};
if (useGlobalLibvips(log)) {
buildFromSource(`Detected globally-installed libvips v${globalLibvipsVersion()}`);
} else if (process.env.npm_config_build_from_source) {
buildFromSource('Detected --build-from-source flag');
}
} catch (err) {
const summary = err.message.split(/\n/).slice(0, 1);

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');
@ -18,7 +18,7 @@ const bool = {
/**
* Remove alpha channels, if any. This is a no-op if the image does not have an alpha channel.
*
* See also {@link /api-operation/#flatten flatten}.
* See also {@link /api-operation#flatten|flatten}.
*
* @example
* sharp('rgba.png')
@ -163,7 +163,7 @@ function bandbool (boolOp) {
* @module Sharp
* @private
*/
module.exports = (Sharp) => {
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
// Public instance functions
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 is = require('./is');
@ -69,7 +69,7 @@ function grayscale (grayscale) {
*
* The input image will be converted to the provided colourspace at the start of the pipeline.
* All operations will use this colourspace before converting to the output colourspace,
* as defined by {@link #tocolourspace toColourspace}.
* as defined by {@link #tocolourspace|toColourspace}.
*
* @since 0.29.0
*
@ -80,7 +80,7 @@ function grayscale (grayscale) {
* .toColourspace('srgb')
* .toFile('16bpc-pipeline-to-8bpc-output.png')
*
* @param {string} [colourspace] - pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
* @param {string} [colourspace] - pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://github.com/libvips/libvips/blob/41cff4e9d0838498487a00623462204eb10ee5b8/libvips/iofuncs/enumtypes.c#L774)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@ -112,7 +112,7 @@ function pipelineColorspace (colorspace) {
* .toColourspace('rgb16')
* .toFile('16-bpp.png')
*
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/3c0bfdf74ce1dc37a6429bed47fa76f16e2cd70a/libvips/iofuncs/enumtypes.c#L777-L794)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@ -141,10 +141,7 @@ function toColorspace (colorspace) {
* @throws {Error} Invalid value
*/
function _getBackgroundColourOption (value) {
if (
is.object(value) ||
(is.string(value) && value.length >= 3 && value.length <= 200)
) {
if (is.object(value) || is.string(value)) {
const colour = color(value);
return [
colour.red(),
@ -175,7 +172,7 @@ function _setBackgroundColourOption (key, value) {
* @module Sharp
* @private
*/
module.exports = (Sharp) => {
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
// Public
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');
@ -56,7 +56,7 @@ const blend = {
* `hard-light`, `soft-light`, `difference`, `exclusion`.
*
* More information about blend modes can be found at
* https://www.libvips.org/API/current/enum.BlendMode.html
* https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
* and https://www.cairographics.org/operators/
*
* @since 0.22.0
@ -123,8 +123,8 @@ const blend = {
* @param {Number} [images[].raw.height]
* @param {Number} [images[].raw.channels]
* @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor/ constructor parameters}
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor/ constructor parameters}
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor#parameters|constructor parameters}
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
@ -206,7 +206,7 @@ function composite (images) {
* @module Sharp
* @private
*/
module.exports = (Sharp) => {
module.exports = function (Sharp) {
Sharp.prototype.composite = composite;
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 stream = require('node:stream');
@ -12,10 +12,6 @@ require('./sharp');
// Use NODE_DEBUG=sharp to enable libvips warnings
const debuglog = util.debuglog('sharp');
const queueListener = (queueLength) => {
Sharp.queue.emit('change', queueLength);
};
/**
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
*
@ -209,7 +205,6 @@ const queueListener = (queueLength) => {
* @throws {Error} Invalid parameters
*/
const Sharp = function (input, options) {
// biome-ignore lint/complexity/noArguments: constructor factory
if (arguments.length === 1 && !is.defined(input)) {
throw new Error('Invalid input');
}
@ -358,7 +353,6 @@ const Sharp = function (input, options) {
gifProgressive: false,
tiffQuality: 80,
tiffCompression: 'jpeg',
tiffBigtiff: false,
tiffPredictor: 'horizontal',
tiffPyramid: false,
tiffMiniswhite: false,
@ -402,7 +396,9 @@ const Sharp = function (input, options) {
debuglog(warning);
},
// Function to notify of queue length changes
queueListener
queueListener: function (queueLength) {
Sharp.queue.emit('change', queueLength);
}
};
this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
return this;

25
lib/index.d.ts vendored
View File

@ -27,7 +27,7 @@
/// <reference types="node" />
import type { Duplex } from 'node:stream';
import { Duplex } from 'stream';
//#region Constructor functions
@ -860,7 +860,6 @@ declare namespace sharp {
| JxlOptions
| GifOptions
| Jp2Options
| RawOptions
| TiffOptions,
): Sharp;
@ -1028,11 +1027,11 @@ declare namespace sharp {
openSlide?: OpenSlideInputOptions | undefined;
/** JPEG 2000 specific input options */
jp2?: Jp2InputOptions | undefined;
/** @deprecated Use {@link SharpOptions.tiff} instead */
/** Deprecated: use tiff.subifd instead */
subifd?: number | undefined;
/** @deprecated Use {@link SharpOptions.pdf} instead */
/** Deprecated: use pdf.background instead */
pdfBackground?: Colour | Color | undefined;
/** @deprecated Use {@link SharpOptions.openSlide} instead */
/** Deprecated: use openSlide.level instead */
level?: number | undefined;
/** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */
animated?: boolean | undefined;
@ -1182,10 +1181,6 @@ declare namespace sharp {
'IFD3'?: ExifDir;
}
type HeifCompression = 'av1' | 'hevc';
type Unit = 'inch' | 'cm';
interface WriteableMetadata {
/** Number of pixels per inch (DPI) */
density?: number | undefined;
@ -1264,7 +1259,7 @@ declare namespace sharp {
/** Buffer containing raw TIFFTAG_PHOTOSHOP data, if present */
tifftagPhotoshop?: Buffer | undefined;
/** The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC) */
compression?: HeifCompression | undefined;
compression?: 'av1' | 'hevc';
/** Default background colour, if present, for PNG (bKGD) and GIF images */
background?: { r: number; g: number; b: number } | { gray: number };
/** Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide */
@ -1272,7 +1267,7 @@ declare namespace sharp {
/** Number of Sub Image File Directories in an OME-TIFF image */
subifds?: number | undefined;
/** The unit of resolution (density) */
resolutionUnit?: Unit | undefined;
resolutionUnit?: 'inch' | 'cm' | undefined;
/** String containing format for images loaded via *magick */
formatMagick?: string | undefined;
/** Array of keyword/text pairs representing PNG text blocks, if present. */
@ -1428,7 +1423,7 @@ declare namespace sharp {
/** quality, integer 1-100 (optional, default 50) */
quality?: number | undefined;
/** compression format: av1, hevc (optional, default 'av1') */
compression?: HeifCompression | undefined;
compression?: 'av1' | 'hevc' | undefined;
/** use lossless compression (optional, default false) */
lossless?: boolean | undefined;
/** Level of CPU effort to reduce file size, between 0 (fastest) and 9 (slowest) (optional, default 4) */
@ -1465,8 +1460,6 @@ declare namespace sharp {
quality?: number | undefined;
/** Compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k (optional, default 'jpeg') */
compression?: string | undefined;
/** Use BigTIFF variant (has no effect when compression is none) (optional, default false) */
bigtiff?: boolean | undefined;
/** Compression predictor options: none, horizontal, float (optional, default 'horizontal') */
predictor?: string | undefined;
/** Write an image pyramid (optional, default false) */
@ -1486,7 +1479,7 @@ declare namespace sharp {
/** Write 1-bit images as miniswhite (optional, default false) */
miniswhite?: boolean | undefined;
/** Resolution unit options: inch, cm (optional, default 'inch') */
resolutionUnit?: Unit | undefined;
resolutionUnit?: 'inch' | 'cm' | undefined;
}
interface PngOptions extends OutputOptions {
@ -1611,7 +1604,7 @@ declare namespace sharp {
}
interface RawOptions {
depth?: keyof DepthEnum;
depth?: 'char' | 'uchar' | 'short' | 'ushort' | 'int' | 'uint' | 'float' | 'complex' | 'double' | 'dpcomplex';
}
/** 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');
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 sharp = require('./sharp');
@ -54,7 +54,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = {
autoOrient: false,
failOn: 'warning',
limitInputPixels: 0x3FFF ** 2,
limitInputPixels: Math.pow(0x3FFF, 2),
ignoreIcc: false,
unlimited: false,
sequentialRead: true
@ -150,7 +150,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
if (is.defined(inputOptions.limitInputPixels)) {
if (is.bool(inputOptions.limitInputPixels)) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels
? 0x3FFF ** 2
? Math.pow(0x3FFF, 2)
: 0;
} else if (is.integer(inputOptions.limitInputPixels) && is.inRange(inputOptions.limitInputPixels, 0, Number.MAX_SAFE_INTEGER)) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
@ -513,7 +513,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
}
}
} else if (is.defined(inputOptions)) {
throw new Error(`Invalid input options ${inputOptions}`);
throw new Error('Invalid input options ' + inputOptions);
}
return inputDescriptor;
}
@ -525,8 +525,10 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
* @param {string} encoding - unused
* @param {Function} callback
*/
function _write (chunk, _encoding, callback) {
function _write (chunk, encoding, callback) {
/* istanbul ignore else */
if (Array.isArray(this.options.input.buffer)) {
/* istanbul ignore else */
if (is.buffer(chunk)) {
if (this.options.input.buffer.length === 0) {
this.on('finish', () => {
@ -570,7 +572,7 @@ function _isStreamInput () {
* such as resize or rotate.
*
* Dimensions in the response will respect the `page` and `pages` properties of the
* {@link /api-constructor/ constructor parameters}.
* {@link /api-constructor#parameters|constructor parameters}.
*
* A `Promise` is returned when `callback` is not provided.
*
@ -578,9 +580,9 @@ function _isStreamInput () {
* - `size`: Total size of image in bytes, for Stream and Buffer input only
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/VipsImage.html#VipsInterpretation)
* - `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/enum.BandFormat.html)
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/VipsImage.html#VipsBandFormat)
* - `density`: Number of pixels per inch (DPI), if present
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
@ -792,7 +794,7 @@ function stats (callback) {
* @module Sharp
* @private
*/
module.exports = (Sharp) => {
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
// Private
_inputOptionsFromObject,

View File

@ -1,49 +1,61 @@
/*!
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?
* @private
*/
const defined = (val) => typeof val !== 'undefined' && val !== null;
const defined = function (val) {
return typeof val !== 'undefined' && val !== null;
};
/**
* Is this value an object?
* @private
*/
const object = (val) => typeof val === 'object';
const object = function (val) {
return typeof val === 'object';
};
/**
* Is this value a plain object?
* @private
*/
const plainObject = (val) => Object.prototype.toString.call(val) === '[object Object]';
const plainObject = function (val) {
return Object.prototype.toString.call(val) === '[object Object]';
};
/**
* Is this value a function?
* @private
*/
const fn = (val) => typeof val === 'function';
const fn = function (val) {
return typeof val === 'function';
};
/**
* Is this value a boolean?
* @private
*/
const bool = (val) => typeof val === 'boolean';
const bool = function (val) {
return typeof val === 'boolean';
};
/**
* Is this value a Buffer object?
* @private
*/
const buffer = (val) => val instanceof Buffer;
const buffer = function (val) {
return val instanceof Buffer;
};
/**
* Is this value a typed array object?. E.g. Uint8Array or Uint8ClampedArray?
* @private
*/
const typedArray = (val) => {
const typedArray = function (val) {
if (defined(val)) {
switch (val.constructor) {
case Uint8Array:
@ -66,37 +78,49 @@ const typedArray = (val) => {
* Is this value an ArrayBuffer object?
* @private
*/
const arrayBuffer = (val) => val instanceof ArrayBuffer;
const arrayBuffer = function (val) {
return val instanceof ArrayBuffer;
};
/**
* Is this value a non-empty string?
* @private
*/
const string = (val) => typeof val === 'string' && val.length > 0;
const string = function (val) {
return typeof val === 'string' && val.length > 0;
};
/**
* Is this value a real number?
* @private
*/
const number = (val) => typeof val === 'number' && !Number.isNaN(val);
const number = function (val) {
return typeof val === 'number' && !Number.isNaN(val);
};
/**
* Is this value an integer?
* @private
*/
const integer = (val) => Number.isInteger(val);
const integer = function (val) {
return Number.isInteger(val);
};
/**
* Is this value within an inclusive given range?
* @private
*/
const inRange = (val, min, max) => val >= min && val <= max;
const inRange = function (val, min, max) {
return val >= min && val <= max;
};
/**
* Is this value within the elements of an array?
* @private
*/
const inArray = (val, list) => list.includes(val);
const inArray = function (val, list) {
return list.includes(val);
};
/**
* Create an Error with a message relating to an invalid parameter.
@ -107,9 +131,11 @@ const inArray = (val, list) => list.includes(val);
* @returns {Error} Containing the formatted message.
* @private
*/
const invalidParameterError = (name, expected, actual) => new Error(
const invalidParameterError = function (name, expected, actual) {
return new Error(
`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`
);
};
/**
* Ensures an Error from C++ contains a JS stack.
@ -119,7 +145,7 @@ const invalidParameterError = (name, expected, actual) => new Error(
* @returns {Error} Error with message and stack.
* @private
*/
const nativeError = (native, context) => {
const nativeError = function (native, context) {
context.message = native.message;
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 { createHash } = require('node:crypto');
@ -12,13 +12,13 @@ const detectLibc = require('detect-libc');
const { config, engines, optionalDependencies } = require('../package.json');
/* node:coverage ignore next */
const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || config.libvips;
const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || /* istanbul ignore next */
config.libvips;
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
const prebuiltPlatforms = [
'darwin-arm64', 'darwin-x64',
'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-riscv64', 'linux-s390x', 'linux-x64',
'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-s390x', 'linux-x64',
'linuxmusl-arm64', 'linuxmusl-x64',
'win32-arm64', 'win32-ia32', 'win32-x64'
];
@ -36,16 +36,17 @@ const log = (item) => {
}
};
/* node:coverage ignore next */
/* istanbul ignore next */
const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
/* istanbul ignore next */
const buildPlatformArch = () => {
/* node:coverage ignore next 3 */
if (isEmscripten()) {
return 'wasm32';
}
/* eslint camelcase: ["error", { allow: ["^npm_config_"] }] */
const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
const libc = typeof npm_config_libc === 'string' ? npm_config_libc : runtimeLibc();
return `${npm_config_platform || process.platform}${libc}-${npm_config_arch || process.arch}`;
@ -55,19 +56,19 @@ const buildSharpLibvipsIncludeDir = () => {
try {
return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/include`);
} catch {
/* node:coverage ignore next 5 */
try {
return require('@img/sharp-libvips-dev/include');
} catch {}
}
/* istanbul ignore next */
return '';
};
const buildSharpLibvipsCPlusPlusDir = () => {
/* node:coverage ignore next 4 */
try {
return require('@img/sharp-libvips-dev/cplusplus');
} catch {}
/* istanbul ignore next */
return '';
};
@ -75,17 +76,16 @@ const buildSharpLibvipsLibDir = () => {
try {
return require(`@img/sharp-libvips-dev-${buildPlatformArch()}/lib`);
} catch {
/* node:coverage ignore next 5 */
try {
return require(`@img/sharp-libvips-${buildPlatformArch()}/lib`);
} catch {}
}
/* istanbul ignore next */
return '';
};
/* node:coverage disable */
const isUnsupportedNodeRuntime = () => {
/* istanbul ignore next */
if (process.release?.name === 'node' && process.versions) {
if (!semverSatisfies(process.versions.node, engines.node)) {
return { found: process.versions.node, expected: engines.node };
@ -93,12 +93,14 @@ const isUnsupportedNodeRuntime = () => {
}
};
/* istanbul ignore next */
const isEmscripten = () => {
const { CC } = process.env;
return Boolean(CC?.endsWith('/emcc'));
return Boolean(CC && CC.endsWith('/emcc'));
};
const isRosetta = () => {
/* istanbul ignore next */
if (process.platform === 'darwin' && process.arch === 'x64') {
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
return (translated || '').trim() === 'sysctl.proc_translated: 1';
@ -106,8 +108,6 @@ const isRosetta = () => {
return false;
};
/* node:coverage enable */
const sha512 = (s) => createHash('sha512').update(s).digest('hex');
const yarnLocator = () => {
@ -121,8 +121,7 @@ const yarnLocator = () => {
return '';
};
/* node:coverage disable */
/* istanbul ignore next */
const spawnRebuild = () =>
spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, {
...spawnSyncOptions,
@ -138,17 +137,16 @@ const globalLibvipsVersion = () => {
PKG_CONFIG_PATH: pkgConfigPath()
}
}).stdout;
/* istanbul ignore next */
return (globalLibvipsVersion || '').trim();
} else {
return '';
}
};
/* node:coverage enable */
/* istanbul ignore next */
const pkgConfigPath = () => {
if (process.platform !== 'win32') {
/* node:coverage ignore next 4 */
const brewPkgConfigPath = spawnSync(
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
spawnSyncOptions
@ -180,13 +178,13 @@ const useGlobalLibvips = (logger) => {
if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS', logger);
}
/* node:coverage ignore next 3 */
/* istanbul ignore next */
if (isRosetta()) {
return skipSearch(false, 'Rosetta', logger);
}
const globalVipsVersion = globalLibvipsVersion();
/* node:coverage ignore next */
return !!globalVipsVersion && semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
return !!globalVipsVersion && /* istanbul ignore next */
semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
};
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');
@ -239,7 +239,7 @@ function affine (matrix, options) {
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
* Fine-grained control over the level of sharpening in "flat" (m1) and "jagged" (m2) areas is available.
*
* See {@link https://www.libvips.org/API/current/method.Image.sharpen.html libvips sharpen} operation.
* See {@link https://www.libvips.org/API/current/libvips-convolution.html#vips-sharpen|libvips sharpen} operation.
*
* @example
* 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.
*
* See also {@link /api-channel#removealpha removeAlpha}.
* See also {@link /api-channel#removealpha|removeAlpha}.
*
* @example
* await sharp(rgbaInput)
@ -660,7 +660,7 @@ function normalize (options) {
/**
* Perform contrast limiting adaptive histogram equalization
* {@link https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE CLAHE}.
* {@link https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE|CLAHE}.
*
* This will, in general, enhance the clarity of the image by bringing out darker details.
*
@ -742,7 +742,9 @@ function convolve (kernel) {
}
// Default scale is sum of kernel values
if (!is.integer(kernel.scale)) {
kernel.scale = kernel.kernel.reduce((a, b) => a + b, 0);
kernel.scale = kernel.kernel.reduce(function (a, b) {
return a + b;
}, 0);
}
// Clip scale to a minimum value of 1
if (kernel.scale < 1) {
@ -987,7 +989,7 @@ function modulate (options) {
* @module Sharp
* @private
*/
module.exports = (Sharp) => {
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
autoOrient,
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 is = require('./is');
@ -43,7 +43,7 @@ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math
* Note that raw pixel data is only supported for buffer output.
*
* By default all metadata will be removed, which includes EXIF-based orientation.
* See {@link #withmetadata withMetadata} for control over this.
* See {@link #withmetadata|withMetadata} for control over this.
*
* The caller is responsible for ensuring directory structures and permissions exist.
*
@ -97,12 +97,12 @@ function toFile (fileOut, callback) {
* Write output to a Buffer.
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
*
* Use {@link #toformat toFormat} or one of the format-specific functions such as {@link #jpeg jpeg}, {@link #png png} etc. to set the output format.
* Use {@link #toformat|toFormat} or one of the format-specific functions such as {@link jpeg}, {@link png} etc. to set the output format.
*
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
*
* By default all metadata will be removed, which includes EXIF-based orientation.
* See {@link #withmetadata withMetadata} for control over this.
* See {@link #withmetadata|withMetadata} for control over this.
*
* `callback`, if present, gets three arguments `(err, data, info)` where:
* - `err` is an error, if any.
@ -256,7 +256,7 @@ function withExifMerge (exif) {
/**
* Keep ICC profile from the input image in the output image.
*
* When input and output colour spaces differ, use with {@link /api-colour/#tocolourspace toColourspace} and optionally {@link /api-colour/#pipelinecolourspace pipelineColourspace}.
* Where necessary, will attempt to convert the output colour space to match the profile.
*
* @since 0.33.0
*
@ -265,13 +265,6 @@ function withExifMerge (exif) {
* .keepIccProfile()
* .toBuffer();
*
* @example
* const cmykOutputWithIccProfile = await sharp(cmykInputWithIccProfile)
* .pipelineColourspace('cmyk')
* .toColourspace('cmyk')
* .keepIccProfile()
* .toBuffer();
*
* @returns {Sharp}
*/
function keepIccProfile () {
@ -572,7 +565,7 @@ function jpeg (options) {
* Set `palette` to `true` for slower, indexed PNG output.
*
* For 16 bits per pixel output, convert to `rgb16` via
* {@link /api-colour/#tocolourspace toColourspace}.
* {@link /api-colour#tocolourspace|toColourspace}.
*
* @example
* // Convert any input to full colour PNG output
@ -852,12 +845,13 @@ function gif (options) {
return this._updateFormatOut('gif', options);
}
/* istanbul ignore next */
/**
* Use these JP2 options for output image.
*
* Requires libvips compiled with support for OpenJPEG.
* The prebuilt binaries do not include this - see
* {@link /install#custom-libvips installing a custom libvips}.
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
*
* @example
* // Convert any input to lossless JP2 output
@ -886,7 +880,6 @@ function gif (options) {
* @throws {Error} Invalid options
*/
function jp2 (options) {
/* node:coverage ignore next 41 */
if (!this.constructor.format.jp2k.output.buffer) {
throw errJp2Save();
}
@ -966,7 +959,7 @@ function trySetAnimationOptions (source, target) {
/**
* Use these TIFF options for output image.
*
* The `density` can be set in pixels/inch via {@link #withmetadata withMetadata}
* The `density` can be set in pixels/inch via {@link #withmetadata|withMetadata}
* instead of providing `xres` and `yres` in pixels/mm.
*
* @example
@ -983,7 +976,6 @@ function trySetAnimationOptions (source, target) {
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
* @param {string} [options.compression='jpeg'] - compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k
* @param {boolean} [options.bigtiff=false] - use BigTIFF variant (has no effect when compression is none)
* @param {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
* @param {boolean} [options.pyramid=false] - write an image pyramid
* @param {boolean} [options.tile=false] - write a tiled tiff
@ -1062,10 +1054,6 @@ function tiff (options) {
throw is.invalidParameterError('compression', 'one of: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k', options.compression);
}
}
// bigtiff
if (is.defined(options.bigtiff)) {
this._setBooleanOption('tiffBigtiff', options.bigtiff);
}
// predictor
if (is.defined(options.predictor)) {
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
@ -1201,7 +1189,7 @@ function heif (options) {
*
* Requires libvips compiled with support for libjxl.
* The prebuilt binaries do not include this - see
* {@link /install/#custom-libvips installing a custom libvips}.
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
*
* @since 0.31.3
*
@ -1514,6 +1502,7 @@ function _setBooleanOption (key, val) {
* @private
*/
function _read () {
/* istanbul ignore else */
if (!this.options.streamOut) {
this.options.streamOut = true;
const stack = Error();
@ -1630,7 +1619,7 @@ function _pipeline (callback, stack) {
* @module Sharp
* @private
*/
module.exports = (Sharp) => {
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
// Public
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');
@ -579,7 +579,7 @@ function trim (options) {
* @module Sharp
* @private
*/
module.exports = (Sharp) => {
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
resize,
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
@ -17,8 +17,6 @@ const paths = [
'@img/sharp-wasm32/sharp.node'
];
/* node:coverage disable */
let path, sharp;
const errors = [];
for (path of paths) {
@ -26,10 +24,12 @@ for (path of paths) {
sharp = require(path);
break;
} catch (err) {
/* istanbul ignore next */
errors.push(err);
}
}
/* istanbul ignore next */
if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2()) {
const err = new Error('Prebuilt binaries for linux-x64 require v2 microarchitecture');
err.code = 'Unsupported CPU';
@ -37,6 +37,7 @@ if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2())
sharp = null;
}
/* istanbul ignore next */
if (sharp) {
module.exports = sharp;
} else {
@ -87,7 +88,7 @@ if (sharp) {
` Found ${libcFound}`,
` Requires ${libcRequires}`
);
} catch (_errEngines) {}
} catch (errEngines) {}
}
if (isLinux && /\/snap\/core[0-9]{2}/.test(messages)) {
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 detectLibc = require('detect-libc');
@ -57,7 +57,7 @@ const interpolators = {
let versions = {
vips: libvipsVersion.semver
};
/* node:coverage ignore next 15 */
/* istanbul ignore next */
if (!libvipsVersion.isGlobal) {
if (!libvipsVersion.isWasm) {
try {
@ -75,7 +75,7 @@ if (!libvipsVersion.isGlobal) {
}
versions.sharp = require('../package.json').version;
/* node:coverage ignore next 5 */
/* istanbul ignore next */
if (versions.heif && format.heif) {
// Prebuilt binaries provide AV1
format.heif.input.fileSuffix = ['.avif'];
@ -136,7 +136,7 @@ cache(true);
* and these are independent of the value set here.
*
* :::note
* Further {@link /performance/ control over performance} is available.
* Further {@link /performance|control over performance} is available.
* :::
*
* @example
@ -150,7 +150,7 @@ cache(true);
function concurrency (concurrency) {
return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
}
/* node:coverage ignore next 7 */
/* istanbul ignore next */
if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
// Reduce default concurrency to 1 when using glibc memory allocator
sharp.concurrency(1);
@ -277,7 +277,7 @@ function unblock (options) {
* @module Sharp
* @private
*/
module.exports = (Sharp) => {
module.exports = function (Sharp) {
Sharp.cache = cache;
Sharp.concurrency = concurrency;
Sharp.counters = counters;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,22 +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 <algorithm>
#include <cstdlib>
#include <map>
#include <mutex>
#include <queue>
#include <string>
#include <tuple>
#include <utility>
#include <string.h>
#include <vector>
#include <queue>
#include <map>
#include <mutex> // NOLINT(build/c++11)
#include <napi.h>
#include <vips/vips8>
#include "./common.h"
#include "common.h"
using vips::VImage;
@ -425,14 +421,14 @@ namespace sharp {
->set("high_bitdepth", descriptor->svgHighBitdepth);
break;
case ImageType::TIFF:
option->set("subifd", descriptor->tiffSubifd);
option->set("tiffSubifd", descriptor->tiffSubifd);
break;
case ImageType::PDF:
option->set("dpi", descriptor->density)
->set("background", descriptor->pdfBackground);
break;
case ImageType::OPENSLIDE:
option->set("level", descriptor->openSlideLevel);
option->set("openSlideLevel", descriptor->openSlideLevel);
break;
case ImageType::JP2:
option->set("oneshot", descriptor->jp2Oneshot);

View File

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

View File

@ -1,7 +1,5 @@
/*!
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 */

View File

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

View File

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

View File

@ -1,7 +1,5 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#include <algorithm>
#include <functional>
@ -10,8 +8,8 @@
#include <vector>
#include <vips/vips8>
#include "./common.h"
#include "./operations.h"
#include "common.h"
#include "operations.h"
using vips::VImage;
using vips::VError;

View File

@ -1,7 +1,5 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#ifndef SRC_OPERATIONS_H_
#define SRC_OPERATIONS_H_
@ -10,7 +8,6 @@
#include <functional>
#include <memory>
#include <tuple>
#include <vector>
#include <vips/vips8>
using vips::VImage;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,5 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
#ifndef SRC_UTILITIES_H_
#define SRC_UTILITIES_H_

24
test/beforeEach.js Normal file
View File

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

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

Binary file not shown.

View File

@ -1,14 +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
const path = require('node:path');
'use strict';
const path = require('path');
const sharp = require('../../');
const maxColourDistance = require('../../lib/sharp')._maxColourDistance;
// Helpers
const getPath = (filename) => path.join(__dirname, filename);
const getPath = function (filename) {
return path.join(__dirname, filename);
};
// Generates a 64-bit-as-binary-string image fingerprint
// Based on the dHash gradient method - see http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
@ -20,7 +22,7 @@ async function fingerprint (image) {
.resize(9, 8, { fit: sharp.fit.fill })
.raw()
.toBuffer()
.then((data) => {
.then(function (data) {
let fingerprint = '';
for (let col = 0; col < 8; col++) {
for (let row = 0; row < 8; row++) {
@ -113,7 +115,6 @@ module.exports = {
inputTiff8BitDepth: getPath('8bit_depth.tiff'),
inputTifftagPhotoshop: getPath('tifftag-photoshop.tiff'), // https://github.com/lovell/sharp/issues/1600
inputTiffFogra: getPath('fogra-0-100-100-0.tif'), // https://github.com/lovell/sharp/issues/4045
inputTiffGeo: getPath('bonne.geo.tif'), // https://download.osgeo.org/geotiff/samples/intergraph
inputJp2: getPath('relax.jp2'), // https://www.fnordware.com/j2k/relax.jp2
inputJp2TileParts: getPath('relax_tileparts.jp2'), // kdu_expand -i relax.jp2 -o relax-tmp.tif ; kdu_compress -i relax-tmp.tif -o relax_tileparts.jp2 -jp2_space sRGB Clayers=8 -rate 1.0,0.04 Stiles='{128,128}' ORGtparts=L ; rm relax-tmp.tif
@ -146,12 +147,14 @@ module.exports = {
path: getPath,
// Path for expected output images
expected: (filename) => getPath(path.join('expected', filename)),
expected: function (filename) {
return getPath(path.join('expected', filename));
},
// Verify similarity of expected vs actual images via fingerprint
// Specify distance threshold using `options={threshold: 42}`, default
// `threshold` is 5;
assertSimilar: async (expectedImage, actualImage, options, callback) => {
assertSimilar: async function (expectedImage, actualImage, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
@ -191,12 +194,12 @@ module.exports = {
}
},
assertMaxColourDistance: (actualImagePath, expectedImagePath, acceptedDistance) => {
assertMaxColourDistance: function (actualImagePath, expectedImagePath, acceptedDistance) {
if (typeof actualImagePath !== 'string') {
throw new TypeError(`\`actualImagePath\` must be a string; got ${actualImagePath}`);
throw new TypeError('`actualImagePath` must be a string; got ' + actualImagePath);
}
if (typeof expectedImagePath !== 'string') {
throw new TypeError(`\`expectedImagePath\` must be a string; got ${expectedImagePath}`);
throw new TypeError('`expectedImagePath` must be a string; got ' + expectedImagePath);
}
if (typeof acceptedDistance !== 'number') {
// Default threshold
@ -204,7 +207,7 @@ module.exports = {
}
const distance = maxColourDistance(actualImagePath, expectedImagePath);
if (distance > acceptedDistance) {
throw new Error(`Expected maximum absolute distance of ${acceptedDistance}, actual ${distance}`);
throw new Error('Expected maximum absolute distance of ' + acceptedDistance + ', actual ' + distance);
}
}

View File

@ -18,5 +18,5 @@ for test in $TESTS; do
--show-leak-kinds=definite,indirect \
--num-callers=20 \
--trace-children=yes \
node --zero-fill-buffers --test "test/unit/$test";
node --expose-gc --zero-fill-buffers node_modules/.bin/mocha --no-config --slow=60000 --timeout=120000 --require test/beforeEach.js "test/unit/$test";
done

View File

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

View File

@ -1,16 +0,0 @@
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,10 +1,9 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
const { describe, it } = require('node:test');
const assert = require('node:assert');
'use strict';
const assert = require('assert');
const sharp = require('../../');
const fixtures = require('../fixtures');

View File

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

View File

@ -1,10 +1,9 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
const { describe, it } = require('node:test');
const assert = require('node:assert');
'use strict';
const assert = require('assert');
const sharp = require('../../');
const { inputAvif, inputJpg, inputGifAnimated } = require('../fixtures');
@ -21,8 +20,8 @@ describe('AVIF', () => {
.resize(32)
.jpeg()
.toBuffer();
const { size, ...metadata } = await sharp(data).metadata();
void size;
const { size, ...metadata } = await sharp(data)
.metadata();
assert.deepStrictEqual(metadata, {
autoOrient: {
height: 13,
@ -50,8 +49,8 @@ describe('AVIF', () => {
.resize(32)
.avif({ effort: 0 })
.toBuffer();
const { size, ...metadata } = await sharp(data).metadata();
void size;
const { size, ...metadata } = await sharp(data)
.metadata();
assert.deepStrictEqual(metadata, {
autoOrient: {
height: 26,
@ -78,8 +77,8 @@ describe('AVIF', () => {
const data = await sharp(inputAvif)
.resize(32)
.toBuffer();
const { size, ...metadata } = await sharp(data).metadata();
void size;
const { size, ...metadata } = await sharp(data)
.metadata();
assert.deepStrictEqual(metadata, {
autoOrient: {
height: 13,
@ -107,8 +106,8 @@ describe('AVIF', () => {
.resize(10)
.avif({ effort: 0 })
.toBuffer();
const { size, ...metadata } = await sharp(data).metadata();
void size;
const { size, ...metadata } = await sharp(data)
.metadata();
assert.deepStrictEqual(metadata, {
autoOrient: {
height: 300,
@ -137,8 +136,8 @@ describe('AVIF', () => {
.sharpen()
.avif({ effort: 0 })
.toBuffer();
const { size, ...metadata } = await sharp(data).metadata();
void size;
const { size, ...metadata } = await sharp(data)
.metadata();
assert.deepStrictEqual(metadata, {
autoOrient: {
height: 26,
@ -175,10 +174,9 @@ describe('AVIF', () => {
)
);
it('Invalid bitdepth value throws error', () =>
assert.throws(
it('Invalid bitdepth value throws error', async () => {
assert.rejects(
() => sharp().avif({ bitdepth: 11 }),
/Expected 8, 10 or 12 for bitdepth but received 11 of type number/
)
);
/Error: Expected 8, 10 or 12 for bitdepth but received 11 of type number/);
});
});

View File

@ -1,52 +1,51 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
const { describe, it } = require('node:test');
const assert = require('node:assert');
'use strict';
const assert = require('assert');
const fixtures = require('../fixtures');
const sharp = require('../../');
describe('Bandbool per-channel boolean operations', () => {
describe('Bandbool per-channel boolean operations', function () {
[
sharp.bool.and,
sharp.bool.or,
sharp.bool.eor
]
.forEach((op) => {
it(`${op} operation`, (_t, done) => {
.forEach(function (op) {
it(op + ' operation', function (done) {
sharp(fixtures.inputPngBooleanNoAlpha)
.bandbool(op)
.toColourspace('b-w')
.toBuffer((err, data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(200, info.width);
assert.strictEqual(200, info.height);
assert.strictEqual(1, info.channels);
fixtures.assertSimilar(fixtures.expected(`bandbool_${op}_result.png`), data, done);
fixtures.assertSimilar(fixtures.expected('bandbool_' + op + '_result.png'), data, done);
});
});
});
it('sRGB image retains 3 channels', (_t, done) => {
it('sRGB image retains 3 channels', function (done) {
sharp(fixtures.inputJpg)
.bandbool('and')
.toBuffer((err, _data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(3, info.channels);
done();
});
});
it('Invalid operation', () => {
assert.throws(() => {
it('Invalid operation', function () {
assert.throws(function () {
sharp().bandbool('fail');
});
});
it('Missing operation', () => {
assert.throws(() => {
it('Missing operation', function () {
assert.throws(function () {
sharp().bandbool();
});
});

View File

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

View File

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

View File

@ -1,140 +1,139 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
const { describe, it } = require('node:test');
const assert = require('node:assert');
'use strict';
const assert = require('assert');
const sharp = require('../../lib');
const fixtures = require('../fixtures');
describe('Clahe', () => {
it('width 5 width 5 maxSlope 0', (_t, done) => {
describe('Clahe', function () {
it('width 5 width 5 maxSlope 0', function (done) {
sharp(fixtures.inputJpgClahe)
.clahe({ width: 5, height: 5, maxSlope: 0 })
.toBuffer((err, data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-5-5-0.jpg'), data, { threshold: 10 }, done);
});
});
it('width 5 width 5 maxSlope 5', (_t, done) => {
it('width 5 width 5 maxSlope 5', function (done) {
sharp(fixtures.inputJpgClahe)
.clahe({ width: 5, height: 5, maxSlope: 5 })
.toBuffer((err, data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-5-5-5.jpg'), data, done);
});
});
it('width 11 width 25 maxSlope 14', (_t, done) => {
it('width 11 width 25 maxSlope 14', function (done) {
sharp(fixtures.inputJpgClahe)
.clahe({ width: 11, height: 25, maxSlope: 14 })
.toBuffer((err, data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-11-25-14.jpg'), data, done);
});
});
it('width 50 width 50 maxSlope 0', (_t, done) => {
it('width 50 width 50 maxSlope 0', function (done) {
sharp(fixtures.inputJpgClahe)
.clahe({ width: 50, height: 50, maxSlope: 0 })
.toBuffer((err, data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-50-50-0.jpg'), data, done);
});
});
it('width 50 width 50 maxSlope 14', (_t, done) => {
it('width 50 width 50 maxSlope 14', function (done) {
sharp(fixtures.inputJpgClahe)
.clahe({ width: 50, height: 50, maxSlope: 14 })
.toBuffer((err, data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-50-50-14.jpg'), data, done);
});
});
it('width 100 width 50 maxSlope 3', (_t, done) => {
it('width 100 width 50 maxSlope 3', function (done) {
sharp(fixtures.inputJpgClahe)
.clahe({ width: 100, height: 50, maxSlope: 3 })
.toBuffer((err, data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done);
});
});
it('width 100 width 100 maxSlope 0', (_t, done) => {
it('width 100 width 100 maxSlope 0', function (done) {
sharp(fixtures.inputJpgClahe)
.clahe({ width: 100, height: 100, maxSlope: 0 })
.toBuffer((err, data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-100-100-0.jpg'), data, done);
});
});
it('invalid maxSlope', () => {
assert.throws(() => {
it('invalid maxSlope', function () {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: -5 });
});
assert.throws(() => {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 110 });
});
assert.throws(() => {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 5.5 });
});
assert.throws(() => {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 'a string' });
});
});
it('invalid width', () => {
assert.throws(() => {
it('invalid width', function () {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 100.5, height: 100 });
});
assert.throws(() => {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: -5, height: 100 });
});
assert.throws(() => {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: true, height: 100 });
});
assert.throws(() => {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 'string test', height: 100 });
});
});
it('invalid height', () => {
assert.throws(() => {
it('invalid height', function () {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100.5 });
});
assert.throws(() => {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: -5 });
});
assert.throws(() => {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: true });
});
assert.throws(() => {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 'string test' });
});
});
it('invalid options object', () => {
assert.throws(() => {
it('invalid options object', function () {
assert.throws(function () {
sharp(fixtures.inputJpgClahe).clahe(100, 100, 5);
});
});
it('uses default maxSlope of 3', (_t, done) => {
it('uses default maxSlope of 3', function (done) {
sharp(fixtures.inputJpgClahe)
.clahe({ width: 100, height: 50 })
.toBuffer((err, data, info) => {
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done);

View File

@ -1,30 +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
const fs = require('node:fs');
const { afterEach, beforeEach, describe, it } = require('node:test');
const assert = require('node:assert');
'use strict';
const fs = require('fs');
const assert = require('assert');
const sharp = require('../../');
const fixtures = require('../fixtures');
describe('Clone', () => {
beforeEach(() => {
describe('Clone', function () {
beforeEach(function () {
sharp.cache(false);
});
afterEach(() => {
afterEach(function () {
sharp.cache(true);
});
it('Read from Stream and write to multiple Streams', (_t, done) => {
it('Read from Stream and write to multiple Streams', function (done) {
let finishEventsExpected = 2;
// Output stream 1
const output1 = fixtures.path('output.multi-stream.1.jpg');
const writable1 = fs.createWriteStream(output1);
writable1.on('finish', () => {
sharp(output1).toBuffer((err, data, info) => {
writable1.on('finish', function () {
sharp(output1).toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
@ -41,8 +40,8 @@ describe('Clone', () => {
// Output stream 2
const output2 = fixtures.path('output.multi-stream.2.jpg');
const writable2 = fs.createWriteStream(output2);
writable2.on('finish', () => {
sharp(output2).toBuffer((err, data, info) => {
writable2.on('finish', function () {
sharp(output2).toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
@ -65,14 +64,14 @@ describe('Clone', () => {
fs.createReadStream(fixtures.inputJpg).pipe(rotator);
});
it('Stream-based input attaches finish event listener to original', () => {
it('Stream-based input attaches finish event listener to original', function () {
const original = sharp();
const clone = original.clone();
assert.strictEqual(1, original.listenerCount('finish'));
assert.strictEqual(0, clone.listenerCount('finish'));
});
it('Non Stream-based input does not attach finish event listeners', () => {
it('Non Stream-based input does not attach finish event listeners', function () {
const original = sharp(fixtures.inputJpg);
const clone = original.clone();
assert.strictEqual(0, original.listenerCount('finish'));

View File

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

View File

@ -1,10 +1,9 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
const { describe, it } = require('node:test');
const assert = require('node:assert');
'use strict';
const assert = require('assert');
const fixtures = require('../fixtures');
const sharp = require('../../');
@ -323,7 +322,7 @@ describe('composite', () => {
describe('string gravity', () => {
Object.keys(sharp.gravity).forEach(gravity => {
it(gravity, done => {
const expected = fixtures.expected(`overlay-gravity-${gravity}.jpg`);
const expected = fixtures.expected('overlay-gravity-' + gravity + '.jpg');
sharp(fixtures.inputJpg)
.resize(80)
.composite([{
@ -345,7 +344,7 @@ describe('composite', () => {
describe('tile and gravity', () => {
Object.keys(sharp.gravity).forEach(gravity => {
it(gravity, done => {
const expected = fixtures.expected(`overlay-tile-gravity-${gravity}.jpg`);
const expected = fixtures.expected('overlay-tile-gravity-' + gravity + '.jpg');
sharp(fixtures.inputJpg)
.resize(80)
.composite([{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,29 +1,28 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
const { describe, it } = require('node:test');
const assert = require('node:assert');
'use strict';
const assert = require('assert');
const fixtures = require('../fixtures');
describe('Test fixtures', () => {
describe('assertMaxColourDistance', () => {
it('should throw an Error when images have a different number of channels', () => {
assert.throws(() => {
describe('Test fixtures', function () {
describe('assertMaxColourDistance', function () {
it('should throw an Error when images have a different number of channels', function () {
assert.throws(function () {
fixtures.assertMaxColourDistance(fixtures.inputPngOverlayLayer1, fixtures.inputJpg);
});
});
it('should throw an Error when images have different dimensions', () => {
assert.throws(() => {
it('should throw an Error when images have different dimensions', function () {
assert.throws(function () {
fixtures.assertMaxColourDistance(fixtures.inputJpg, fixtures.inputJpgWithExif);
});
});
it('should accept a zero threshold when comparing an image to itself', () => {
it('should accept a zero threshold when comparing an image to itself', function () {
const image = fixtures.inputPngOverlayLayer0;
fixtures.assertMaxColourDistance(image, image, 0);
});
it('should accept a numeric threshold for two different images', () => {
it('should accept a numeric threshold for two different images', function () {
fixtures.assertMaxColourDistance(fixtures.inputPngOverlayLayer0, fixtures.inputPngOverlayLayer1, 100);
});
});

View File

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

View File

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

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