Compare commits

..

39 Commits

Author SHA1 Message Date
Lovell Fuller
8669fbc936 Prerelease v0.34.3-rc.0 2025-06-14 21:49:24 +01:00
Lovell Fuller
cab02463ec Remove prebuild dependency
Every CI build task tagged with 'package' now populates and
publishes the relevant npm/platform directory as an artefact.
These are aggregated by a fan-in release task at the end to
create a complete npm workspace zipfile. If the commit is
tagged, a release is created and the npm workspace attached.
2025-06-14 17:49:17 +01:00
Lovell Fuller
5374b036f3 CI: Switch ARM64 from CircleCI to GitHub Actions 2025-06-13 14:08:19 +01:00
Kleis Auke Wolthuizen
327a6d2083 Docs: update link to concurrency API (#4414) 2025-06-13 10:48:32 +01:00
Michael B. Klein
751f9992c4 Expose JPEG 2000 oneshot decoder option #4262
Requires libvips compiled with support for JP2 images

Co-authored-by: Kleis Auke Wolthuizen <github@kleisauke.nl>
2025-06-13 08:46:36 +01:00
Lovell Fuller
01f6cbbaee Upgrade to sharp-libvips v1.2.0-rc.2 2025-06-12 14:59:13 +01:00
Lovell Fuller
99be893dd4 Upgrade to libvips v8.17.0
CI: Use more recent, non-deprecated Windows runners

Bump devDeps
2025-06-12 11:27:26 +01:00
Kleis Auke Wolthuizen
4d1f7e051d Support composite op with non-sRGB pipeline colourspace (#4412) 2025-06-12 10:32:24 +01:00
Kleis Auke Wolthuizen
91f1b58f31 Tests: Regenerate expected fixtures ahead of libvips v8.17.0 (#4402) 2025-06-08 23:31:07 +01:00
Lovell Fuller
6d04b7c1fa Release v0.34.2 2025-05-20 14:36:42 +01:00
Harshal Bhakta
d4b30b7392 Docs: Update pnpm settings documentation URLs 2025-05-20 13:57:15 +01:00
Lovell Fuller
7f03502003 Docs: upgrade to latest Astro Starlight 2025-05-20 12:57:28 +01:00
Lovell Fuller
63b0a11b5b Tests: remove a possible race condition 2025-05-19 23:21:55 +01:00
Lovell Fuller
c4d6aec48c Docs: Highlight that Windows ARM64 support is experimental 2025-05-19 22:04:08 +01:00
Lovell Fuller
e75ae970ed Ensure PDF scale-on-load optimisation uses background #4398 2025-05-16 14:38:00 +01:00
Lovell Fuller
956d72ddc0 Prerelease v0.34.2-rc.0 2025-05-14 12:37:15 +01:00
Lovell Fuller
00e66efbee Bump deps 2025-05-14 12:37:08 +01:00
Kleis Auke Wolthuizen
db3a4528eb Simplify 94481a9 2025-05-13 19:22:36 +01:00
Kleis Auke Wolthuizen
d36fd5064d Prefer use of bandjoin_const() and list-initialization 2025-05-13 19:22:36 +01:00
Kleis Auke Wolthuizen
8e17c6f518 Prefer use of vips_interpretation_max_alpha()
This also ensures we handle scRGB correctly, see:
e9c5a31552
2025-05-13 19:22:36 +01:00
Lovell Fuller
94481a967e Ensure fit=contain resizing supports multiple alpha channels #4382 2025-05-13 14:31:51 +01:00
Lovell Fuller
32872ef840 TypeScript: ensure metadata response matches reality #4383 2025-05-13 14:26:25 +01:00
Lovell Fuller
7c7f960b60 Ensure support for wide-char filenames on Windows #4391 2025-05-13 08:53:37 +01:00
Kleis Auke Wolthuizen
0b5f131df8 Improve install error help text for ppc64le architecture (#4392) 2025-05-11 08:44:15 +01:00
Hans
e922ef7450 Add support for prebuilt Windows ARM64 binaries #4375 2025-05-10 14:29:25 +01:00
Lovell Fuller
73bec629cf Docs: website already hosts images, no need to use jsdelivr 2025-05-05 13:20:49 +01:00
Lovell Fuller
758d7e63cc Docs: changelog entry for #4387 2025-05-05 13:19:10 +01:00
Stephen Tse
eba3e9aeb2 TypeScript: Add smartDeblock definition to WebpOptions (#4387) 2025-05-05 13:15:50 +01:00
Lovell Fuller
701143afb3 Ensure animated GIF to WebP conversion retains loop #3394 2025-04-23 15:43:58 +01:00
Steven D'Onfro
38b6f44611 TypeScript: Add missing properties for animated images (#4369) 2025-04-12 11:38:24 +01:00
Lovell Fuller
5b5dfbad77 Ensure pdfBackground constructor property is used #4207
Slightly refactor the way background colours are set
2025-04-09 22:21:14 +01:00
Kleis Auke Wolthuizen
a642767329 Docs: update path to api-resize-fit.svg (#4368) 2025-04-08 10:15:29 +01:00
Lovell Fuller
5cae1abe8f Release v0.34.1 2025-04-07 20:51:11 +01:00
Lovell Fuller
66ffc48707 Changelog entry for #4362 2025-04-07 13:35:40 +01:00
Steven
3c7dbb8fba fix(types): update autoOrient type to include undefined (#4362)
This PR fixes the following error:

```
node_modules/.pnpm/sharp@0.34.0/node_modules/sharp/lib/index.d.ts(1590,15): error TS2430: Interface 'OverlayOptions' incorrectly extends interface 'SharpOptions'.
  Types of property 'autoOrient' are incompatible.
    Type 'boolean | undefined' is not assignable to type 'boolean'.
      Type 'undefined' is not assignable to type 'boolean'.
```
2025-04-04 21:53:46 +01:00
Lovell Fuller
a9e191328f Prevent glib assertions and checks #4351 2025-04-04 15:42:34 +01:00
Lovell Fuller
7323dbee98 Docs: update path to logo 2025-04-04 15:35:46 +01:00
Lovell Fuller
d7a771ca7a Docs: update path to logo 2025-04-04 15:33:34 +01:00
Lovell Fuller
7d0585fad1 Docs: remove outdated redirects/rewrites 2025-04-04 15:30:07 +01:00
61 changed files with 648 additions and 558 deletions

View File

@@ -1,105 +0,0 @@
version: 2.1
workflows:
build:
jobs:
- linux-arm64-glibc-node-18:
filters:
tags:
only: /^v.*/
- linux-arm64-musl-node-18:
filters:
tags:
only: /^v.*/
- linux-arm64-glibc-node-20:
filters:
tags:
only: /^v.*/
- linux-arm64-musl-node-20:
filters:
tags:
only: /^v.*/
jobs:
linux-arm64-glibc-node-18:
resource_class: arm.medium
machine:
image: ubuntu-2204:current
steps:
- checkout
- run: |
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl fonts-noto-core"
sudo docker exec sharp sh -c "mkdir -p /etc/apt/keyrings"
sudo docker exec sharp sh -c "curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg"
sudo docker exec sharp sh -c "echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main' | tee /etc/apt/sources.list.d/nodesource.list"
sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
- run: sudo docker exec sharp sh -c "npm install --build-from-source"
- run: sudo docker exec sharp sh -c "npm test"
- run: |
sudo docker exec sharp sh -c "npm run package-from-local-build"
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@img/sharp-linux-arm64=file:./npm/linux-arm64\""
sudo docker exec sharp sh -c "npm run clean"
sudo docker exec sharp sh -c "npm install --ignore-scripts"
sudo docker exec sharp sh -c "npm test"
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"cd src && ln -s ../package.json && npx prebuild --upload=$prebuild_upload\" || true"
linux-arm64-glibc-node-20:
resource_class: arm.medium
machine:
image: ubuntu-2204:current
steps:
- checkout
- run: |
sudo docker run -dit --name sharp --workdir /mnt/sharp arm64v8/debian:bullseye
sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python3 curl fonts-noto-core"
sudo docker exec sharp sh -c "mkdir -p /etc/apt/keyrings"
sudo docker exec sharp sh -c "curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg"
sudo docker exec sharp sh -c "echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' | tee /etc/apt/sources.list.d/nodesource.list"
sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
sudo docker cp . sharp:/mnt/sharp/.
- run: sudo docker exec sharp sh -c "npm install --build-from-source"
- run: sudo docker exec sharp sh -c "npm test"
- run: |
sudo docker exec sharp sh -c "npm run package-from-local-build"
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@img/sharp-linux-arm64=file:./npm/linux-arm64\""
sudo docker exec sharp sh -c "npm run clean"
sudo docker exec sharp sh -c "npm install --ignore-scripts"
sudo docker exec sharp sh -c "npm test"
linux-arm64-musl-node-18:
resource_class: arm.medium
machine:
image: ubuntu-2204:current
steps:
- checkout
- run: |
sudo docker run -dit --name sharp --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:18-alpine3.17
sudo docker exec sharp sh -c "apk add build-base git python3 font-noto --update-cache"
- run: sudo docker exec sharp sh -c "npm install --build-from-source"
- run: sudo docker exec sharp sh -c "npm test"
- run: |
sudo docker exec sharp sh -c "npm run package-from-local-build"
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@img/sharp-linuxmusl-arm64=file:./npm/linuxmusl-arm64\""
sudo docker exec sharp sh -c "npm run clean"
sudo docker exec sharp sh -c "npm install --ignore-scripts"
sudo docker exec sharp sh -c "npm test"
- run: "[[ -n $CIRCLE_TAG ]] && sudo docker exec --env prebuild_upload sharp sh -c \"cd src && ln -s ../package.json && npx prebuild --upload=$prebuild_upload\" || true"
linux-arm64-musl-node-20:
resource_class: arm.medium
machine:
image: ubuntu-2204:current
steps:
- checkout
- run: |
sudo docker run -dit --name sharp --workdir /mnt/sharp node:20-alpine3.18
sudo docker exec sharp sh -c "apk add build-base git python3 font-noto --update-cache"
sudo docker exec sharp sh -c "mkdir -p /mnt/sharp"
sudo docker cp . sharp:/mnt/sharp/.
- run: sudo docker exec sharp sh -c "npm install --build-from-source"
- run: sudo docker exec sharp sh -c "npm test"
- run: |
sudo docker exec sharp sh -c "npm run package-from-local-build"
sudo docker exec sharp sh -c "npm pkg set \"optionalDependencies.@img/sharp-linuxmusl-arm64=file:./npm/linuxmusl-arm64\""
sudo docker exec sharp sh -c "npm run clean"
sudo docker exec sharp sh -c "npm install --ignore-scripts"
sudo docker exec sharp sh -c "npm test"

View File

@@ -1,5 +1,5 @@
freebsd_instance: freebsd_instance:
image_family: freebsd-14-0-snap image_family: freebsd-15-0-snap
task: task:
name: FreeBSD name: FreeBSD

View File

@@ -4,10 +4,10 @@ on:
- pull_request - pull_request
permissions: {} permissions: {}
jobs: jobs:
github-runner: build-native:
permissions: permissions:
contents: write contents: read
name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} ${{ matrix.prebuild && '- prebuild' }} name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
container: ${{ matrix.container }} container: ${{ matrix.container }}
strategy: strategy:
@@ -20,7 +20,7 @@ jobs:
nodejs_version: "^18.17.0" nodejs_version: "^18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
platform: linux-x64 platform: linux-x64
prebuild: true package: true
- os: ubuntu-24.04 - os: ubuntu-24.04
container: rockylinux:8 container: rockylinux:8
nodejs_arch: x64 nodejs_arch: x64
@@ -37,7 +37,7 @@ jobs:
container: node:18-alpine3.17 container: node:18-alpine3.17
nodejs_version_major: 18 nodejs_version_major: 18
platform: linuxmusl-x64 platform: linuxmusl-x64
prebuild: true package: true
- os: ubuntu-24.04 - os: ubuntu-24.04
container: node:20-alpine3.18 container: node:20-alpine3.18
nodejs_version_major: 20 nodejs_version_major: 20
@@ -46,12 +46,25 @@ jobs:
container: node:22-alpine3.20 container: node:22-alpine3.20
nodejs_version_major: 22 nodejs_version_major: 22
platform: linuxmusl-x64 platform: linuxmusl-x64
- os: ubuntu-24.04-arm
container: arm64v8/rockylinux:8
nodejs_arch: arm64
nodejs_version: "^18.17.0"
nodejs_version_major: 18
platform: linux-arm64
package: true
- os: ubuntu-24.04-arm
container: arm64v8/rockylinux:8
nodejs_arch: arm64
nodejs_version: "^20.3.0"
nodejs_version_major: 20
platform: linux-arm64
- os: macos-13 - os: macos-13
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^18.17.0" nodejs_version: "^18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
platform: darwin-x64 platform: darwin-x64
prebuild: true package: true
- os: macos-13 - os: macos-13
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^20.3.0" nodejs_version: "^20.3.0"
@@ -67,7 +80,7 @@ jobs:
nodejs_version: "^18.17.0" nodejs_version: "^18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
platform: darwin-arm64 platform: darwin-arm64
prebuild: true package: true
- os: macos-14 - os: macos-14
nodejs_arch: arm64 nodejs_arch: arm64
nodejs_version: "^20.3.0" nodejs_version: "^20.3.0"
@@ -78,44 +91,55 @@ jobs:
nodejs_version: "^22.9.0" nodejs_version: "^22.9.0"
nodejs_version_major: 22 nodejs_version_major: 22
platform: darwin-arm64 platform: darwin-arm64
- os: windows-2019 - os: windows-2022
nodejs_arch: x86 nodejs_arch: x86
nodejs_version: "18.18.2" # pinned to avoid 18.19.0 and npm 10 nodejs_version: "18.18.2" # pinned to avoid 18.19.0 and npm 10
nodejs_version_major: 18 nodejs_version_major: 18
platform: win32-ia32 platform: win32-ia32
prebuild: true package: true
- os: windows-2019 - os: windows-2022
nodejs_arch: x86 nodejs_arch: x86
nodejs_version: "^20.3.0" nodejs_version: "^20.3.0"
nodejs_version_major: 20 nodejs_version_major: 20
platform: win32-ia32 platform: win32-ia32
- os: windows-2019 - os: windows-2022
nodejs_arch: x86 nodejs_arch: x86
nodejs_version: "^22.9.0" nodejs_version: "^22.9.0"
nodejs_version_major: 22 nodejs_version_major: 22
platform: win32-ia32 platform: win32-ia32
- os: windows-2019 - os: windows-2022
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^18.17.0" nodejs_version: "^18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
platform: win32-x64 platform: win32-x64
prebuild: true package: true
- os: windows-2019 - os: windows-2022
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^20.3.0" nodejs_version: "^20.3.0"
nodejs_version_major: 20 nodejs_version_major: 20
platform: win32-x64 platform: win32-x64
- os: windows-2019 - os: windows-2022
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^22.9.0" nodejs_version: "^22.9.0"
nodejs_version_major: 22 nodejs_version_major: 22
platform: win32-x64 platform: win32-x64
- os: windows-11-arm
nodejs_arch: arm64
nodejs_version: "^20.3.0"
nodejs_version_major: 20
platform: win32-arm64
package: true
- os: windows-11-arm
nodejs_arch: arm64
nodejs_version: "^22.9.0"
nodejs_version_major: 22
platform: win32-arm64
steps: steps:
- name: Dependencies (Rocky Linux glibc) - name: Dependencies (Rocky Linux glibc)
if: contains(matrix.container, 'rockylinux') if: contains(matrix.container, 'rockylinux')
run: | run: |
dnf install -y gcc-toolset-11-gcc-c++ make git python3.12 fontconfig google-noto-sans-fonts dnf install -y gcc-toolset-14-gcc-c++ make git python3.12 fontconfig google-noto-sans-fonts
echo "/opt/rh/gcc-toolset-11/root/usr/bin" >> $GITHUB_PATH echo "/opt/rh/gcc-toolset-14/root/usr/bin" >> $GITHUB_PATH
- name: Dependencies (Linux musl) - name: Dependencies (Linux musl)
if: contains(matrix.container, 'alpine') if: contains(matrix.container, 'alpine')
run: apk add build-base git python3 font-noto --update-cache run: apk add build-base git python3 font-noto --update-cache
@@ -130,31 +154,70 @@ jobs:
with: with:
node-version: ${{ matrix.nodejs_version }} node-version: ${{ matrix.nodejs_version }}
architecture: ${{ matrix.nodejs_arch }} architecture: ${{ matrix.nodejs_arch }}
- name: Checkout - uses: actions/checkout@v4
uses: actions/checkout@v4
- name: Install - name: Install
run: npm install --build-from-source run: npm install --build-from-source
- name: Test - name: Test
run: npm test run: npm test
- name: Test packaging - name: Populate npm package
run: | if: matrix.package
npm run package-from-local-build run: npm run package-from-local-build
npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}" - uses: actions/upload-artifact@v4
npm run clean if: matrix.package
npm install --ignore-scripts with:
npm test name: ${{ matrix.platform }}
- name: Prebuild path: npm/${{ matrix.platform }}
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/') retention-days: 1
env: if-no-files-found: error
prebuild_upload: ${{ secrets.GITHUB_TOKEN }} build-linuxmusl-arm-64:
run: |
node -e "require('fs').cpSync('package.json', 'src/package.json')"
cd src
npx prebuild
github-runner-qemu:
permissions: permissions:
contents: write contents: read
name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} - prebuild name: "build-linuxmusl-arm64 [Node.js ${{ matrix.nodejs_version_major }}] ${{ matrix.package && '[package]' }}"
runs-on: ubuntu-24.04-arm
container:
image: ${{ matrix.container }}
volumes:
- /:/host
strategy:
fail-fast: false
matrix:
include:
- container: node:18-alpine3.17
nodejs_version_major: 18
package: true
- container: node:20-alpine3.18
nodejs_version_major: 20
steps:
- name: Allow Linux musl containers on ARM64 runners # https://github.com/actions/runner/issues/801#issuecomment-2394425757
shell: sh
run: |
apk add nodejs
sed -i "s:ID=alpine:ID=NotpineForGHA:" /etc/os-release
cd /host/home/runner/runners/*/externals/
rm -rf node20/*
mkdir node20/bin
ln -s /usr/bin/node node20/bin/node
- name: Dependencies
run: apk add build-base git python3 font-noto --update-cache
- uses: actions/checkout@v4
- name: Install
run: npm install --build-from-source
- name: Test
run: npm test
- name: Populate npm package
if: matrix.package
run: npm run package-from-local-build
- uses: actions/upload-artifact@v4
if: matrix.package
with:
name: linuxmusl-arm64
path: npm/linuxmusl-arm64
retention-days: 1
if-no-files-found: error
build-qemu:
permissions:
contents: read
name: "build-${{ matrix.platform }} [Node.js ${{ matrix.nodejs_version_major }}] [package]"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
strategy: strategy:
fail-fast: false fail-fast: false
@@ -187,8 +250,6 @@ jobs:
with: with:
arch: ${{ matrix.run_on_arch }} arch: ${{ matrix.run_on_arch }}
distro: ${{ matrix.distro }} distro: ${{ matrix.distro }}
env: |
prebuild_upload: "${{ startsWith(github.ref, 'refs/tags/') && secrets.GITHUB_TOKEN || '' }}"
run: | run: |
apt-get update apt-get update
apt-get install -y curl g++ git libatomic1 make python3 xz-utils apt-get install -y curl g++ git libatomic1 make python3 xz-utils
@@ -198,20 +259,20 @@ jobs:
npm install --build-from-source npm install --build-from-source
npx mocha --no-config --spec=test/unit/io.js --timeout=30000 npx mocha --no-config --spec=test/unit/io.js --timeout=30000
npm run package-from-local-build npm run package-from-local-build
npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}" - uses: actions/upload-artifact@v4
npm run clean with:
npm install --ignore-scripts name: ${{ matrix.platform }}
npx mocha --no-config --spec=test/unit/io.js --timeout=30000 path: npm/${{ matrix.platform }}
[[ -n $prebuild_upload ]] && cd src && ln -s ../package.json && npx prebuild || true retention-days: 1
github-runner-emscripten: if-no-files-found: error
build-emscripten:
permissions: permissions:
contents: write contents: read
name: wasm32 - prebuild name: "build-wasm32 [package]"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
container: "emscripten/emsdk:4.0.6" container: "emscripten/emsdk:4.0.10"
steps: steps:
- name: Checkout - uses: actions/checkout@v4
uses: actions/checkout@v4
- name: Dependencies - name: Dependencies
run: apt-get update && apt-get install -y pkg-config run: apt-get update && apt-get install -y pkg-config
- name: Dependencies (Node.js) - name: Dependencies (Node.js)
@@ -229,17 +290,35 @@ jobs:
test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP" test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP"
- name: Test - name: Test
run: emmake npm test run: emmake npm test
- name: Test packaging - name: Populate npm package
run: | run: emmake npm run package-from-local-build
emmake npm run package-from-local-build - uses: actions/upload-artifact@v4
npm pkg set "optionalDependencies.@img/sharp-wasm32=file:./npm/wasm32" with:
npm run clean name: wasm32
rm -rf node_modules/@img/sharp-linux-x64 path: npm/wasm32
npm install --cpu=wasm32 retention-days: 1
npm test if-no-files-found: error
- name: Prebuild release:
if: startsWith(github.ref, 'refs/tags/') permissions:
env: contents: write
npm_config_nodedir: emscripten runs-on: ubuntu-24.04
prebuild_upload: ${{ secrets.GITHUB_TOKEN }} needs:
run: cd src && ln -s ../package.json && emmake npx prebuild --platform=emscripten --arch=wasm32 --strip=0 - build-native
- build-linuxmusl-arm-64
- build-qemu
- build-emscripten
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: npm
- name: Create npm workspace tarball
run: tar -vcaf npm-workspace.tar.xz --directory npm --exclude=from-local-build.js .
- name: Create GitHub release for tag
if: startsWith(github.ref, 'refs/tags/v')
uses: ncipollo/release-action@v1
with:
artifacts: npm-workspace.tar.xz
artifactContentType: application/x-xz
prerelease: ${{ contains(github.ref, '-rc') }}
makeLatest: ${{ !contains(github.ref, '-rc') }}

View File

@@ -70,27 +70,27 @@ jobs:
runtime: bun runtime: bun
- name: win32-x64-node-npm - name: win32-x64-node-npm
runs-on: windows-2019 runs-on: windows-2022
runtime: node runtime: node
package-manager: npm package-manager: npm
- name: win32-x64-node-pnpm - name: win32-x64-node-pnpm
runs-on: windows-2019 runs-on: windows-2022
runtime: node runtime: node
package-manager: pnpm package-manager: pnpm
- name: win32-x64-node-yarn - name: win32-x64-node-yarn
runs-on: windows-2019 runs-on: windows-2022
runtime: node runtime: node
package-manager: yarn package-manager: yarn
- name: win32-x64-node-yarn-pnp - name: win32-x64-node-yarn-pnp
runs-on: windows-2019 runs-on: windows-2022
runtime: node runtime: node
package-manager: yarn-pnp package-manager: yarn-pnp
- name: win32-x64-node-yarn-v1 - name: win32-x64-node-yarn-v1
runs-on: windows-2019 runs-on: windows-2022
runtime: node runtime: node
package-manager: yarn-v1 package-manager: yarn-v1
- name: win32-x64-deno - name: win32-x64-deno
runs-on: windows-2019 runs-on: windows-2022
runtime: deno runtime: deno
steps: steps:

View File

@@ -1,6 +0,0 @@
{
"runtime": "napi",
"include-regex": "(sharp-.+\\.node|libvips-.+\\.dll)",
"prerelease": true,
"strip": true
}

View File

@@ -1,6 +1,6 @@
# sharp # sharp
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right"> <img src="https://sharp.pixelplumbing.com/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
The typical use case for this high speed Node-API module The typical use case for this high speed Node-API module
is to convert large images in common formats to is to convert large images in common formats to

View File

@@ -18,7 +18,7 @@ export default defineConfig({
tag: 'meta', tag: 'meta',
attrs: { attrs: {
'http-equiv': 'Content-Security-Policy', 'http-equiv': 'Content-Security-Policy',
content: "default-src 'self'; connect-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://cdn.jsdelivr.net/gh/lovell/; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com/beacon.min.js/;" content: "default-src 'self'; connect-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com/beacon.min.js/;"
} }
}, { }, {
tag: 'link', tag: 'link',
@@ -70,10 +70,10 @@ export default defineConfig({
{ label: 'Performance', slug: 'performance' }, { label: 'Performance', slug: 'performance' },
{ label: 'Changelog', slug: 'changelog' } { label: 'Changelog', slug: 'changelog' }
], ],
social: { social: [
openCollective: 'https://opencollective.com/libvips', { icon: 'openCollective', label: 'Open Collective', href: 'https://opencollective.com/libvips' },
github: 'https://github.com/lovell/sharp' { icon: 'github', label: 'GitHub', href: 'https://github.com/lovell/sharp' }
} ]
}) })
] ]
}); });

View File

@@ -11,99 +11,6 @@
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" } { "key": "X-Frame-Options", "value": "SAMEORIGIN" }
] ]
} }
],
"redirects": [
{
"source": "**/install/**",
"destination": "/install",
"type": 301
},
{
"source": "/page/install",
"destination": "/install",
"type": 301
},
{
"source": "**/api-constructor/**",
"destination": "/api-constructor",
"type": 301
},
{
"source": "**/api-input/**",
"destination": "/api-input",
"type": 301
},
{
"source": "**/api-output/**",
"destination": "/api-output",
"type": 301
},
{
"source": "**/api-resize/**",
"destination": "/api-resize",
"type": 301
},
{
"source": "**/api-compsite/**",
"destination": "/api-compsite",
"type": 301
},
{
"source": "**/api-operation/**",
"destination": "/api-operation",
"type": 301
},
{
"source": "**/api-colour/**",
"destination": "/api-colour",
"type": 301
},
{
"source": "**/api-channel/**",
"destination": "/api-channel",
"type": 301
},
{
"source": "**/api-utility/**",
"destination": "/api-utility",
"type": 301
},
{
"source": "/page/api",
"destination": "/api-constructor",
"type": 301
},
{
"source": "**/performance/**",
"destination": "/performance",
"type": 301
},
{
"source": "/page/performance",
"destination": "/performance",
"type": 301
},
{
"source": "**/changelog/**",
"destination": "/changelog",
"type": 301
},
{
"source": "/page/changelog",
"destination": "/changelog",
"type": 301
},
{
"source": "/en/**",
"destination": "/",
"type": 301
}
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
] ]
} }
} }

View File

@@ -11,7 +11,7 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/starlight": "^0.32.3", "@astrojs/starlight": "^0.34.3",
"astro": "^5.5.3" "astro": "^5.7.13"
} }
} }

View File

@@ -317,3 +317,6 @@ GitHub: https://github.com/florentzabera
Name: Quentin Pinçon Name: Quentin Pinçon
GitHub: https://github.com/qpincon GitHub: https://github.com/qpincon
Name: Hans Chen
GitHub: https://github.com/hans00

View File

@@ -47,6 +47,7 @@ where the overall height is the `pageHeight` multiplied by the number of `pages`
| [options.subifd] | <code>number</code> | <code>-1</code> | subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. | | [options.subifd] | <code>number</code> | <code>-1</code> | subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. |
| [options.level] | <code>number</code> | <code>0</code> | level to extract from a multi-level input (OpenSlide), zero based. | | [options.level] | <code>number</code> | <code>0</code> | level to extract from a multi-level input (OpenSlide), zero based. |
| [options.pdfBackground] | <code>string</code> \| <code>Object</code> | | Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. | | [options.pdfBackground] | <code>string</code> \| <code>Object</code> | | Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. |
| [options.jp2Oneshot] | <code>boolean</code> | <code>false</code> | Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility. |
| [options.animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. | | [options.animated] | <code>boolean</code> | <code>false</code> | Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. |
| [options.raw] | <code>Object</code> | | describes raw pixel input image data. See `raw()` for pixel ordering. | | [options.raw] | <code>Object</code> | | describes raw pixel input image data. See `raw()` for pixel ordering. |
| [options.raw.width] | <code>number</code> | | integral number of pixels wide. | | [options.raw.width] | <code>number</code> | | integral number of pixels wide. |

View File

@@ -626,6 +626,9 @@ Use these AVIF options for output image.
AVIF image sequences are not supported. AVIF image sequences are not supported.
Prebuilt binaries support a bitdepth of 8 only. Prebuilt binaries support a bitdepth of 8 only.
This feature is experimental on the Windows ARM64 platform
and requires a CPU with ARM64v8.4 or later.
**Throws**: **Throws**:

View File

@@ -17,7 +17,7 @@ When both a `width` and `height` are provided, the possible methods by which the
Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property. Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
<img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.svg"> <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="/api-resize-fit.svg">
When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are: When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
- `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`. - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.

View File

@@ -4,7 +4,51 @@ title: Changelog
## v0.34 - *hat* ## v0.34 - *hat*
Requires libvips v8.16.1 Requires libvips v8.17.0
### v0.34.3 - TBD
* Upgrade to libvips v8.17.0 for upstream bug fixes.
* Expose JPEG 2000 `oneshot` decoder option.
[#4262](https://github.com/lovell/sharp/pull/4262)
[@mbklein](https://github.com/mbklein)
* Support composite operation with non-sRGB pipeline colourspace.
[#4412](https://github.com/lovell/sharp/pull/4412)
[@kleisauke](https://github.com/kleisauke)
### v0.34.2 - 20th May 2025
* Ensure animated GIF to WebP conversion retains loop (regression in 0.34.0).
[#3394](https://github.com/lovell/sharp/issues/3394)
* Ensure `pdfBackground` constructor property is used.
[#4207](https://github.com/lovell/sharp/pull/4207)
[#4398](https://github.com/lovell/sharp/issues/4398)
* Add experimental support for prebuilt Windows ARM64 binaries.
[#4375](https://github.com/lovell/sharp/pull/4375)
[@hans00](https://github.com/hans00)
* Ensure resizing with a `fit` of `contain` supports multiple alpha channels.
[#4382](https://github.com/lovell/sharp/issues/4382)
* TypeScript: Ensure `metadata` response more closely matches reality.
[#4383](https://github.com/lovell/sharp/issues/4383)
* TypeScript: Ensure `smartDeblock` property is included in WebP definition.
[#4387](https://github.com/lovell/sharp/pull/4387)
[@Stephen-X](https://github.com/Stephen-X)
* Ensure support for wide-character filenames on Windows (regression in 0.34.0).
[#4391](https://github.com/lovell/sharp/issues/4391)
### v0.34.1 - 7th April 2025
* TypeScript: Ensure new `autoOrient` property is optional.
[#4362](https://github.com/lovell/sharp/pull/4362)
[@styfle](https://github.com/styfle)
### v0.34.0 - 4th April 2025 ### v0.34.0 - 4th April 2025

View File

@@ -2,7 +2,7 @@
title: "High performance Node.js image processing" title: "High performance Node.js image processing"
--- ---
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right"> <img src="/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
The typical use case for this high speed Node-API module The typical use case for this high speed Node-API module
is to convert large images in common formats to is to convert large images in common formats to

View File

@@ -21,7 +21,7 @@ pnpm add sharp
``` ```
When using `pnpm`, you may need to add `sharp` to When using `pnpm`, you may need to add `sharp` to
[ignoredBuiltDependencies](https://pnpm.io/package_json#pnpmignoredbuiltdependencies) [ignoredBuiltDependencies](https://pnpm.io/settings#ignoredbuiltdependencies)
to silence warnings. to silence warnings.
```sh ```sh
@@ -53,6 +53,7 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
* Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2) * Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
* Windows x64 * Windows x64
* Windows x86 * Windows x86
* Windows ARM64 (experimental, CPU with ARMv8.4 required for all features)
This provides support for the This provides support for the
JPEG, PNG, WebP, AVIF (limited to 8-bit depth), TIFF, GIF and SVG (input) image formats. JPEG, PNG, WebP, AVIF (limited to 8-bit depth), TIFF, GIF and SVG (input) image formats.
@@ -93,7 +94,7 @@ Use the [supportedArchitectures](https://yarnpkg.com/configuration/yarnrc#suppor
### pnpm v8+ ### pnpm v8+
Use the [supportedArchitectures](https://pnpm.io/package_json#pnpmsupportedarchitectures) configuration. Use the [supportedArchitectures](https://pnpm.io/settings#supportedarchitectures) configuration.
## Custom libvips ## Custom libvips
@@ -133,7 +134,7 @@ npm install --save node-addon-api node-gyp
``` ```
When using `pnpm`, you may need to add `sharp` to When using `pnpm`, you may need to add `sharp` to
[onlyBuiltDependencies](https://pnpm.io/package_json#pnpmonlybuiltdependencies) [onlyBuiltDependencies](https://pnpm.io/settings#onlybuiltdependencies)
to ensure the installation script can be run. to ensure the installation script can be run.
For cross-compiling, the `--platform`, `--arch` and `--libc` npm flags For cross-compiling, the `--platform`, `--arch` and `--libc` npm flags
@@ -174,7 +175,7 @@ The default memory allocator on most glibc-based Linux systems
processes that involve lots of small memory allocations. processes that involve lots of small memory allocations.
For this reason, by default, sharp will limit the use of thread-based For this reason, by default, sharp will limit the use of thread-based
[concurrency](api-utility#concurrency) when the glibc allocator is [concurrency](/api-utility#concurrency) when the glibc allocator is
detected at runtime. detected at runtime.
To help avoid fragmentation and improve performance on these systems, To help avoid fragmentation and improve performance on these systems,

View File

@@ -134,6 +134,26 @@ function toColorspace (colorspace) {
return this.toColourspace(colorspace); return this.toColourspace(colorspace);
} }
/**
* Create a RGBA colour array from a given value.
* @private
* @param {string|Object} value
* @throws {Error} Invalid value
*/
function _getBackgroundColourOption (value) {
if (is.object(value) || is.string(value)) {
const colour = color(value);
return [
colour.red(),
colour.green(),
colour.blue(),
Math.round(colour.alpha() * 255)
];
} else {
throw is.invalidParameterError('background', 'object or string', value);
}
}
/** /**
* Update a colour attribute of the this.options Object. * Update a colour attribute of the this.options Object.
* @private * @private
@@ -143,17 +163,7 @@ function toColorspace (colorspace) {
*/ */
function _setBackgroundColourOption (key, value) { function _setBackgroundColourOption (key, value) {
if (is.defined(value)) { if (is.defined(value)) {
if (is.object(value) || is.string(value)) { this.options[key] = _getBackgroundColourOption(value);
const colour = color(value);
this.options[key] = [
colour.red(),
colour.green(),
colour.blue(),
Math.round(colour.alpha() * 255)
];
} else {
throw is.invalidParameterError('background', 'object or string', value);
}
} }
} }
@@ -173,6 +183,7 @@ module.exports = function (Sharp) {
toColourspace, toColourspace,
toColorspace, toColorspace,
// Private // Private
_getBackgroundColourOption,
_setBackgroundColourOption _setBackgroundColourOption
}); });
// Class attributes // Class attributes

View File

@@ -156,6 +156,7 @@ const debuglog = util.debuglog('sharp');
* @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image. * @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based. * @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
* @param {string|Object} [options.pdfBackground] - Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. * @param {string|Object} [options.pdfBackground] - Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick.
* @param {boolean} [options.jp2Oneshot=false] - Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility.
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`. * @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`.
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering. * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
* @param {number} [options.raw.width] - integral number of pixels wide. * @param {number} [options.raw.width] - integral number of pixels wide.
@@ -298,7 +299,7 @@ const Sharp = function (input, options) {
withExif: {}, withExif: {},
withExifMerge: true, withExifMerge: true,
resolveWithObject: false, resolveWithObject: false,
loop: 1, loop: -1,
delay: [], delay: [],
// output format // output format
jpegQuality: 80, jpegQuality: 80,

63
lib/index.d.ts vendored
View File

@@ -971,7 +971,7 @@ declare namespace sharp {
* *
* Using this option will remove the EXIF `Orientation` tag, if any. * Using this option will remove the EXIF `Orientation` tag, if any.
*/ */
autoOrient?: boolean; autoOrient?: boolean | undefined;
/** /**
* When to abort processing of invalid pixel data, one of (in order of sensitivity): * When to abort processing of invalid pixel data, one of (in order of sensitivity):
* 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. (optional, default 'warning') * 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. (optional, default 'warning')
@@ -1009,6 +1009,8 @@ declare namespace sharp {
level?: number | undefined; level?: number | undefined;
/** Background colour to use when PDF is partially transparent. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. */ /** Background colour to use when PDF is partially transparent. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick. */
pdfBackground?: Colour | Color | undefined; pdfBackground?: Colour | Color | undefined;
/** Set to `true` to load JPEG 2000 images using [oneshot mode](https://github.com/libvips/libvips/issues/4205) */
jp2Oneshot?: boolean | undefined;
/** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */ /** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default false) */
animated?: boolean | undefined; animated?: boolean | undefined;
/** Describes raw pixel input image data. See raw() for pixel ordering. */ /** Describes raw pixel input image data. See raw() for pixel ordering. */
@@ -1146,13 +1148,13 @@ declare namespace sharp {
/** Number value of the EXIF Orientation header, if present */ /** Number value of the EXIF Orientation header, if present */
orientation?: number | undefined; orientation?: number | undefined;
/** Name of decoder used to decompress image data e.g. jpeg, png, webp, gif, svg */ /** Name of decoder used to decompress image data e.g. jpeg, png, webp, gif, svg */
format?: keyof FormatEnum | undefined; format: keyof FormatEnum;
/** Total size of image in bytes, for Stream and Buffer input only */ /** Total size of image in bytes, for Stream and Buffer input only */
size?: number | undefined; size?: number | undefined;
/** Number of pixels wide (EXIF orientation is not taken into consideration) */ /** Number of pixels wide (EXIF orientation is not taken into consideration) */
width?: number | undefined; width: number;
/** Number of pixels high (EXIF orientation is not taken into consideration) */ /** Number of pixels high (EXIF orientation is not taken into consideration) */
height?: number | undefined; height: number;
/** Any changed metadata after the image orientation is applied. */ /** Any changed metadata after the image orientation is applied. */
autoOrient: { autoOrient: {
/** Number of pixels wide (EXIF orientation is taken into consideration) */ /** Number of pixels wide (EXIF orientation is taken into consideration) */
@@ -1161,19 +1163,19 @@ declare namespace sharp {
height: number; height: number;
}; };
/** Name of colour space interpretation */ /** Name of colour space interpretation */
space?: keyof ColourspaceEnum | undefined; space: keyof ColourspaceEnum;
/** Number of bands e.g. 3 for sRGB, 4 for CMYK */ /** Number of bands e.g. 3 for sRGB, 4 for CMYK */
channels?: Channels | undefined; channels: Channels;
/** Name of pixel depth format e.g. uchar, char, ushort, float ... */ /** Name of pixel depth format e.g. uchar, char, ushort, float ... */
depth?: string | undefined; depth: keyof DepthEnum;
/** Number of pixels per inch (DPI), if present */ /** Number of pixels per inch (DPI), if present */
density?: number | undefined; density?: number | undefined;
/** String containing JPEG chroma subsampling, 4:2:0 or 4:4:4 for RGB, 4:2:0:4 or 4:4:4:4 for CMYK */ /** String containing JPEG chroma subsampling, 4:2:0 or 4:4:4 for RGB, 4:2:0:4 or 4:4:4:4 for CMYK */
chromaSubsampling?: string | undefined; chromaSubsampling?: string | undefined;
/** Boolean indicating whether the image is interlaced using a progressive scan */ /** Boolean indicating whether the image is interlaced using a progressive scan */
isProgressive?: boolean | undefined; isProgressive: boolean;
/** Boolean indicating whether the image is palette-based (GIF, PNG). */ /** Boolean indicating whether the image is palette-based (GIF, PNG). */
isPalette?: boolean | undefined; isPalette: boolean;
/** Number of bits per sample for each channel (GIF, PNG). */ /** Number of bits per sample for each channel (GIF, PNG). */
bitsPerSample?: number | undefined; bitsPerSample?: number | undefined;
/** Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP */ /** Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP */
@@ -1187,9 +1189,9 @@ declare namespace sharp {
/** Number of the primary page in a HEIF image */ /** Number of the primary page in a HEIF image */
pagePrimary?: number | undefined; pagePrimary?: number | undefined;
/** Boolean indicating the presence of an embedded ICC profile */ /** Boolean indicating the presence of an embedded ICC profile */
hasProfile?: boolean | undefined; hasProfile: boolean;
/** Boolean indicating the presence of an alpha transparency channel */ /** Boolean indicating the presence of an alpha transparency channel */
hasAlpha?: boolean | undefined; hasAlpha: boolean;
/** Buffer containing raw EXIF data, if present */ /** Buffer containing raw EXIF data, if present */
exif?: Buffer | undefined; exif?: Buffer | undefined;
/** Buffer containing raw ICC profile data, if present */ /** Buffer containing raw ICC profile data, if present */
@@ -1336,6 +1338,8 @@ declare namespace sharp {
nearLossless?: boolean | undefined; nearLossless?: boolean | undefined;
/** Use high quality chroma subsampling (optional, default false) */ /** Use high quality chroma subsampling (optional, default false) */
smartSubsample?: boolean | undefined; smartSubsample?: boolean | undefined;
/** Auto-adjust the deblocking filter, slow but can improve low contrast edges (optional, default false) */
smartDeblock?: boolean | undefined;
/** Level of CPU effort to reduce file size, integer 0-6 (optional, default 4) */ /** Level of CPU effort to reduce file size, integer 0-6 (optional, default 4) */
effort?: number | undefined; effort?: number | undefined;
/** Prevent use of animation key frames to minimise file size (slow) (optional, default false) */ /** Prevent use of animation key frames to minimise file size (slow) (optional, default false) */
@@ -1699,6 +1703,10 @@ declare namespace sharp {
/** When using the attention crop strategy, the focal point of the cropped region */ /** When using the attention crop strategy, the focal point of the cropped region */
attentionX?: number | undefined; attentionX?: number | undefined;
attentionY?: number | undefined; attentionY?: number | undefined;
/** Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP */
pages?: number | undefined;
/** Number of pixels high each page in a multi-page image will be. */
pageHeight?: number | undefined;
} }
interface AvailableFormatInfo { interface AvailableFormatInfo {
@@ -1739,11 +1747,38 @@ declare namespace sharp {
} }
interface ColourspaceEnum { interface ColourspaceEnum {
multiband: string;
'b-w': string; 'b-w': string;
bw: string; cmc: string;
cmyk: string; cmyk: string;
fourier: string;
grey16: string;
histogram: string;
hsv: string;
lab: string;
labq: string;
labs: string;
lch: string;
matrix: string;
multiband: string;
rgb: string;
rgb16: string;
scrgb: string;
srgb: string; srgb: string;
xyz: string;
yxy: string;
}
interface DepthEnum {
char: string;
complex: string;
double: string;
dpcomplex: string;
float: string;
int: string;
short: string;
uchar: string;
uint: string;
ushort: string;
} }
type FailOnOptions = 'none' | 'truncated' | 'error' | 'warning'; type FailOnOptions = 'none' | 'truncated' | 'error' | 'warning';
@@ -1812,6 +1847,7 @@ declare namespace sharp {
interface FormatEnum { interface FormatEnum {
avif: AvailableFormatInfo; avif: AvailableFormatInfo;
dz: AvailableFormatInfo; dz: AvailableFormatInfo;
exr: AvailableFormatInfo;
fits: AvailableFormatInfo; fits: AvailableFormatInfo;
gif: AvailableFormatInfo; gif: AvailableFormatInfo;
heif: AvailableFormatInfo; heif: AvailableFormatInfo;
@@ -1825,6 +1861,7 @@ declare namespace sharp {
pdf: AvailableFormatInfo; pdf: AvailableFormatInfo;
png: AvailableFormatInfo; png: AvailableFormatInfo;
ppm: AvailableFormatInfo; ppm: AvailableFormatInfo;
rad: AvailableFormatInfo;
raw: AvailableFormatInfo; raw: AvailableFormatInfo;
svg: AvailableFormatInfo; svg: AvailableFormatInfo;
tiff: AvailableFormatInfo; tiff: AvailableFormatInfo;

View File

@@ -3,7 +3,6 @@
'use strict'; 'use strict';
const color = require('color');
const is = require('./is'); const is = require('./is');
const sharp = require('./sharp'); const sharp = require('./sharp');
@@ -28,9 +27,9 @@ const align = {
* @private * @private
*/ */
function _inputOptionsFromObject (obj) { function _inputOptionsFromObject (obj) {
const { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient } = obj; const { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient, jp2Oneshot } = obj;
return [raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient].some(is.defined) return [raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient, jp2Oneshot].some(is.defined)
? { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient } ? { raw, density, limitInputPixels, ignoreIcc, unlimited, sequentialRead, failOn, failOnError, animated, page, pages, subifd, pdfBackground, autoOrient, jp2Oneshot }
: undefined; : undefined;
} }
@@ -249,7 +248,15 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
} }
// PDF background colour // PDF background colour
if (is.defined(inputOptions.pdfBackground)) { if (is.defined(inputOptions.pdfBackground)) {
this._setBackgroundColourOption('pdfBackground', inputOptions.pdfBackground); inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdfBackground);
}
// JP2 oneshot
if (is.defined(inputOptions.jp2Oneshot)) {
if (is.bool(inputOptions.jp2Oneshot)) {
inputDescriptor.jp2Oneshot = inputOptions.jp2Oneshot;
} else {
throw is.invalidParameterError('jp2Oneshot', 'boolean', inputOptions.jp2Oneshot);
}
} }
// Create new image // Create new image
if (is.defined(inputOptions.create)) { if (is.defined(inputOptions.create)) {
@@ -288,13 +295,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
if (!is.inRange(inputOptions.create.channels, 3, 4)) { if (!is.inRange(inputOptions.create.channels, 3, 4)) {
throw is.invalidParameterError('create.channels', 'number between 3 and 4', inputOptions.create.channels); throw is.invalidParameterError('create.channels', 'number between 3 and 4', inputOptions.create.channels);
} }
const background = color(inputOptions.create.background); inputDescriptor.createBackground = this._getBackgroundColourOption(inputOptions.create.background);
inputDescriptor.createBackground = [
background.red(),
background.green(),
background.blue(),
Math.round(background.alpha() * 255)
];
} else { } else {
throw new Error('Expected valid noise or background to create a new input image'); throw new Error('Expected valid noise or background to create a new input image');
} }
@@ -410,13 +411,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
} }
} }
if (is.defined(inputOptions.join.background)) { if (is.defined(inputOptions.join.background)) {
const background = color(inputOptions.join.background); inputDescriptor.joinBackground = this._getBackgroundColourOption(inputOptions.join.background);
inputDescriptor.joinBackground = [
background.red(),
background.green(),
background.blue(),
Math.round(background.alpha() * 255)
];
} }
if (is.defined(inputOptions.join.halign)) { if (is.defined(inputOptions.join.halign)) {
if (is.string(inputOptions.join.halign) && is.string(this.constructor.align[inputOptions.join.halign])) { if (is.string(inputOptions.join.halign) && is.string(this.constructor.align[inputOptions.join.halign])) {

View File

@@ -18,9 +18,9 @@ const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).versio
const prebuiltPlatforms = [ const prebuiltPlatforms = [
'darwin-arm64', 'darwin-x64', 'darwin-arm64', 'darwin-x64',
'linux-arm', 'linux-arm64', 'linux-s390x', 'linux-x64', 'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-s390x', 'linux-x64',
'linuxmusl-arm64', 'linuxmusl-x64', 'linuxmusl-arm64', 'linuxmusl-x64',
'win32-ia32', 'win32-x64' 'win32-arm64', 'win32-ia32', 'win32-x64'
]; ];
const spawnSyncOptions = { const spawnSyncOptions = {

View File

@@ -3,7 +3,6 @@
'use strict'; 'use strict';
const color = require('color');
const is = require('./is'); const is = require('./is');
/** /**
@@ -67,13 +66,7 @@ function rotate (angle, options) {
} else if (is.number(angle)) { } else if (is.number(angle)) {
this.options.rotationAngle = angle; this.options.rotationAngle = angle;
if (is.object(options) && options.background) { if (is.object(options) && options.background) {
const backgroundColour = color(options.background); this._setBackgroundColourOption('rotationBackground', options.background);
this.options.rotationBackground = [
backgroundColour.red(),
backgroundColour.green(),
backgroundColour.blue(),
Math.round(backgroundColour.alpha() * 255)
];
} }
} else { } else {
throw is.invalidParameterError('angle', 'numeric', angle); throw is.invalidParameterError('angle', 'numeric', angle);

View File

@@ -1019,6 +1019,9 @@ function tiff (options) {
* AVIF image sequences are not supported. * AVIF image sequences are not supported.
* Prebuilt binaries support a bitdepth of 8 only. * Prebuilt binaries support a bitdepth of 8 only.
* *
* This feature is experimental on the Windows ARM64 platform
* and requires a CPU with ARM64v8.4 or later.
*
* @example * @example
* const data = await sharp(input) * const data = await sharp(input)
* .avif({ effort: 2 }) * .avif({ effort: 2 })

View File

@@ -129,7 +129,7 @@ function isResizeExpected (options) {
* *
* Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property. * Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
* *
* <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/api-resize-fit.svg"> * <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="/api-resize-fit.svg">
* *
* When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are: * When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`. * - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.

View File

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

View File

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

View File

@@ -1,73 +0,0 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
// Populate contents of all packages with the current GitHub release
const { readFile, writeFile, appendFile, copyFile, rm } = require('node:fs/promises');
const path = require('node:path');
const { Readable } = require('node:stream');
const { pipeline } = require('node:stream/promises');
const { createGunzip } = require('node:zlib');
const { extract } = require('tar-fs');
const { workspaces } = require('./package.json');
const { version } = require('../package.json');
const mapTarballEntry = (header) => {
header.name = path.basename(header.name);
return header;
};
const licensing = `
## Licensing
Copyright 2013 Lovell Fuller and others.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
`;
workspaces.map(async platform => {
const prebuildPlatform = platform === 'wasm32' ? 'emscripten-wasm32' : platform;
const url = `https://github.com/lovell/sharp/releases/download/v${version}/sharp-v${version}-napi-v9-${prebuildPlatform}.tar.gz`;
const dir = path.join(__dirname, platform);
const response = await fetch(url);
if (!response.ok) {
console.log(`Skipping ${platform}: ${response.statusText}`);
return;
}
// Extract prebuild tarball
const lib = path.join(dir, 'lib');
await rm(lib, { force: true, recursive: true });
await pipeline(
Readable.fromWeb(response.body),
createGunzip(),
extract(lib, { map: mapTarballEntry })
);
// Generate README
const { name, description } = require(`./${platform}/package.json`);
await writeFile(path.join(dir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`);
// Copy Apache-2.0 LICENSE
await copyFile(path.join(__dirname, '..', 'LICENSE'), path.join(dir, 'LICENSE'));
// Copy files for packages without an explicit sharp-libvips dependency (Windows, wasm)
if (platform.startsWith('win') || platform.startsWith('wasm')) {
const libvipsPlatform = platform === 'wasm32' ? 'dev-wasm32' : platform;
const sharpLibvipsDir = path.join(require(`@img/sharp-libvips-${libvipsPlatform}/lib`), '..');
// Copy versions.json
await copyFile(path.join(sharpLibvipsDir, 'versions.json'), path.join(dir, 'versions.json'));
// Append third party licensing to README
const readme = await readFile(path.join(sharpLibvipsDir, 'README.md'), { encoding: 'utf-8' });
const thirdParty = readme.substring(readme.indexOf('\nThis software contains'));
appendFile(path.join(dir, 'README.md'), thirdParty);
}
});

View File

@@ -3,24 +3,62 @@
'use strict'; 'use strict';
// Populate contents of a single npm/sharpen-sharp-<build-platform> package // Populate the npm package for the current platform with the local build
// with the local/CI build directory for local/CI prebuild testing
const fs = require('node:fs'); const { copyFileSync, cpSync, readFileSync, writeFileSync, appendFileSync } = require('node:fs');
const path = require('node:path'); const { basename, join } = require('node:path');
const { buildPlatformArch } = require('../lib/libvips'); const { buildPlatformArch } = require('../lib/libvips');
const platform = buildPlatformArch();
const dest = path.join(__dirname, platform);
// Use same config as prebuild to copy binary files const licensing = `
const release = path.join(__dirname, '..', 'src', 'build', 'Release'); ## Licensing
const prebuildrc = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '.prebuildrc'), 'utf8'));
const include = new RegExp(prebuildrc['include-regex'], 'i'); Copyright 2013 Lovell Fuller and others.
fs.cpSync(release, path.join(dest, 'lib'), {
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
`;
const platform = buildPlatformArch();
const destDir = join(__dirname, platform);
console.log(`Populating npm package for platform: ${platform}`);
// Copy binaries
const releaseDir = join(__dirname, '..', 'src', 'build', 'Release');
const libDir = join(destDir, 'lib');
cpSync(releaseDir, libDir, {
recursive: true, recursive: true,
filter: (file) => { filter: (file) => {
const name = path.basename(file); const name = basename(file);
return name === 'Release' || include.test(name); return name === 'Release' ||
(name.startsWith('sharp-') && name.includes('.node')) ||
(name.startsWith('libvips-') && name.endsWith('.dll'));
} }
}); });
// Generate README
const { name, description } = require(`./${platform}/package.json`);
writeFileSync(join(destDir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`);
// Copy Apache-2.0 LICENSE
copyFileSync(join(__dirname, '..', 'LICENSE'), join(destDir, 'LICENSE'));
// Copy files for packages without an explicit sharp-libvips dependency (Windows, wasm)
if (platform.startsWith('win') || platform.startsWith('wasm')) {
const libvipsPlatform = platform === 'wasm32' ? 'dev-wasm32' : platform;
const sharpLibvipsDir = join(require(`@img/sharp-libvips-${libvipsPlatform}/lib`), '..');
// Copy versions.json
copyFileSync(join(sharpLibvipsDir, 'versions.json'), join(destDir, 'versions.json'));
// Append third party licensing to README
const readme = readFileSync(join(sharpLibvipsDir, 'README.md'), { encoding: 'utf-8' });
const thirdParty = readme.substring(readme.indexOf('\nThis software contains'));
appendFileSync(join(destDir, 'README.md'), thirdParty);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp", "name": "@img/sharp",
"version": "0.34.0", "version": "0.34.3-rc.0",
"private": "true", "private": "true",
"workspaces": [ "workspaces": [
"darwin-arm64", "darwin-arm64",
@@ -13,6 +13,7 @@
"linuxmusl-arm64", "linuxmusl-arm64",
"linuxmusl-x64", "linuxmusl-x64",
"wasm32", "wasm32",
"win32-arm64",
"win32-ia32", "win32-ia32",
"win32-x64" "win32-x64"
] ]

View File

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

View File

@@ -0,0 +1,39 @@
{
"name": "@img/sharp-win32-arm64",
"version": "0.34.3-rc.0",
"description": "Prebuilt sharp for use with Windows 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",
"repository": {
"type": "git",
"url": "git+https://github.com/lovell/sharp.git",
"directory": "npm/win32-arm64"
},
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"funding": {
"url": "https://opencollective.com/libvips"
},
"preferUnplugged": true,
"files": [
"lib",
"versions.json"
],
"publishConfig": {
"access": "public"
},
"type": "commonjs",
"exports": {
"./sharp.node": "./lib/sharp-win32-arm64.node",
"./package": "./package.json",
"./versions": "./versions.json"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"os": [
"win32"
],
"cpu": [
"arm64"
]
}

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
{ {
"name": "sharp", "name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.34.0", "version": "0.34.3-rc.0",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
"contributors": [ "contributors": [
@@ -92,7 +92,7 @@
"Don Denton <don@happycollision.com>" "Don Denton <don@happycollision.com>"
], ],
"scripts": { "scripts": {
"install": "node install/check", "install": "node install/check.js",
"clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*", "clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*",
"test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types", "test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types",
"test-lint": "semistandard && cpplint", "test-lint": "semistandard && cpplint",
@@ -100,8 +100,7 @@
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"", "test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"",
"test-leak": "./test/leak/leak.sh", "test-leak": "./test/leak/leak.sh",
"test-types": "tsd", "test-types": "tsd",
"package-from-local-build": "node npm/from-local-build", "package-from-local-build": "node npm/from-local-build.js",
"package-from-github-release": "node npm/from-github-release",
"docs-build": "node docs/build.mjs", "docs-build": "node docs/build.mjs",
"docs-serve": "cd docs && npm start", "docs-serve": "cd docs && npm start",
"docs-publish": "cd docs && npm run build && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp" "docs-publish": "cd docs && npm run build && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
@@ -138,68 +137,66 @@
], ],
"dependencies": { "dependencies": {
"color": "^4.2.3", "color": "^4.2.3",
"detect-libc": "^2.0.3", "detect-libc": "^2.0.4",
"semver": "^7.7.1" "semver": "^7.7.2"
}, },
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-darwin-arm64": "0.34.0", "@img/sharp-darwin-arm64": "0.34.3-rc.0",
"@img/sharp-darwin-x64": "0.34.0", "@img/sharp-darwin-x64": "0.34.3-rc.0",
"@img/sharp-libvips-darwin-arm64": "1.1.0", "@img/sharp-libvips-darwin-arm64": "1.2.0-rc.2",
"@img/sharp-libvips-darwin-x64": "1.1.0", "@img/sharp-libvips-darwin-x64": "1.2.0-rc.2",
"@img/sharp-libvips-linux-arm": "1.1.0", "@img/sharp-libvips-linux-arm": "1.2.0-rc.2",
"@img/sharp-libvips-linux-arm64": "1.1.0", "@img/sharp-libvips-linux-arm64": "1.2.0-rc.2",
"@img/sharp-libvips-linux-ppc64": "1.1.0", "@img/sharp-libvips-linux-ppc64": "1.2.0-rc.2",
"@img/sharp-libvips-linux-s390x": "1.1.0", "@img/sharp-libvips-linux-s390x": "1.2.0-rc.2",
"@img/sharp-libvips-linux-x64": "1.1.0", "@img/sharp-libvips-linux-x64": "1.2.0-rc.2",
"@img/sharp-libvips-linuxmusl-arm64": "1.1.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0-rc.2",
"@img/sharp-libvips-linuxmusl-x64": "1.1.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0-rc.2",
"@img/sharp-linux-arm": "0.34.0", "@img/sharp-linux-arm": "0.34.3-rc.0",
"@img/sharp-linux-arm64": "0.34.0", "@img/sharp-linux-arm64": "0.34.3-rc.0",
"@img/sharp-linux-s390x": "0.34.0", "@img/sharp-linux-ppc64": "0.34.3-rc.0",
"@img/sharp-linux-x64": "0.34.0", "@img/sharp-linux-s390x": "0.34.3-rc.0",
"@img/sharp-linuxmusl-arm64": "0.34.0", "@img/sharp-linux-x64": "0.34.3-rc.0",
"@img/sharp-linuxmusl-x64": "0.34.0", "@img/sharp-linuxmusl-arm64": "0.34.3-rc.0",
"@img/sharp-wasm32": "0.34.0", "@img/sharp-linuxmusl-x64": "0.34.3-rc.0",
"@img/sharp-win32-ia32": "0.34.0", "@img/sharp-wasm32": "0.34.3-rc.0",
"@img/sharp-win32-x64": "0.34.0" "@img/sharp-win32-arm64": "0.34.3-rc.0",
"@img/sharp-win32-ia32": "0.34.3-rc.0",
"@img/sharp-win32-x64": "0.34.3-rc.0"
}, },
"devDependencies": { "devDependencies": {
"@emnapi/runtime": "^1.4.0", "@emnapi/runtime": "^1.4.3",
"@img/sharp-libvips-dev": "1.1.0", "@img/sharp-libvips-dev": "1.2.0-rc.2",
"@img/sharp-libvips-dev-wasm32": "1.1.0", "@img/sharp-libvips-dev-wasm32": "1.2.0-rc.2",
"@img/sharp-libvips-win32-ia32": "1.1.0", "@img/sharp-libvips-win32-arm64": "1.2.0-rc.2",
"@img/sharp-libvips-win32-x64": "1.1.0", "@img/sharp-libvips-win32-ia32": "1.2.0-rc.2",
"@img/sharp-libvips-win32-x64": "1.2.0-rc.2",
"@types/node": "*", "@types/node": "*",
"cc": "^3.0.1", "cc": "^3.0.1",
"emnapi": "^1.4.0", "emnapi": "^1.4.3",
"exif-reader": "^2.0.2", "exif-reader": "^2.0.2",
"extract-zip": "^2.0.1", "extract-zip": "^2.0.1",
"icc": "^3.0.0", "icc": "^3.0.0",
"jsdoc-to-markdown": "^9.1.1", "jsdoc-to-markdown": "^9.1.1",
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
"mocha": "^11.1.0", "mocha": "^11.6.0",
"node-addon-api": "^8.3.1", "node-addon-api": "^8.3.1",
"node-gyp": "^11.2.0",
"nyc": "^17.1.0", "nyc": "^17.1.0",
"prebuild": "^13.0.1",
"semistandard": "^17.0.0", "semistandard": "^17.0.0",
"tar-fs": "^3.0.8", "tar-fs": "^3.0.9",
"tsd": "^0.31.2" "tsd": "^0.32.0"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
"config": { "config": {
"libvips": ">=8.16.1" "libvips": ">=8.17.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
}, },
"binary": {
"napi_versions": [
9
]
},
"semistandard": { "semistandard": {
"env": [ "env": [
"mocha" "mocha"

View File

@@ -19,7 +19,10 @@
'type': 'shared_library', 'type': 'shared_library',
'defines': [ 'defines': [
'_VIPS_PUBLIC=__declspec(dllexport)', '_VIPS_PUBLIC=__declspec(dllexport)',
'_ALLOW_KEYWORD_MACROS' '_ALLOW_KEYWORD_MACROS',
'G_DISABLE_ASSERT',
'G_DISABLE_CAST_CHECKS',
'G_DISABLE_CHECKS'
], ],
'sources': [ 'sources': [
'<(sharp_libvips_cplusplus_dir)/VConnection.cpp', '<(sharp_libvips_cplusplus_dir)/VConnection.cpp',
@@ -160,6 +163,8 @@
}, },
'xcode_settings': { 'xcode_settings': {
'OTHER_LDFLAGS': [ 'OTHER_LDFLAGS': [
'-Wl,-s',
'-Wl,-dead_strip',
# Ensure runtime linking is relative to sharp.node # Ensure runtime linking is relative to sharp.node
'-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'', '-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath,\'@loader_path/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'', '-Wl,-rpath,\'@loader_path/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'',
@@ -173,6 +178,9 @@
'defines': [ 'defines': [
'_GLIBCXX_USE_CXX11_ABI=1' '_GLIBCXX_USE_CXX11_ABI=1'
], ],
'cflags_cc': [
'<!(node -p "require(\'detect-libc\').isNonGlibcLinuxSync() ? \'\' : \'-flto=auto\'")'
],
'link_settings': { 'link_settings': {
'libraries': [ 'libraries': [
'-l:libvips-cpp.so.<(vips_version)' '-l:libvips-cpp.so.<(vips_version)'

View File

@@ -113,6 +113,10 @@ namespace sharp {
if (HasAttr(input, "pdfBackground")) { if (HasAttr(input, "pdfBackground")) {
descriptor->pdfBackground = AttrAsVectorOfDouble(input, "pdfBackground"); descriptor->pdfBackground = AttrAsVectorOfDouble(input, "pdfBackground");
} }
// Use JPEG 2000 oneshot mode?
if (HasAttr(input, "jp2Oneshot")) {
descriptor->jp2Oneshot = AttrAsBool(input, "jp2Oneshot");
}
// Create new image // Create new image
if (HasAttr(input, "createChannels")) { if (HasAttr(input, "createChannels")) {
descriptor->createChannels = AttrAsUint32(input, "createChannels"); descriptor->createChannels = AttrAsUint32(input, "createChannels");
@@ -434,6 +438,9 @@ namespace sharp {
if (imageType == ImageType::PDF) { if (imageType == ImageType::PDF) {
option->set("background", descriptor->pdfBackground); option->set("background", descriptor->pdfBackground);
} }
if (imageType == ImageType::JP2) {
option->set("oneshot", descriptor->jp2Oneshot);
}
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option); image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) { if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
image = SetDensity(image, descriptor->density); image = SetDensity(image, descriptor->density);
@@ -541,6 +548,9 @@ namespace sharp {
if (imageType == ImageType::PDF) { if (imageType == ImageType::PDF) {
option->set("background", descriptor->pdfBackground); option->set("background", descriptor->pdfBackground);
} }
if (imageType == ImageType::JP2) {
option->set("oneshot", descriptor->jp2Oneshot);
}
image = VImage::new_from_file(descriptor->file.data(), option); image = VImage::new_from_file(descriptor->file.data(), option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) { if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
image = SetDensity(image, descriptor->density); image = SetDensity(image, descriptor->density);
@@ -651,22 +661,21 @@ namespace sharp {
*/ */
VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop) { VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop) {
bool hasDelay = !delay.empty(); bool hasDelay = !delay.empty();
// Avoid a copy if none of the animation properties are needed.
if (nPages == 1 && !hasDelay && loop == -1) return image;
if (delay.size() == 1) {
// We have just one delay, repeat that value for all frames.
delay.insert(delay.end(), nPages - 1, delay[0]);
}
// Attaching metadata, need to copy the image.
VImage copy = image.copy(); VImage copy = image.copy();
// Only set page-height if we have more than one page, or this could // Only set page-height if we have more than one page, or this could
// accidentally turn into an animated image later. // accidentally turn into an animated image later.
if (nPages > 1) copy.set(VIPS_META_PAGE_HEIGHT, pageHeight); if (nPages > 1) copy.set(VIPS_META_PAGE_HEIGHT, pageHeight);
if (hasDelay) copy.set("delay", delay); if (hasDelay) {
if (delay.size() == 1) {
// We have just one delay, repeat that value for all frames.
delay.insert(delay.end(), nPages - 1, delay[0]);
}
copy.set("delay", delay);
}
if (nPages == 1 && !hasDelay && loop == -1) {
loop = 1;
}
if (loop != -1) copy.set("loop", loop); if (loop != -1) copy.set("loop", loop);
return copy; return copy;
@@ -952,14 +961,6 @@ namespace sharp {
return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16; return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
} }
/*
Return the image alpha maximum. Useful for combining alpha bands. scRGB
images are 0 - 1 for image data, but the alpha is 0 - 255.
*/
double MaximumImageAlpha(VipsInterpretation const interpretation) {
return Is16Bit(interpretation) ? 65535.0 : 255.0;
}
/* /*
Convert RGBA value to another colourspace Convert RGBA value to another colourspace
*/ */
@@ -1002,16 +1003,16 @@ namespace sharp {
0.0722 * colour[2]) 0.0722 * colour[2])
}; };
} }
// Add alpha channel to alphaColour colour // Add alpha channel(s) to alphaColour colour
if (colour[3] < 255.0 || image.has_alpha()) { if (colour[3] < 255.0 || image.has_alpha()) {
alphaColour.push_back(colour[3] * multiplier); int extraBands = image.bands() > 4 ? image.bands() - 3 : 1;
alphaColour.insert(alphaColour.end(), extraBands, colour[3] * multiplier);
} }
// Ensure alphaColour colour uses correct colourspace // Ensure alphaColour colour uses correct colourspace
alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation(), premultiply); alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation(), premultiply);
// Add non-transparent alpha channel, if required // Add non-transparent alpha channel, if required
if (colour[3] < 255.0 && !image.has_alpha()) { if (colour[3] < 255.0 && !image.has_alpha()) {
image = image.bandjoin( image = image.bandjoin_const({ 255 * multiplier });
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier).cast(image.format()));
} }
return std::make_tuple(image, alphaColour); return std::make_tuple(image, alphaColour);
} }
@@ -1031,9 +1032,7 @@ namespace sharp {
*/ */
VImage EnsureAlpha(VImage image, double const value) { VImage EnsureAlpha(VImage image, double const value) {
if (!image.has_alpha()) { if (!image.has_alpha()) {
std::vector<double> alpha; image = image.bandjoin_const({ value * vips_interpretation_max_alpha(image.interpretation()) });
alpha.push_back(value * sharp::MaximumImageAlpha(image.interpretation()));
image = image.bandjoin_const(alpha);
} }
return image; return image;
} }

View File

@@ -15,9 +15,9 @@
// Verify platform and compiler compatibility // Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8) || \ #if (VIPS_MAJOR_VERSION < 8) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 16) || \ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 17) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 16 && VIPS_MICRO_VERSION < 1) (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 17 && VIPS_MICRO_VERSION < 0)
#error "libvips version 8.16.1+ is required - please see https://sharp.pixelplumbing.com/install" #error "libvips version 8.17.0+ is required - please see https://sharp.pixelplumbing.com/install"
#endif #endif
#if defined(__has_include) #if defined(__has_include)
@@ -78,6 +78,7 @@ namespace sharp {
VipsAlign joinHalign; VipsAlign joinHalign;
VipsAlign joinValign; VipsAlign joinValign;
std::vector<double> pdfBackground; std::vector<double> pdfBackground;
bool jp2Oneshot;
InputDescriptor(): InputDescriptor():
autoOrient(false), autoOrient(false),
@@ -120,7 +121,8 @@ namespace sharp {
joinBackground{ 0.0, 0.0, 0.0, 255.0 }, joinBackground{ 0.0, 0.0, 0.0, 255.0 },
joinHalign(VIPS_ALIGN_LOW), joinHalign(VIPS_ALIGN_LOW),
joinValign(VIPS_ALIGN_LOW), joinValign(VIPS_ALIGN_LOW),
pdfBackground{ 255.0, 255.0, 255.0, 255.0 } {} pdfBackground{ 255.0, 255.0, 255.0, 255.0 },
jp2Oneshot(false) {}
}; };
// Convenience methods to access the attributes of a Napi::Object // Convenience methods to access the attributes of a Napi::Object
@@ -357,12 +359,6 @@ namespace sharp {
*/ */
bool Is16Bit(VipsInterpretation const interpretation); bool Is16Bit(VipsInterpretation const interpretation);
/*
Return the image alpha maximum. Useful for combining alpha bands. scRGB
images are 0 - 1 for image data, but the alpha is 0 - 255.
*/
double MaximumImageAlpha(VipsInterpretation const interpretation);
/* /*
Convert RGBA value to another colourspace Convert RGBA value to another colourspace
*/ */

View File

@@ -294,6 +294,7 @@ class PipelineWorker : public Napi::AsyncWorker {
option->set("n", baton->input->pages); option->set("n", baton->input->pages);
option->set("page", baton->input->page); option->set("page", baton->input->page);
option->set("dpi", baton->input->density); option->set("dpi", baton->input->density);
option->set("background", baton->input->pdfBackground);
if (baton->input->buffer != nullptr) { if (baton->input->buffer != nullptr) {
// Reload PDF buffer // Reload PDF buffer
@@ -668,7 +669,6 @@ class PipelineWorker : public Napi::AsyncWorker {
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN; sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
composite->input->access = access; composite->input->access = access;
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input); std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline);
if (composite->input->autoOrient) { if (composite->input->autoOrient) {
// Respect EXIF Orientation // Respect EXIF Orientation
@@ -733,8 +733,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// gravity was used for extract_area, set it back to its default value of 0 // gravity was used for extract_area, set it back to its default value of 0
composite->gravity = 0; composite->gravity = 0;
} }
// Ensure image to composite is sRGB with unpremultiplied alpha // Ensure image to composite is with unpremultiplied alpha
compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
compositeImage = sharp::EnsureAlpha(compositeImage, 1); compositeImage = sharp::EnsureAlpha(compositeImage, 1);
if (composite->premultiplied) compositeImage = compositeImage.unpremultiply(); if (composite->premultiplied) compositeImage = compositeImage.unpremultiply();
// Calculate position // Calculate position
@@ -759,7 +758,12 @@ class PipelineWorker : public Napi::AsyncWorker {
xs.push_back(left); xs.push_back(left);
ys.push_back(top); ys.push_back(top);
} }
image = VImage::composite(images, modes, VImage::option()->set("x", xs)->set("y", ys)); image = VImage::composite(images, modes, VImage::option()
->set("compositing_space", baton->colourspacePipeline == VIPS_INTERPRETATION_LAST
? VIPS_INTERPRETATION_sRGB
: baton->colourspacePipeline)
->set("x", xs)
->set("y", ys));
image = sharp::RemoveGifPalette(image); image = sharp::RemoveGifPalette(image);
} }
@@ -1359,7 +1363,8 @@ class PipelineWorker : public Napi::AsyncWorker {
// Add file size to info // Add file size to info
if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) { if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
try { try {
uint32_t const size = static_cast<uint32_t>(std::filesystem::file_size(baton->fileOut)); uint32_t const size = static_cast<uint32_t>(
std::filesystem::file_size(std::filesystem::u8path(baton->fileOut)));
info.Set("size", size); info.Set("size", size);
} catch (...) {} } catch (...) {}
} }

View File

@@ -384,7 +384,7 @@ struct PipelineBaton {
ensureAlpha(-1.0), ensureAlpha(-1.0),
colourspacePipeline(VIPS_INTERPRETATION_LAST), colourspacePipeline(VIPS_INTERPRETATION_LAST),
colourspace(VIPS_INTERPRETATION_LAST), colourspace(VIPS_INTERPRETATION_LAST),
loop(1), loop(-1),
tileSize(256), tileSize(256),
tileOverlap(0), tileOverlap(0),
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS), tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),

View File

@@ -18,8 +18,10 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
vips_init("sharp"); vips_init("sharp");
}); });
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING), for (auto domain : { "VIPS", "vips2tiff" }) {
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr); g_log_set_handler(domain, static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
}
// Methods available to JavaScript // Methods available to JavaScript
exports.Set("metadata", Napi::Function::New(env, metadata)); exports.Set("metadata", Napi::Function::New(env, metadata));

View File

@@ -60,7 +60,7 @@ class StatsWorker : public Napi::AsyncWorker {
// Image is not opaque when alpha layer is present and contains a non-mamixa value // Image is not opaque when alpha layer is present and contains a non-mamixa value
if (image.has_alpha()) { if (image.has_alpha()) {
double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front()); double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
if (minAlpha != sharp::MaximumImageAlpha(image.interpretation())) { if (minAlpha != vips_interpretation_max_alpha(image.interpretation())) {
baton->isOpaque = false; baton->isOpaque = false;
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

View File

@@ -117,6 +117,7 @@ module.exports = {
inputTiffFogra: getPath('fogra-0-100-100-0.tif'), // https://github.com/lovell/sharp/issues/4045 inputTiffFogra: getPath('fogra-0-100-100-0.tif'), // https://github.com/lovell/sharp/issues/4045
inputJp2: getPath('relax.jp2'), // https://www.fnordware.com/j2k/relax.jp2 inputJp2: getPath('relax.jp2'), // https://www.fnordware.com/j2k/relax.jp2
inputJp2TileParts: getPath('relax_tileparts.jp2'), // kdu_expand -i relax.jp2 -o relax-tmp.tif ; kdu_compress -i relax-tmp.tif -o relax_tileparts.jp2 -jp2_space sRGB Clayers=8 -rate 1.0,0.04 Stiles='{128,128}' ORGtparts=L ; rm relax-tmp.tif
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
inputGifAnimated: getPath('rotating-squares.gif'), // CC0 https://loading.io/spinner/blocks/-rotating-squares-preloader-gif inputGifAnimated: getPath('rotating-squares.gif'), // CC0 https://loading.io/spinner/blocks/-rotating-squares-preloader-gif

BIN
test/fixtures/relax_tileparts.jp2 vendored Normal file

Binary file not shown.

View File

@@ -721,6 +721,9 @@ const color: sharp.Color = '#fff';
sharp({ pdfBackground: colour }); sharp({ pdfBackground: colour });
sharp({ pdfBackground: color }); sharp({ pdfBackground: color });
sharp({ jp2Oneshot: true });
sharp({ jp2Oneshot: false });
sharp({ autoOrient: true }); sharp({ autoOrient: true });
sharp({ autoOrient: false }); sharp({ autoOrient: false });
sharp().autoOrient(); sharp().autoOrient();

View File

@@ -122,6 +122,26 @@ describe('composite', () => {
}); });
}); });
it('scrgb pipeline', () => {
const filename = 'composite-red-scrgb.png';
const actual = fixtures.path(`output.${filename}`);
const expected = fixtures.expected(filename);
return sharp({
create: {
width: 32, height: 32, channels: 4, background: red
}
})
.pipelineColourspace('scrgb')
.composite([{
input: fixtures.inputPngWithTransparency16bit,
blend: 'color-burn'
}])
.toFile(actual)
.then(() => {
fixtures.assertMaxColourDistance(actual, expected);
});
});
it('multiple', async () => { it('multiple', async () => {
const filename = 'composite-multiple.png'; const filename = 'composite-multiple.png';
const actual = fixtures.path(`output.${filename}`); const actual = fixtures.path(`output.${filename}`);

View File

@@ -238,4 +238,15 @@ describe('GIF input', () => {
assert.strictEqual(1, loop); assert.strictEqual(1, loop);
} }
}); });
it('Animated GIF to animated WebP merges identical frames', async () => {
const webp = await sharp(fixtures.inputGifAnimated, { animated: true })
.webp()
.toBuffer();
const { delay, loop, pages } = await sharp(webp).metadata();
assert.deepStrictEqual([120, 120, 90, 120, 120, 90, 120, 90, 30], delay);
assert.strictEqual(0, loop);
assert.strictEqual(9, pages);
});
}); });

View File

@@ -1036,4 +1036,21 @@ describe('Input/output', function () {
}); });
readable.pipe(inPipeline).pipe(badPipeline); readable.pipe(inPipeline).pipe(badPipeline);
}); });
it('supports wide-character filenames', async () => {
const filename = fixtures.path('output.图片.jpg');
const create = {
width: 8,
height: 8,
channels: 3,
background: 'green'
};
await sharp({ create }).toFile(filename);
const { width, height, channels, format } = await sharp(filename).metadata();
assert.strictEqual(width, 8);
assert.strictEqual(height, 8);
assert.strictEqual(channels, 3);
assert.strictEqual(format, 'jpeg');
});
}); });

View File

@@ -93,10 +93,38 @@ describe('JP2 output', () => {
}); });
}); });
it('Invalid JP2 chromaSubsampling value throws error', function () { it('can use the jp2Oneshot option to handle multi-part tiled JPEG 2000 file', async () => {
assert.throws(function () { const outputJpg = fixtures.path('output.jpg');
sharp().jpeg({ chromaSubsampling: '4:2:2' }); await assert.rejects(
() => sharp(fixtures.inputJp2TileParts).toFile(outputJpg)
);
await assert.doesNotReject(async () => {
await sharp(fixtures.inputJp2TileParts, { jp2Oneshot: true }).toFile(outputJpg);
const { format, width, height } = await sharp(outputJpg).metadata();
assert.strictEqual(format, 'jpeg');
assert.strictEqual(width, 320);
assert.strictEqual(height, 240);
}); });
}); });
it('Invalid JP2 chromaSubsampling value throws error', () => {
assert.throws(
() => sharp().jp2({ chromaSubsampling: '4:2:2' }),
/Expected one of 4:2:0, 4:4:4 but received 4:2:2 of type string/
);
});
} }
it('valid JP2 oneshot value does not throw error', () => {
assert.doesNotThrow(
() => sharp(fixtures.inputJp2TileParts, { jp2Oneshot: true })
);
});
it('invalid JP2 oneshot value throws error', () => {
assert.throws(
() => sharp(fixtures.inputJp2TileParts, { jp2Oneshot: 'fail' }),
/Expected boolean for jp2Oneshot but received fail of type string/
);
});
}); });

View File

@@ -179,7 +179,7 @@ describe('libvips binaries', function () {
process.env.npm_config_arch = 's390x'; process.env.npm_config_arch = 's390x';
process.env.npm_config_libc = ''; process.env.npm_config_libc = '';
const locatorHash = libvips.yarnLocator(); const locatorHash = libvips.yarnLocator();
assert.strictEqual(locatorHash, '9b2ea457de'); assert.strictEqual(locatorHash, 'e23686d7dd');
delete process.env.npm_config_platform; delete process.env.npm_config_platform;
delete process.env.npm_config_arch; delete process.env.npm_config_arch;
delete process.env.npm_config_libc; delete process.env.npm_config_libc;

View File

@@ -806,4 +806,33 @@ describe('Resize fit=contain', function () {
fixtures.assertSimilar(fixtures.expected('./embedgravitybird/9-c.png'), data, done); fixtures.assertSimilar(fixtures.expected('./embedgravitybird/9-c.png'), data, done);
}); });
}); });
it('multiple alpha channels', async () => {
const create = {
width: 20,
height: 12,
channels: 4,
background: 'green'
};
const multipleAlphaChannels = await sharp({ create })
.joinChannel({ create })
.tiff({ compression: 'deflate' })
.toBuffer();
const data = await sharp(multipleAlphaChannels)
.resize({
width: 8,
height: 8,
fit: 'contain',
background: 'blue'
})
.tiff({ compression: 'deflate' })
.toBuffer();
const { format, width, height, space, channels } = await sharp(data).metadata();
assert.deepStrictEqual(format, 'tiff');
assert.deepStrictEqual(width, 8);
assert.deepStrictEqual(height, 8);
assert.deepStrictEqual(space, 'srgb');
assert.deepStrictEqual(channels, 8);
});
}); });

View File

@@ -59,7 +59,7 @@ describe('Text to image', function () {
assert.strictEqual('png', info.format); assert.strictEqual('png', info.format);
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
assert.ok(inRange(info.width, 400, 600), `Actual width ${info.width}`); assert.ok(inRange(info.width, 400, 600), `Actual width ${info.width}`);
assert.ok(inRange(info.height, 300, 500), `Actual height ${info.height}`); assert.ok(inRange(info.height, 290, 500), `Actual height ${info.height}`);
assert.ok(inRange(info.textAutofitDpi, 900, 1300), `Actual textAutofitDpi ${info.textAutofitDpi}`); assert.ok(inRange(info.textAutofitDpi, 900, 1300), `Actual textAutofitDpi ${info.textAutofitDpi}`);
done(); done();
}); });

View File

@@ -10,18 +10,20 @@ const sharp = require('../../');
describe('Utilities', function () { describe('Utilities', function () {
describe('Cache', function () { describe('Cache', function () {
it('Can be disabled', function (done) { it('Can be disabled', function (done) {
queueMicrotask(() => { const check = setInterval(() => {
sharp.cache(false);
const cache = sharp.cache(false); const cache = sharp.cache(false);
assert.strictEqual(cache.memory.current, 0); const empty =
assert.strictEqual(cache.memory.max, 0); cache.memory.current +
assert.strictEqual(typeof cache.memory.high, 'number'); cache.memory.max +
assert.strictEqual(cache.files.current, 0); cache.files.current +
assert.strictEqual(cache.files.max, 0); cache.files.max +
assert.strictEqual(cache.items.current, 0); cache.items.current +
assert.strictEqual(cache.items.max, 0); cache.items.max === 0;
done(); if (empty) {
}); clearInterval(check);
done();
}
}, 2000);
}); });
it('Can be enabled with defaults', function () { it('Can be enabled with defaults', function () {
const cache = sharp.cache(true); const cache = sharp.cache(true);