mirror of
https://github.com/lovell/sharp.git
synced 2026-02-11 09:06:15 +01:00
Compare commits
10 Commits
main
...
3278a9a913
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3278a9a913 | ||
|
|
1b2f79335d | ||
|
|
937167933b | ||
|
|
dbcb7e60bd | ||
|
|
e1bad5470e | ||
|
|
1a2c1c8833 | ||
|
|
aaeded2b67 | ||
|
|
f6cdd36559 | ||
|
|
283c7d3f0c | ||
|
|
34c39fa194 |
2
.github/ISSUE_TEMPLATE/installation.md
vendored
2
.github/ISSUE_TEMPLATE/installation.md
vendored
@@ -31,7 +31,7 @@ please open an issue against that package instead.
|
||||
|
||||
<!-- Please place an [x] in the relevant box to confirm. -->
|
||||
|
||||
- [ ] I am using Node.js with a version that satisfies `^18.17.0 || ^20.3.0 || >=21.0.0`
|
||||
- [ ] I am using Node.js with a version that satisfies `>=20.9.0`
|
||||
- [ ] I am using Deno
|
||||
- [ ] I am using Bun
|
||||
|
||||
|
||||
148
.github/workflows/ci.yml
vendored
148
.github/workflows/ci.yml
vendored
@@ -9,8 +9,8 @@ jobs:
|
||||
contents: read
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "24"
|
||||
- run: npm install --ignore-scripts
|
||||
@@ -31,123 +31,129 @@ jobs:
|
||||
- os: ubuntu-24.04
|
||||
container: rockylinux:8
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: linux-x64
|
||||
package: true
|
||||
- os: ubuntu-24.04
|
||||
container: rockylinux:8
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
platform: linux-x64
|
||||
- os: ubuntu-24.04
|
||||
container: rockylinux:8
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: linux-x64
|
||||
- os: ubuntu-24.04
|
||||
container: node:18-alpine3.17
|
||||
nodejs_version_major: 18
|
||||
platform: linuxmusl-x64
|
||||
package: true
|
||||
container: rockylinux:8
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: linux-x64
|
||||
- os: ubuntu-24.04
|
||||
container: node:20-alpine3.18
|
||||
nodejs_version_major: 20
|
||||
platform: linuxmusl-x64
|
||||
package: true
|
||||
- os: ubuntu-24.04
|
||||
container: node:22-alpine3.20
|
||||
nodejs_version_major: 22
|
||||
platform: linuxmusl-x64
|
||||
- os: ubuntu-24.04
|
||||
container: node:24-alpine3.22
|
||||
nodejs_version_major: 24
|
||||
platform: linuxmusl-x64
|
||||
- os: ubuntu-24.04-arm
|
||||
container: arm64v8/rockylinux:8
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: linux-arm64
|
||||
package: true
|
||||
- os: ubuntu-24.04-arm
|
||||
container: arm64v8/rockylinux:8
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: linux-arm64
|
||||
- os: ubuntu-24.04-arm
|
||||
container: arm64v8/rockylinux:8
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: linux-arm64
|
||||
- os: macos-15-intel
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: darwin-x64
|
||||
package: true
|
||||
- os: macos-15-intel
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: darwin-x64
|
||||
- os: macos-15-intel
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version_major: 22
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: darwin-x64
|
||||
- os: macos-15
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: darwin-arm64
|
||||
package: true
|
||||
- os: macos-15
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: darwin-arm64
|
||||
- os: macos-15
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version_major: 22
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: darwin-arm64
|
||||
- os: windows-2022
|
||||
nodejs_arch: x86
|
||||
nodejs_version: "18.18.2" # pinned to avoid 18.19.0 and npm 10
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: win32-ia32
|
||||
package: true
|
||||
- os: windows-2022
|
||||
nodejs_arch: x86
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
platform: win32-ia32
|
||||
- os: windows-2022
|
||||
nodejs_arch: x86
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version: "22.21.0"
|
||||
nodejs_version_major: 22
|
||||
platform: win32-ia32
|
||||
- os: windows-2022
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: win32-x64
|
||||
package: true
|
||||
- os: windows-2022
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version_major: 20
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: win32-x64
|
||||
- os: windows-2022
|
||||
nodejs_arch: x64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version_major: 22
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: win32-x64
|
||||
- os: windows-11-arm
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^20.3.0"
|
||||
nodejs_version: "^20.9.0"
|
||||
nodejs_version_major: 20
|
||||
platform: win32-arm64
|
||||
package: true
|
||||
- os: windows-11-arm
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^22.9.0"
|
||||
nodejs_version: "^22"
|
||||
nodejs_version_major: 22
|
||||
platform: win32-arm64
|
||||
- os: windows-11-arm
|
||||
nodejs_arch: arm64
|
||||
nodejs_version: "^24"
|
||||
nodejs_version_major: 24
|
||||
platform: win32-arm64
|
||||
steps:
|
||||
- name: Dependencies (Rocky Linux glibc)
|
||||
if: contains(matrix.container, 'rockylinux')
|
||||
@@ -159,22 +165,22 @@ jobs:
|
||||
run: apk add build-base git python3 font-noto --update-cache
|
||||
- name: Dependencies (Python 3.11 - macOS, Windows)
|
||||
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Dependencies (Node.js)
|
||||
if: "!contains(matrix.platform, 'linuxmusl')"
|
||||
uses: actions/setup-node@v5
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ matrix.nodejs_version }}
|
||||
architecture: ${{ matrix.nodejs_arch }}
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- run: npm run test-unit
|
||||
- if: matrix.package
|
||||
run: npm run package-from-local-build
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: matrix.package
|
||||
with:
|
||||
name: ${{ matrix.platform }}
|
||||
@@ -191,16 +197,18 @@ jobs:
|
||||
image: ${{ matrix.container }}
|
||||
volumes:
|
||||
- /opt:/opt:rw,rshared
|
||||
- /opt:/__e/node20:ro,rshared
|
||||
- /opt:/__e/node24:ro,rshared
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- container: node:18-alpine3.17
|
||||
nodejs_version_major: 18
|
||||
package: true
|
||||
- container: node:20-alpine3.18
|
||||
- container: node:20-alpine3.20
|
||||
nodejs_version_major: 20
|
||||
package: true
|
||||
- container: node:22-alpine3.20
|
||||
nodejs_version_major: 22
|
||||
- container: node:24-alpine3.22
|
||||
nodejs_version_major: 24
|
||||
steps:
|
||||
- name: Allow Linux musl containers on ARM64 runners # https://github.com/actions/runner/issues/801#issuecomment-2394425757
|
||||
shell: sh
|
||||
@@ -211,13 +219,13 @@ jobs:
|
||||
ln -s /usr/bin/node /opt/bin/node
|
||||
- name: Dependencies
|
||||
run: apk add build-base git python3 font-noto --update-cache
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- run: npm run test-unit
|
||||
- if: matrix.package
|
||||
run: npm run package-from-local-build
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: matrix.package
|
||||
with:
|
||||
name: linuxmusl-arm64
|
||||
@@ -238,20 +246,20 @@ jobs:
|
||||
base_image: "balenalib/rpi-raspbian:bullseye"
|
||||
nodejs_arch: armv6l
|
||||
nodejs_hostname: unofficial-builds.nodejs.org
|
||||
nodejs_version: "18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "20.9.0"
|
||||
nodejs_version_major: 20
|
||||
- platform: linux-s390x
|
||||
base_image: "--platform=linux/s390x s390x/debian:bookworm"
|
||||
nodejs_arch: s390x
|
||||
nodejs_hostname: nodejs.org
|
||||
nodejs_version: "18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "20.9.0"
|
||||
nodejs_version_major: 20
|
||||
- platform: linux-ppc64
|
||||
base_image: "--platform=linux/ppc64le ppc64le/debian:bookworm"
|
||||
nodejs_arch: ppc64le
|
||||
nodejs_hostname: nodejs.org
|
||||
nodejs_version: "18.17.0"
|
||||
nodejs_version_major: 18
|
||||
nodejs_version: "20.9.0"
|
||||
nodejs_version_major: 20
|
||||
- platform: linux-riscv64
|
||||
base_image: "--platform=linux/riscv64 riscv64/debian:trixie"
|
||||
compiler_flags: "-march=rv64gc"
|
||||
@@ -260,7 +268,7 @@ jobs:
|
||||
nodejs_version: "20.19.5"
|
||||
nodejs_version_major: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: uraimo/run-on-arch-action@v3
|
||||
with:
|
||||
arch: none
|
||||
@@ -279,7 +287,7 @@ jobs:
|
||||
npm run build
|
||||
node --test test/unit/io.js
|
||||
npm run package-from-local-build
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ matrix.platform }}
|
||||
path: npm/${{ matrix.platform }}
|
||||
@@ -291,7 +299,7 @@ jobs:
|
||||
needs: lint
|
||||
name: "build-wasm32 [package]"
|
||||
runs-on: ubuntu-24.04
|
||||
container: "emscripten/emsdk:4.0.18"
|
||||
container: "emscripten/emsdk:4.0.21"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Dependencies
|
||||
@@ -311,7 +319,7 @@ jobs:
|
||||
test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP"
|
||||
- run: emmake npm run test-unit
|
||||
- run: emmake npm run package-from-local-build
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: wasm32
|
||||
path: npm/wasm32
|
||||
@@ -329,12 +337,12 @@ jobs:
|
||||
- build-emscripten
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v7
|
||||
with:
|
||||
path: npm
|
||||
- name: Create npm workspace tarball
|
||||
run: tar -vcaf npm-workspace.tar.xz --directory npm --exclude=from-local-build.js .
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
- name: Create release notes
|
||||
|
||||
2
.github/workflows/npm.yml
vendored
2
.github/workflows/npm.yml
vendored
@@ -96,7 +96,7 @@ jobs:
|
||||
steps:
|
||||
- name: Install Node.js
|
||||
if: ${{ matrix.runtime == 'node' }}
|
||||
uses: actions/setup-node@v5
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
- name: Install pnpm
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
src/build
|
||||
src/node_modules
|
||||
node_modules
|
||||
/coverage
|
||||
npm/*/*
|
||||
!npm/*/package.json
|
||||
test/bench/node_modules
|
||||
@@ -9,7 +8,6 @@ test/fixtures/output*
|
||||
test/fixtures/vips-properties.xml
|
||||
test/leak/libvips.supp
|
||||
.DS_Store
|
||||
.nyc_output
|
||||
.vscode/
|
||||
package-lock.json
|
||||
.idea
|
||||
|
||||
@@ -8,7 +8,7 @@ smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions
|
||||
|
||||
It can be used with all JavaScript runtimes
|
||||
that provide support for Node-API v9, including
|
||||
Node.js (^18.17.0 or >= 20.3.0), Deno and Bun.
|
||||
Node.js (>= 20.9.0), Deno and Bun.
|
||||
|
||||
Resizing an image is typically 4x-5x faster than using the
|
||||
quickest ImageMagick and GraphicsMagick settings
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.4/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/starlight": "^0.36.2",
|
||||
"astro": "^5.15.3",
|
||||
"starlight-auto-sidebar": "^0.1.3"
|
||||
"@astrojs/starlight": "^0.37.1",
|
||||
"astro": "^5.16.6",
|
||||
"jsdoc-to-markdown": "^9.1.3",
|
||||
"starlight-auto-sidebar": "^0.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ A `Promise` is returned when `callback` is not provided.
|
||||
- `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
||||
- `formatMagick`: String containing format for images loaded via *magick
|
||||
- `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
|
||||
- `gainMap.image`: HDR gain map, if present, as compressed JPEG image.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ inputStream
|
||||
|
||||
|
||||
## sharpen
|
||||
> sharpen([options], [flat], [jagged]) ⇒ <code>Sharp</code>
|
||||
> sharpen([options]) ⇒ <code>Sharp</code>
|
||||
|
||||
Sharpen the image.
|
||||
|
||||
@@ -189,15 +189,13 @@ See [libvips sharpen](https://www.libvips.org/API/current/method.Image.sharpen.h
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [options] | <code>Object</code> \| <code>number</code> | | if present, is an Object with attributes |
|
||||
| [options] | <code>Object</code> | | if present, is an Object with attributes |
|
||||
| [options.sigma] | <code>number</code> | | the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`, between 0.000001 and 10 |
|
||||
| [options.m1] | <code>number</code> | <code>1.0</code> | the level of sharpening to apply to "flat" areas, between 0 and 1000000 |
|
||||
| [options.m2] | <code>number</code> | <code>2.0</code> | the level of sharpening to apply to "jagged" areas, between 0 and 1000000 |
|
||||
| [options.x1] | <code>number</code> | <code>2.0</code> | threshold between "flat" and "jagged", between 0 and 1000000 |
|
||||
| [options.y2] | <code>number</code> | <code>10.0</code> | maximum amount of brightening, between 0 and 1000000 |
|
||||
| [options.y3] | <code>number</code> | <code>20.0</code> | maximum amount of darkening, between 0 and 1000000 |
|
||||
| [flat] | <code>number</code> | | (deprecated) see `options.m1`. |
|
||||
| [jagged] | <code>number</code> | | (deprecated) see `options.m2`. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
@@ -360,8 +358,6 @@ with all white pixel values made fully transparent.
|
||||
|
||||
Existing alpha channel values for non-white pixels remain unchanged.
|
||||
|
||||
This feature is experimental and the API may change.
|
||||
|
||||
|
||||
**Since**: 0.32.1
|
||||
**Example**
|
||||
|
||||
@@ -117,6 +117,38 @@ await sharp(pixelArray, { raw: { width, height, channels } })
|
||||
```
|
||||
|
||||
|
||||
## toUint8Array
|
||||
> toUint8Array() ⇒ <code>Promise.<{data: Uint8Array, info: Object}></code>
|
||||
|
||||
Write output to a `Uint8Array` backed by a transferable `ArrayBuffer`.
|
||||
JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
||||
|
||||
Use [toFormat](#toformat) or one of the format-specific functions such as [jpeg](#jpeg), [png](#png) etc. to set the output format.
|
||||
|
||||
If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
||||
|
||||
By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
See [keepExif](#keepexif) and similar methods for control over this.
|
||||
|
||||
Resolves with an `Object` containing:
|
||||
- `data` is the output image as a `Uint8Array` backed by a transferable `ArrayBuffer`.
|
||||
- `info` contains properties relating to the output image such as `width` and `height`.
|
||||
|
||||
|
||||
**Since**: v0.35.0
|
||||
**Example**
|
||||
```js
|
||||
const { data, info } = await sharp(input).toUint8Array();
|
||||
```
|
||||
**Example**
|
||||
```js
|
||||
const { data } = await sharp(input)
|
||||
.avif()
|
||||
.toUint8Array();
|
||||
const base64String = data.toBase64();
|
||||
```
|
||||
|
||||
|
||||
## keepExif
|
||||
> keepExif() ⇒ <code>Sharp</code>
|
||||
|
||||
@@ -250,6 +282,27 @@ const outputWithP3 = await sharp(input)
|
||||
```
|
||||
|
||||
|
||||
## withGainMap
|
||||
> withGainMap() ⇒ <code>Sharp</code>
|
||||
|
||||
If the input contains gain map metadata, use it to convert the main image to HDR (High Dynamic Range) before further processing.
|
||||
The input gain map is discarded.
|
||||
|
||||
If the output is JPEG, generate and attach a new ISO 21496-1 gain map.
|
||||
JPEG output options other than `quality` are ignored.
|
||||
|
||||
This feature is experimental and the API may change.
|
||||
|
||||
|
||||
**Since**: 0.35.0
|
||||
**Example**
|
||||
```js
|
||||
const outputWithGainMap = await sharp(inputWithGainMap)
|
||||
.withGainMap()
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
|
||||
## keepXmp
|
||||
> keepXmp() ⇒ <code>Sharp</code>
|
||||
|
||||
@@ -687,8 +740,7 @@ Use these AVIF options for output image.
|
||||
AVIF image sequences are not supported.
|
||||
Prebuilt binaries support a bitdepth of 8 only.
|
||||
|
||||
This feature is experimental on the Windows ARM64 platform
|
||||
and requires a CPU with ARM64v8.4 or later.
|
||||
When using Windows ARM64, this feature requires a CPU with ARM64v8.4 or later.
|
||||
|
||||
|
||||
**Throws**:
|
||||
|
||||
23
docs/src/content/docs/changelog/v0.35.0.md
Normal file
23
docs/src/content/docs/changelog/v0.35.0.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title: v0.35.0 - TBC
|
||||
slug: changelog/v0.35.0
|
||||
---
|
||||
|
||||
* Breaking: Drop support for Node.js 18, now requires Node.js >= 20.9.0.
|
||||
|
||||
* Breaking: Remove `install` script from `package.json` file.
|
||||
Compiling from source is now opt-in via the `build` script.
|
||||
|
||||
* Breaking: Remove deprecated `failOnError` constructor property.
|
||||
|
||||
* Breaking: Remove deprecated `paletteBitDepth` from `metadata` response.
|
||||
|
||||
* Breaking: Remove deprecated properties from `sharpen` operation.
|
||||
|
||||
* Upgrade to libvips v8.18.0 for upstream bug fixes.
|
||||
|
||||
* Add `withGainMap` to process HDR JPEG images with embedded gain maps.
|
||||
[#4314](https://github.com/lovell/sharp/issues/4314)
|
||||
|
||||
* Add `toUint8Array` for output image as a `TypedArray` backed by a transferable `ArrayBuffer`.
|
||||
[#4355](https://github.com/lovell/sharp/issues/4355)
|
||||
@@ -10,7 +10,7 @@ smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions
|
||||
|
||||
It can be used with all JavaScript runtimes
|
||||
that provide support for Node-API v9, including
|
||||
Node.js >= 18.17.0, Deno and Bun.
|
||||
Node.js >= 20.9.0, Deno and Bun.
|
||||
|
||||
Resizing an image is typically 4x-5x faster than using the
|
||||
quickest ImageMagick and GraphicsMagick settings
|
||||
|
||||
@@ -20,10 +20,6 @@ npm install sharp
|
||||
pnpm add sharp
|
||||
```
|
||||
|
||||
When using `pnpm`, add `sharp` to
|
||||
[ignoredBuiltDependencies](https://pnpm.io/settings#ignoredbuiltdependencies)
|
||||
to silence warnings.
|
||||
|
||||
```sh frame="none"
|
||||
yarn add sharp
|
||||
```
|
||||
@@ -39,7 +35,7 @@ deno run --allow-env --allow-ffi --allow-read --allow-sys ...
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Node-API v9 compatible runtime e.g. Node.js ^18.17.0 or >=20.3.0.
|
||||
* Node-API v9 compatible runtime e.g. Node.js >= 20.9.0.
|
||||
|
||||
## Prebuilt binaries
|
||||
|
||||
@@ -112,13 +108,13 @@ and on macOS when running Node.js under Rosetta.
|
||||
|
||||
## Building from source
|
||||
|
||||
This module will be compiled from source when:
|
||||
```sh frame="none"
|
||||
npm install sharp
|
||||
npm explore sharp -- npm run build
|
||||
```
|
||||
|
||||
* a globally-installed libvips is detected, or
|
||||
* using `npm explore sharp -- npm run build`, or
|
||||
* using the deprecated `npm run --build-from-source` at `npm install` time.
|
||||
|
||||
The logic to detect a globally-installed libvips can be skipped by setting the
|
||||
The build process will search for a globally-installed libvips.
|
||||
This detection logic can be skipped by setting the
|
||||
`SHARP_IGNORE_GLOBAL_LIBVIPS` (never try to use it) or
|
||||
`SHARP_FORCE_GLOBAL_LIBVIPS` (always try to use it, even when missing or outdated)
|
||||
environment variables.
|
||||
@@ -129,21 +125,12 @@ Building from source requires:
|
||||
* [node-addon-api](https://www.npmjs.com/package/node-addon-api) version 7+
|
||||
* [node-gyp](https://github.com/nodejs/node-gyp#installation) version 9+ and its dependencies
|
||||
|
||||
There is an install-time check for these dependencies.
|
||||
If `node-addon-api` or `node-gyp` cannot be found, try adding them via:
|
||||
|
||||
```sh frame="none"
|
||||
npm install --save node-addon-api node-gyp
|
||||
```
|
||||
|
||||
When using `pnpm`, you may need to add `sharp` to
|
||||
[onlyBuiltDependencies](https://pnpm.io/settings#onlybuiltdependencies)
|
||||
to ensure the installation script can be run.
|
||||
|
||||
For cross-compiling, the `--platform`, `--arch` and `--libc` npm flags
|
||||
(or the `npm_config_platform`, `npm_config_arch` and `npm_config_libc` environment variables)
|
||||
can be used to configure the target environment.
|
||||
|
||||
## WebAssembly
|
||||
|
||||
Experimental support is provided for runtime environments that provide
|
||||
@@ -166,10 +153,8 @@ as well as the additional [building from source](#building-from-source) dependen
|
||||
|
||||
```sh frame="none"
|
||||
pkg install -y pkgconf vips
|
||||
```
|
||||
|
||||
```sh frame="none"
|
||||
cd /usr/ports/graphics/vips/ && make install clean
|
||||
npm install sharp
|
||||
npm explore sharp -- npm run build
|
||||
```
|
||||
|
||||
## Linux memory allocator
|
||||
@@ -203,6 +188,12 @@ and how to configure it.
|
||||
Some package managers use symbolic links
|
||||
but AWS Lambda does not support these within deployment packages.
|
||||
|
||||
An alternative approach is to use a well-maintained, third-party Lambda Layer:
|
||||
|
||||
- [cbschuld/sharp-aws-lambda-layer](https://github.com/cbschuld/sharp-aws-lambda-layer)
|
||||
- [pH200/sharp-layer](https://github.com/pH200/sharp-layer)
|
||||
- [zoellner/sharp-heic-lambda-layer](https://github.com/zoellner/sharp-heic-lambda-layer)
|
||||
|
||||
To get the best performance select the largest memory available.
|
||||
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ const {
|
||||
spawnRebuild,
|
||||
} = require('../lib/libvips');
|
||||
|
||||
log('Attempting to build from source via node-gyp');
|
||||
log('Building from source');
|
||||
log('See https://sharp.pixelplumbing.com/install#building-from-source');
|
||||
|
||||
try {
|
||||
@@ -29,7 +29,7 @@ try {
|
||||
}
|
||||
|
||||
if (useGlobalLibvips(log)) {
|
||||
log(`Detected globally-installed libvips v${globalLibvipsVersion()}`);
|
||||
log(`Found globally-installed libvips v${globalLibvipsVersion()}`);
|
||||
}
|
||||
|
||||
const status = spawnRebuild();
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
try {
|
||||
const { useGlobalLibvips } = require('../lib/libvips');
|
||||
if (useGlobalLibvips() || process.env.npm_config_build_from_source) {
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
const summary = err.message.split(/\n/).slice(0, 1);
|
||||
console.log(`sharp: skipping install check: ${summary}`);
|
||||
}
|
||||
@@ -306,6 +306,7 @@ const Sharp = function (input, options) {
|
||||
fileOut: '',
|
||||
formatOut: 'input',
|
||||
streamOut: false,
|
||||
typedArrayOut: false,
|
||||
keepMetadata: 0,
|
||||
withMetadataOrientation: -1,
|
||||
withMetadataDensity: 0,
|
||||
@@ -313,6 +314,7 @@ const Sharp = function (input, options) {
|
||||
withExif: {},
|
||||
withExifMerge: true,
|
||||
withXmp: '',
|
||||
withGainMap: false,
|
||||
resolveWithObject: false,
|
||||
loop: -1,
|
||||
delay: [],
|
||||
|
||||
46
lib/index.d.ts
vendored
46
lib/index.d.ts
vendored
@@ -259,7 +259,6 @@ declare namespace sharp {
|
||||
* Set the pipeline colourspace.
|
||||
* The input image will be converted to the provided colourspace at the start of the pipeline.
|
||||
* All operations will use this colourspace before converting to the output colourspace, as defined by toColourspace.
|
||||
* This feature is experimental and has not yet been fully-tested with all operations.
|
||||
*
|
||||
* @param colourspace pipeline colourspace e.g. rgb16, scrgb, lab, grey16 ...
|
||||
* @throws {Error} Invalid parameters
|
||||
@@ -470,21 +469,6 @@ declare namespace sharp {
|
||||
*/
|
||||
sharpen(options?: SharpenOptions): Sharp;
|
||||
|
||||
/**
|
||||
* Sharpen the image.
|
||||
* When used without parameters, performs a fast, mild sharpen of the output image.
|
||||
* When a sigma is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
|
||||
* Fine-grained control over the level of sharpening in "flat" (m1) and "jagged" (m2) areas is available.
|
||||
* @param sigma the sigma of the Gaussian mask, where sigma = 1 + radius / 2.
|
||||
* @param flat the level of sharpening to apply to "flat" areas. (optional, default 1.0)
|
||||
* @param jagged the level of sharpening to apply to "jagged" areas. (optional, default 2.0)
|
||||
* @throws {Error} Invalid parameters
|
||||
* @returns A sharp instance that can be used to chain operations
|
||||
*
|
||||
* @deprecated Use the object parameter `sharpen({sigma, m1, m2, x1, y2, y3})` instead
|
||||
*/
|
||||
sharpen(sigma?: number, flat?: number, jagged?: number): Sharp;
|
||||
|
||||
/**
|
||||
* Apply median filter. When used without parameters the default window is 3x3.
|
||||
* @param size square mask size: size x size (optional, default 3)
|
||||
@@ -693,6 +677,13 @@ declare namespace sharp {
|
||||
*/
|
||||
toBuffer(options: { resolveWithObject: true }): Promise<{ data: Buffer; info: OutputInfo }>;
|
||||
|
||||
/**
|
||||
* Write output to a Uint8Array backed by a transferable ArrayBuffer. JPEG, PNG, WebP, AVIF, TIFF, GIF and RAW output are supported.
|
||||
* By default, the format will match the input image, except SVG input which becomes PNG output.
|
||||
* @returns A promise that resolves with an object containing the Uint8Array data and an info object containing the output image format, size (bytes), width, height and channels
|
||||
*/
|
||||
toUint8Array(): Promise<{ data: Uint8Array; info: OutputInfo }>;
|
||||
|
||||
/**
|
||||
* Keep all EXIF metadata from the input image in the output image.
|
||||
* EXIF metadata is unsupported for TIFF output.
|
||||
@@ -903,7 +894,7 @@ declare namespace sharp {
|
||||
* - sharp.gravity: north, northeast, east, southeast, south, southwest, west, northwest, center or centre.
|
||||
* - sharp.strategy: cover only, dynamically crop using either the entropy or attention strategy. Some of these values are based on the object-position CSS property.
|
||||
*
|
||||
* The experimental strategy-based approach resizes so one dimension is at its target length then repeatedly ranks edge regions,
|
||||
* The strategy-based approach resizes so one dimension is at its target length then repeatedly ranks edge regions,
|
||||
* discarding the edge with the lowest score based on the selected strategy.
|
||||
* - entropy: focus on the region with the highest Shannon entropy.
|
||||
* - attention: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
|
||||
@@ -992,14 +983,6 @@ declare namespace sharp {
|
||||
* 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. (optional, default 'warning')
|
||||
*/
|
||||
failOn?: FailOnOptions | undefined;
|
||||
/**
|
||||
* By default halt processing and raise an error when loading invalid images.
|
||||
* Set this flag to false if you'd rather apply a "best effort" to decode images,
|
||||
* even if the data is corrupt or invalid. (optional, default true)
|
||||
*
|
||||
* @deprecated Use `failOn` instead
|
||||
*/
|
||||
failOnError?: boolean | undefined;
|
||||
/**
|
||||
* Do not process input images where the number of pixels (width x height) exceeds this limit.
|
||||
* Assumes image dimensions contained in the input metadata can be trusted.
|
||||
@@ -1277,6 +1260,8 @@ declare namespace sharp {
|
||||
formatMagick?: string | undefined;
|
||||
/** Array of keyword/text pairs representing PNG text blocks, if present. */
|
||||
comments?: CommentsMetadata[] | undefined;
|
||||
/** HDR gain map, if present */
|
||||
gainMap?: GainMapMetadata | undefined;
|
||||
}
|
||||
|
||||
interface LevelMetadata {
|
||||
@@ -1289,16 +1274,21 @@ declare namespace sharp {
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface GainMapMetadata {
|
||||
/** JPEG image */
|
||||
image: Buffer;
|
||||
}
|
||||
|
||||
interface Stats {
|
||||
/** Array of channel statistics for each channel in the image. */
|
||||
channels: ChannelStats[];
|
||||
/** Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel */
|
||||
isOpaque: boolean;
|
||||
/** Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental) */
|
||||
/** Histogram-based estimation of greyscale entropy, discarding alpha channel if any */
|
||||
entropy: number;
|
||||
/** Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any (experimental) */
|
||||
/** Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any */
|
||||
sharpness: number;
|
||||
/** Object containing most dominant sRGB colour based on a 4096-bin 3D histogram (experimental) */
|
||||
/** Object containing most dominant sRGB colour based on a 4096-bin 3D histogram */
|
||||
dominant: { r: number; g: number; b: number };
|
||||
}
|
||||
|
||||
|
||||
11
lib/input.js
11
lib/input.js
@@ -30,7 +30,7 @@ const inputStreamParameters = [
|
||||
// Format-specific
|
||||
'jp2', 'openSlide', 'pdf', 'raw', 'svg', 'tiff',
|
||||
// Deprecated
|
||||
'failOnError', 'openSlideLevel', 'pdfBackground', 'tiffSubifd'
|
||||
'openSlideLevel', 'pdfBackground', 'tiffSubifd'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -106,14 +106,6 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
}`);
|
||||
}
|
||||
if (is.object(inputOptions)) {
|
||||
// Deprecated: failOnError
|
||||
if (is.defined(inputOptions.failOnError)) {
|
||||
if (is.bool(inputOptions.failOnError)) {
|
||||
inputDescriptor.failOn = inputOptions.failOnError ? 'warning' : 'none';
|
||||
} else {
|
||||
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
|
||||
}
|
||||
}
|
||||
// failOn
|
||||
if (is.defined(inputOptions.failOn)) {
|
||||
if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {
|
||||
@@ -607,6 +599,7 @@ function _isStreamInput () {
|
||||
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
|
||||
* - `formatMagick`: String containing format for images loaded via *magick
|
||||
* - `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
|
||||
* - `gainMap.image`: HDR gain map, if present, as compressed JPEG image.
|
||||
*
|
||||
* @example
|
||||
* const metadata = await sharp(input).metadata();
|
||||
|
||||
@@ -259,45 +259,18 @@ function affine (matrix, options) {
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object|number} [options] - if present, is an Object with attributes
|
||||
* @param {Object} [options] - if present, is an Object with attributes
|
||||
* @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`, between 0.000001 and 10
|
||||
* @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas, between 0 and 1000000
|
||||
* @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas, between 0 and 1000000
|
||||
* @param {number} [options.x1=2.0] - threshold between "flat" and "jagged", between 0 and 1000000
|
||||
* @param {number} [options.y2=10.0] - maximum amount of brightening, between 0 and 1000000
|
||||
* @param {number} [options.y3=20.0] - maximum amount of darkening, between 0 and 1000000
|
||||
* @param {number} [flat] - (deprecated) see `options.m1`.
|
||||
* @param {number} [jagged] - (deprecated) see `options.m2`.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
function sharpen (options, flat, jagged) {
|
||||
if (!is.defined(options)) {
|
||||
// No arguments: default to mild sharpen
|
||||
this.options.sharpenSigma = -1;
|
||||
} else if (is.bool(options)) {
|
||||
// Deprecated boolean argument: apply mild sharpen?
|
||||
this.options.sharpenSigma = options ? -1 : 0;
|
||||
} else if (is.number(options) && is.inRange(options, 0.01, 10000)) {
|
||||
// Deprecated numeric argument: specific sigma
|
||||
this.options.sharpenSigma = options;
|
||||
// Deprecated control over flat areas
|
||||
if (is.defined(flat)) {
|
||||
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
|
||||
this.options.sharpenM1 = flat;
|
||||
} else {
|
||||
throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
|
||||
}
|
||||
}
|
||||
// Deprecated control over jagged areas
|
||||
if (is.defined(jagged)) {
|
||||
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
|
||||
this.options.sharpenM2 = jagged;
|
||||
} else {
|
||||
throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
|
||||
}
|
||||
}
|
||||
} else if (is.plainObject(options)) {
|
||||
function sharpen (options) {
|
||||
if (is.plainObject(options)) {
|
||||
if (is.number(options.sigma) && is.inRange(options.sigma, 0.000001, 10)) {
|
||||
this.options.sharpenSigma = options.sigma;
|
||||
} else {
|
||||
@@ -339,7 +312,7 @@ function sharpen (options, flat, jagged) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', options);
|
||||
this.options.sharpenSigma = -1;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -510,8 +483,6 @@ function flatten (options) {
|
||||
*
|
||||
* Existing alpha channel values for non-white pixels remain unchanged.
|
||||
*
|
||||
* This feature is experimental and the API may change.
|
||||
*
|
||||
* @since 0.32.1
|
||||
*
|
||||
* @example
|
||||
|
||||
@@ -164,6 +164,41 @@ function toBuffer (options, callback) {
|
||||
return this._pipeline(is.fn(options) ? options : callback, stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write output to a `Uint8Array` backed by a transferable `ArrayBuffer`.
|
||||
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
||||
*
|
||||
* Use {@link #toformat toFormat} or one of the format-specific functions such as {@link #jpeg jpeg}, {@link #png png} etc. to set the output format.
|
||||
*
|
||||
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
||||
*
|
||||
* By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
* See {@link #keepexif keepExif} and similar methods for control over this.
|
||||
*
|
||||
* Resolves with an `Object` containing:
|
||||
* - `data` is the output image as a `Uint8Array` backed by a transferable `ArrayBuffer`.
|
||||
* - `info` contains properties relating to the output image such as `width` and `height`.
|
||||
*
|
||||
* @since v0.35.0
|
||||
*
|
||||
* @example
|
||||
* const { data, info } = await sharp(input).toUint8Array();
|
||||
*
|
||||
* @example
|
||||
* const { data } = await sharp(input)
|
||||
* .avif()
|
||||
* .toUint8Array();
|
||||
* const base64String = data.toBase64();
|
||||
*
|
||||
* @returns {Promise<{ data: Uint8Array, info: Object }>}
|
||||
*/
|
||||
function toUint8Array () {
|
||||
this.options.resolveWithObject = true;
|
||||
this.options.typedArrayOut = true;
|
||||
const stack = Error();
|
||||
return this._pipeline(null, stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep all EXIF metadata from the input image in the output image.
|
||||
*
|
||||
@@ -319,6 +354,30 @@ function withIccProfile (icc, options) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the input contains gain map metadata, use it to convert the main image to HDR (High Dynamic Range) before further processing.
|
||||
* The input gain map is discarded.
|
||||
*
|
||||
* If the output is JPEG, generate and attach a new ISO 21496-1 gain map.
|
||||
* JPEG output options other than `quality` are ignored.
|
||||
*
|
||||
* This feature is experimental and the API may change.
|
||||
*
|
||||
* @since 0.35.0
|
||||
*
|
||||
* @example
|
||||
* const outputWithGainMap = await sharp(inputWithGainMap)
|
||||
* .withGainMap()
|
||||
* .toBuffer();
|
||||
*
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
function withGainMap() {
|
||||
this.options.withGainMap = true;
|
||||
this.options.colourspace = 'scrgb';
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep XMP metadata from the input image in the output image.
|
||||
*
|
||||
@@ -1092,8 +1151,7 @@ function tiff (options) {
|
||||
* AVIF image sequences are not supported.
|
||||
* Prebuilt binaries support a bitdepth of 8 only.
|
||||
*
|
||||
* This feature is experimental on the Windows ARM64 platform
|
||||
* and requires a CPU with ARM64v8.4 or later.
|
||||
* When using Windows ARM64, this feature requires a CPU with ARM64v8.4 or later.
|
||||
*
|
||||
* @example
|
||||
* const data = await sharp(input)
|
||||
@@ -1635,11 +1693,13 @@ module.exports = (Sharp) => {
|
||||
// Public
|
||||
toFile,
|
||||
toBuffer,
|
||||
toUint8Array,
|
||||
keepExif,
|
||||
withExif,
|
||||
withExifMerge,
|
||||
keepIccProfile,
|
||||
withIccProfile,
|
||||
withGainMap,
|
||||
keepXmp,
|
||||
withXmp,
|
||||
keepMetadata,
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
|
||||
const { familySync, versionSync } = require('detect-libc');
|
||||
|
||||
const { version } = require('../package.json');
|
||||
const { runtimePlatformArch, isUnsupportedNodeRuntime, prebuiltPlatforms, minimumLibvipsVersion } = require('./libvips');
|
||||
const runtimePlatform = runtimePlatformArch();
|
||||
|
||||
const paths = [
|
||||
`../src/build/Release/sharp-${runtimePlatform}.node`,
|
||||
'../src/build/Release/sharp-wasm32.node',
|
||||
`../src/build/Release/sharp-${runtimePlatform}-${version}.node`,
|
||||
`../src/build/Release/sharp-wasm32-${version}.node`,
|
||||
`@img/sharp-${runtimePlatform}/sharp.node`,
|
||||
'@img/sharp-wasm32/sharp.node'
|
||||
];
|
||||
@@ -72,6 +73,7 @@ if (sharp) {
|
||||
} else {
|
||||
help.push(
|
||||
`- Manually install libvips >= ${minimumLibvipsVersion}`,
|
||||
' See https://sharp.pixelplumbing.com/install#building-from-source',
|
||||
'- Add experimental WebAssembly-based dependencies:',
|
||||
' npm install --cpu=wasm32 sharp',
|
||||
' npm install @img/sharp-wasm32'
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-darwin-arm64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"darwin"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4"
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-darwin-x64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"darwin"
|
||||
|
||||
@@ -44,9 +44,10 @@ cpSync(releaseDir, libDir, {
|
||||
}
|
||||
});
|
||||
|
||||
// Generate README
|
||||
const { name, description } = require(`./${platform}/package.json`);
|
||||
// Generate README and index.cjs
|
||||
const { version, name, description } = require(`./${platform}/package.json`);
|
||||
writeFileSync(join(destDir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`);
|
||||
writeFileSync(join(destDir, 'index.cjs'), `module.exports = require('./lib/sharp-${platform}-${version}.node');`);
|
||||
|
||||
// Copy Apache-2.0 LICENSE
|
||||
copyFileSync(join(__dirname, '..', 'LICENSE'), join(destDir, 'LICENSE'));
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4"
|
||||
"@img/sharp-libvips-linux-arm": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-arm.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.31"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4"
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-arm64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.26"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4"
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-ppc64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.36"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4"
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-riscv64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.41"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4"
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-s390x.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.36"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4"
|
||||
"@img/sharp-libvips-linux-x64": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linux-x64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.26"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linuxmusl-arm64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"musl": ">=1.2.2"
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.0-rc.1"
|
||||
},
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
@@ -25,11 +26,11 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-linuxmusl-x64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"musl": ">=1.2.2"
|
||||
|
||||
@@ -23,15 +23,15 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-wasm32.node.js",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.7.0"
|
||||
"@emnapi/runtime": "^1.7.1"
|
||||
},
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib",
|
||||
"versions.json"
|
||||
],
|
||||
@@ -23,12 +24,12 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-win32-arm64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"win32"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib",
|
||||
"versions.json"
|
||||
],
|
||||
@@ -23,12 +24,12 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-win32-ia32.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"win32"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"files": [
|
||||
"index.cjs",
|
||||
"lib",
|
||||
"versions.json"
|
||||
],
|
||||
@@ -23,12 +24,12 @@
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-win32-x64.node",
|
||||
"./sharp.node": "./index.cjs",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"os": [
|
||||
"win32"
|
||||
|
||||
48
package.json
48
package.json
@@ -93,8 +93,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "node install/build.js",
|
||||
"install": "node install/check.js || npm run build",
|
||||
"clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*",
|
||||
"clean": "rm -rf src/build/ test/fixtures/output.*",
|
||||
"test": "npm run lint && npm run test-unit",
|
||||
"lint": "npm run lint-cpp && npm run lint-js && npm run lint-types",
|
||||
"lint-cpp": "cpplint --quiet src/*.h src/*.cc",
|
||||
@@ -146,16 +145,16 @@
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.5",
|
||||
"@img/sharp-darwin-x64": "0.34.5",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-linux-arm": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-linux-x64": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.0-rc.1",
|
||||
"@img/sharp-linux-arm": "0.34.5",
|
||||
"@img/sharp-linux-arm64": "0.34.5",
|
||||
"@img/sharp-linux-ppc64": "0.34.5",
|
||||
@@ -170,31 +169,30 @@
|
||||
"@img/sharp-win32-x64": "0.34.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.3.4",
|
||||
"@biomejs/biome": "^2.3.10",
|
||||
"@cpplint/cli": "^0.1.0",
|
||||
"@emnapi/runtime": "^1.7.0",
|
||||
"@img/sharp-libvips-dev": "1.2.4",
|
||||
"@img/sharp-libvips-dev-wasm32": "1.2.4",
|
||||
"@img/sharp-libvips-win32-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-win32-ia32": "1.2.4",
|
||||
"@img/sharp-libvips-win32-x64": "1.2.4",
|
||||
"@emnapi/runtime": "^1.7.1",
|
||||
"@img/sharp-libvips-dev": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-dev-wasm32": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-win32-arm64": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-win32-ia32": "1.3.0-rc.1",
|
||||
"@img/sharp-libvips-win32-x64": "1.3.0-rc.1",
|
||||
"@types/node": "*",
|
||||
"emnapi": "^1.7.0",
|
||||
"exif-reader": "^2.0.2",
|
||||
"emnapi": "^1.7.1",
|
||||
"exif-reader": "^2.0.3",
|
||||
"extract-zip": "^2.0.1",
|
||||
"icc": "^3.0.0",
|
||||
"jsdoc-to-markdown": "^9.1.3",
|
||||
"node-addon-api": "^8.5.0",
|
||||
"node-gyp": "^11.5.0",
|
||||
"node-gyp": "^12.1.0",
|
||||
"tar-fs": "^3.1.1",
|
||||
"tsd": "^0.33.0"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"config": {
|
||||
"libvips": ">=8.17.3"
|
||||
"libvips": ">=8.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
'variables': {
|
||||
'vips_version': '<!(node -p "require(\'../lib/libvips\').minimumLibvipsVersion")',
|
||||
'platform_and_arch': '<!(node -p "require(\'../lib/libvips\').buildPlatformArch()")',
|
||||
'sharp_version': '<!(node -p "require(\'../package.json\').version")',
|
||||
'sharp_libvips_version': '<!(node -p "require(\'../package.json\').optionalDependencies[\'@img/sharp-libvips-<(platform_and_arch)\']")',
|
||||
'sharp_libvips_yarn_locator': '<!(node -p "require(\'../lib/libvips\').yarnLocator()")',
|
||||
'sharp_libvips_include_dir': '<!(node -p "require(\'../lib/libvips\').buildSharpLibvipsIncludeDir()")',
|
||||
@@ -81,7 +82,7 @@
|
||||
}]
|
||||
]
|
||||
}, {
|
||||
'target_name': 'sharp-<(platform_and_arch)',
|
||||
'target_name': 'sharp-<(platform_and_arch)-<(sharp_version)',
|
||||
'defines': [
|
||||
'G_DISABLE_ASSERT',
|
||||
'G_DISABLE_CAST_CHECKS',
|
||||
@@ -282,7 +283,7 @@
|
||||
'target_name': 'copy-dll',
|
||||
'type': 'none',
|
||||
'dependencies': [
|
||||
'sharp-<(platform_and_arch)'
|
||||
'sharp-<(platform_and_arch)-<(sharp_version)'
|
||||
],
|
||||
'conditions': [
|
||||
['OS == "win"', {
|
||||
|
||||
@@ -289,6 +289,7 @@ namespace sharp {
|
||||
case ImageType::JXL: id = "jxl"; break;
|
||||
case ImageType::RAD: id = "rad"; break;
|
||||
case ImageType::DCRAW: id = "dcraw"; break;
|
||||
case ImageType::UHDR: id = "uhdr"; break;
|
||||
case ImageType::VIPS: id = "vips"; break;
|
||||
case ImageType::RAW: id = "raw"; break;
|
||||
case ImageType::UNKNOWN: id = "unknown"; break;
|
||||
@@ -339,6 +340,9 @@ namespace sharp {
|
||||
{ "VipsForeignLoadRadBuffer", ImageType::RAD },
|
||||
{ "VipsForeignLoadDcRawFile", ImageType::DCRAW },
|
||||
{ "VipsForeignLoadDcRawBuffer", ImageType::DCRAW },
|
||||
{ "VipsForeignLoadUhdr", ImageType::UHDR },
|
||||
{ "VipsForeignLoadUhdrFile", ImageType::UHDR },
|
||||
{ "VipsForeignLoadUhdrBuffer", ImageType::UHDR },
|
||||
{ "VipsForeignLoadVips", ImageType::VIPS },
|
||||
{ "VipsForeignLoadVipsFile", ImageType::VIPS },
|
||||
{ "VipsForeignLoadRaw", ImageType::RAW }
|
||||
@@ -356,6 +360,9 @@ namespace sharp {
|
||||
imageType = it->second;
|
||||
}
|
||||
}
|
||||
if (imageType == ImageType::UHDR) {
|
||||
imageType = ImageType::JPEG;
|
||||
}
|
||||
return imageType;
|
||||
}
|
||||
|
||||
@@ -375,6 +382,9 @@ namespace sharp {
|
||||
imageType = ImageType::MISSING;
|
||||
}
|
||||
}
|
||||
if (imageType == ImageType::UHDR) {
|
||||
imageType = ImageType::JPEG;
|
||||
}
|
||||
return imageType;
|
||||
}
|
||||
|
||||
@@ -1127,4 +1137,20 @@ namespace sharp {
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/*
|
||||
Does this image have a gain map?
|
||||
*/
|
||||
bool HasGainMap(VImage image) {
|
||||
return image.get_typeof("gainmap-data") == VIPS_TYPE_BLOB;
|
||||
}
|
||||
|
||||
/*
|
||||
Removes gain map, if any.
|
||||
*/
|
||||
VImage RemoveGainMap(VImage image) {
|
||||
VImage copy = image.copy();
|
||||
copy.remove("gainmap-data");
|
||||
return copy;
|
||||
}
|
||||
} // namespace sharp
|
||||
|
||||
17
src/common.h
17
src/common.h
@@ -18,9 +18,9 @@
|
||||
// Verify platform and compiler compatibility
|
||||
|
||||
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 17) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 17 && VIPS_MICRO_VERSION < 3)
|
||||
#error "libvips version 8.17.3+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 18) || \
|
||||
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 18 && VIPS_MICRO_VERSION < 0)
|
||||
#error "libvips version 8.18.0+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||
#endif
|
||||
|
||||
#if defined(__has_include)
|
||||
@@ -173,6 +173,7 @@ namespace sharp {
|
||||
JXL,
|
||||
RAD,
|
||||
DCRAW,
|
||||
UHDR,
|
||||
VIPS,
|
||||
RAW,
|
||||
UNKNOWN,
|
||||
@@ -397,6 +398,16 @@ namespace sharp {
|
||||
*/
|
||||
VImage StaySequential(VImage image, bool condition = true);
|
||||
|
||||
/*
|
||||
Does this image have a gain map?
|
||||
*/
|
||||
bool HasGainMap(VImage image);
|
||||
|
||||
/*
|
||||
Removes gain map, if any.
|
||||
*/
|
||||
VImage RemoveGainMap(VImage image);
|
||||
|
||||
} // namespace sharp
|
||||
|
||||
#endif // SRC_COMMON_H_
|
||||
|
||||
@@ -141,6 +141,14 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength);
|
||||
baton->tifftagPhotoshopLength = tifftagPhotoshopLength;
|
||||
}
|
||||
// Gain Map
|
||||
if (image.get_typeof("gainmap-data") == VIPS_TYPE_BLOB) {
|
||||
size_t gainMapLength;
|
||||
void const *gainMap = image.get_blob("gainmap-data", &gainMapLength);
|
||||
baton->gainMap = static_cast<char *>(g_malloc(gainMapLength));
|
||||
memcpy(baton->gainMap, gainMap, gainMapLength);
|
||||
baton->gainMapLength = gainMapLength;
|
||||
}
|
||||
// PNG comments
|
||||
vips_image_map(image.get_image(), readPNGComment, &baton->comments);
|
||||
}
|
||||
@@ -182,10 +190,6 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
info.Set("isPalette", baton->isPalette);
|
||||
if (baton->bitsPerSample > 0) {
|
||||
info.Set("bitsPerSample", baton->bitsPerSample);
|
||||
if (baton->isPalette) {
|
||||
// Deprecated, remove with libvips 8.17.0
|
||||
info.Set("paletteBitDepth", baton->bitsPerSample);
|
||||
}
|
||||
}
|
||||
if (baton->pages > 0) {
|
||||
info.Set("pages", baton->pages);
|
||||
@@ -276,6 +280,12 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
Napi::Buffer<char>::NewOrCopy(env, baton->tifftagPhotoshop,
|
||||
baton->tifftagPhotoshopLength, sharp::FreeCallback));
|
||||
}
|
||||
if (baton->gainMapLength > 0) {
|
||||
Napi::Object gainMap = Napi::Object::New(env);
|
||||
info.Set("gainMap", gainMap);
|
||||
gainMap.Set("image",
|
||||
Napi::Buffer<char>::NewOrCopy(env, baton->gainMap, baton->gainMapLength, sharp::FreeCallback));
|
||||
}
|
||||
if (baton->comments.size() > 0) {
|
||||
int i = 0;
|
||||
Napi::Array comments = Napi::Array::New(env, baton->comments.size());
|
||||
|
||||
@@ -53,6 +53,8 @@ struct MetadataBaton {
|
||||
size_t xmpLength;
|
||||
char *tifftagPhotoshop;
|
||||
size_t tifftagPhotoshopLength;
|
||||
char *gainMap;
|
||||
size_t gainMapLength;
|
||||
MetadataComments comments;
|
||||
std::string err;
|
||||
|
||||
@@ -82,7 +84,9 @@ struct MetadataBaton {
|
||||
xmp(nullptr),
|
||||
xmpLength(0),
|
||||
tifftagPhotoshop(nullptr),
|
||||
tifftagPhotoshopLength(0) {}
|
||||
tifftagPhotoshopLength(0),
|
||||
gainMap(nullptr),
|
||||
gainMapLength(0) {}
|
||||
};
|
||||
|
||||
Napi::Value metadata(const Napi::CallbackInfo& info);
|
||||
|
||||
@@ -296,6 +296,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (baton->input->autoOrient) {
|
||||
image = sharp::RemoveExifOrientation(image);
|
||||
}
|
||||
if (sharp::HasGainMap(image)) {
|
||||
if (baton->withGainMap) {
|
||||
image = image.uhdr2scRGB();
|
||||
}
|
||||
image = sharp::RemoveGainMap(image);
|
||||
} else {
|
||||
baton->withGainMap = false;
|
||||
}
|
||||
|
||||
// Any pre-shrinking may already have been done
|
||||
inputWidth = image.width();
|
||||
@@ -335,7 +343,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
image.interpretation() != VIPS_INTERPRETATION_LABS &&
|
||||
image.interpretation() != VIPS_INTERPRETATION_GREY16 &&
|
||||
baton->colourspacePipeline != VIPS_INTERPRETATION_CMYK &&
|
||||
!baton->input->ignoreIcc
|
||||
!baton->input->ignoreIcc && !baton->withGainMap
|
||||
) {
|
||||
// Convert to sRGB/P3 using embedded profile
|
||||
try {
|
||||
@@ -1337,12 +1345,21 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
}
|
||||
|
||||
if (baton->bufferOutLength > 0) {
|
||||
// Add buffer size to info
|
||||
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
|
||||
// Pass ownership of output data to Buffer instance
|
||||
Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
|
||||
baton->bufferOutLength, sharp::FreeCallback);
|
||||
Callback().Call(Receiver().Value(), { env.Null(), data, info });
|
||||
if (baton->typedArrayOut) {
|
||||
// ECMAScript ArrayBuffer with Uint8Array view
|
||||
Napi::ArrayBuffer ab = Napi::ArrayBuffer::New(env, baton->bufferOutLength);
|
||||
memcpy(ab.Data(), baton->bufferOut, baton->bufferOutLength);
|
||||
sharp::FreeCallback(static_cast<char*>(baton->bufferOut), nullptr);
|
||||
Napi::TypedArrayOf<uint8_t> data = Napi::TypedArrayOf<uint8_t>::New(env,
|
||||
baton->bufferOutLength, ab, 0, napi_uint8_array);
|
||||
Callback().Call(Receiver().Value(), { env.Null(), data, info });
|
||||
} else {
|
||||
// Node.js Buffer
|
||||
Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
|
||||
baton->bufferOutLength, sharp::FreeCallback);
|
||||
Callback().Call(Receiver().Value(), { env.Null(), data, info });
|
||||
}
|
||||
} else {
|
||||
// Add file size to info
|
||||
if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
|
||||
@@ -1692,6 +1709,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
// Output
|
||||
baton->formatOut = sharp::AttrAsStr(options, "formatOut");
|
||||
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
||||
baton->typedArrayOut = sharp::AttrAsBool(options, "typedArrayOut");
|
||||
baton->keepMetadata = sharp::AttrAsUint32(options, "keepMetadata");
|
||||
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
||||
baton->withMetadataDensity = sharp::AttrAsDouble(options, "withMetadataDensity");
|
||||
@@ -1706,6 +1724,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
}
|
||||
baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
|
||||
baton->withXmp = sharp::AttrAsStr(options, "withXmp");
|
||||
baton->withGainMap = sharp::AttrAsBool(options, "withGainMap");
|
||||
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
||||
baton->loop = sharp::AttrAsUint32(options, "loop");
|
||||
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
|
||||
|
||||
@@ -48,6 +48,7 @@ struct PipelineBaton {
|
||||
size_t bufferOutLength;
|
||||
int pageHeightOut;
|
||||
int pagesOut;
|
||||
bool typedArrayOut;
|
||||
std::vector<Composite *> composite;
|
||||
std::vector<sharp::InputDescriptor *> joinChannelIn;
|
||||
int topOffsetPre;
|
||||
@@ -208,6 +209,7 @@ struct PipelineBaton {
|
||||
std::unordered_map<std::string, std::string> withExif;
|
||||
bool withExifMerge;
|
||||
std::string withXmp;
|
||||
bool withGainMap;
|
||||
int timeoutSeconds;
|
||||
std::vector<double> convKernel;
|
||||
int convKernelWidth;
|
||||
@@ -242,6 +244,7 @@ struct PipelineBaton {
|
||||
bufferOutLength(0),
|
||||
pageHeightOut(0),
|
||||
pagesOut(0),
|
||||
typedArrayOut(false),
|
||||
topOffsetPre(-1),
|
||||
topOffsetPost(-1),
|
||||
channels(0),
|
||||
@@ -381,6 +384,7 @@ struct PipelineBaton {
|
||||
withMetadataOrientation(-1),
|
||||
withMetadataDensity(0.0),
|
||||
withExifMerge(true),
|
||||
withGainMap(false),
|
||||
timeoutSeconds(0),
|
||||
convKernelWidth(0),
|
||||
convKernelHeight(0),
|
||||
|
||||
@@ -228,6 +228,19 @@ async.series({
|
||||
}
|
||||
});
|
||||
}
|
||||
}).add('sharp-buffer-uint8array', {
|
||||
defer: true,
|
||||
fn: (deferred) => {
|
||||
sharp(inputJpgBuffer)
|
||||
.resize(width, height)
|
||||
.toUint8Array()
|
||||
.then(() => {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}).add('sharp-file-file', {
|
||||
defer: true,
|
||||
fn: (deferred) => {
|
||||
@@ -266,6 +279,19 @@ async.series({
|
||||
}
|
||||
});
|
||||
}
|
||||
}).add('sharp-file-uint8array', {
|
||||
defer: true,
|
||||
fn: (deferred) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(width, height)
|
||||
.toUint8Array()
|
||||
.then(() => {
|
||||
deferred.resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}).add('sharp-promise', {
|
||||
defer: true,
|
||||
fn: (deferred) => {
|
||||
|
||||
BIN
test/fixtures/gain-map.jpg
vendored
Normal file
BIN
test/fixtures/gain-map.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
1
test/fixtures/index.js
vendored
1
test/fixtures/index.js
vendored
@@ -70,6 +70,7 @@ module.exports = {
|
||||
inputJpgRandom: getPath('random.jpg'), // convert -size 200x200 xc: +noise Random random.jpg
|
||||
inputJpgThRandom: getPath('thRandom.jpg'), // convert random.jpg -channel G -threshold 5% -separate +channel -negate thRandom.jpg
|
||||
inputJpgLossless: getPath('testimgl.jpg'), // Lossless JPEG from ftp://ftp.fu-berlin.de/unix/X11/graphics/ImageMagick/delegates/ljpeg-6b.tar.gz
|
||||
inputJpgWithGainMap: getPath('gain-map.jpg'), // https://github.com/libvips/libvips/issues/3799
|
||||
|
||||
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
||||
inputPngGradients: getPath('gradients-rgb8.png'),
|
||||
|
||||
@@ -86,6 +86,9 @@ let transformer = sharp()
|
||||
});
|
||||
readableStream.pipe(transformer).pipe(writableStream);
|
||||
|
||||
sharp().toUint8Array();
|
||||
sharp().toUint8Array().then(({ data }) => data.byteLength);
|
||||
|
||||
console.log(sharp.format);
|
||||
console.log(sharp.versions);
|
||||
|
||||
|
||||
@@ -54,23 +54,6 @@ describe('failOn', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('deprecated failOnError', () => {
|
||||
assert.doesNotThrow(
|
||||
() => sharp({ failOnError: true })
|
||||
);
|
||||
assert.doesNotThrow(
|
||||
() => sharp({ failOnError: false })
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ failOnError: 'zoinks' }),
|
||||
/Expected boolean for failOnError but received zoinks of type string/
|
||||
);
|
||||
assert.throws(
|
||||
() => sharp({ failOnError: 1 }),
|
||||
/Expected boolean for failOnError but received 1 of type number/
|
||||
);
|
||||
});
|
||||
|
||||
it('returns errors to callback for truncated JPEG', (_t, done) => {
|
||||
sharp(fixtures.inputJpgTruncated, { failOn: 'truncated' }).toBuffer((err, data, info) => {
|
||||
assert.ok(err.message.includes('VipsJpeg: premature end of'), err);
|
||||
|
||||
69
test/unit/gain-map.js
Normal file
69
test/unit/gain-map.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/*!
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const { describe, it } = require('node:test');
|
||||
|
||||
const sharp = require('../../');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
describe('Gain maps', () => {
|
||||
it('Metadata contains gainMap', async (t) => {
|
||||
t.plan(4);
|
||||
|
||||
const { format, gainMap } = await sharp(
|
||||
fixtures.inputJpgWithGainMap,
|
||||
).metadata();
|
||||
t.assert.strictEqual(format, 'jpeg');
|
||||
t.assert.strictEqual(typeof gainMap, 'object');
|
||||
t.assert.ok(Buffer.isBuffer(gainMap.image));
|
||||
t.assert.strictEqual(gainMap.image.length, 31738);
|
||||
});
|
||||
|
||||
it('Can be regenerated', async (t) => {
|
||||
t.plan(4);
|
||||
|
||||
const data = await sharp(fixtures.inputJpgWithGainMap)
|
||||
.withGainMap()
|
||||
.toBuffer();
|
||||
const metadata = await sharp(data).metadata();
|
||||
t.assert.strictEqual(metadata.format, 'jpeg');
|
||||
t.assert.strictEqual(typeof metadata.gainMap, 'object');
|
||||
t.assert.ok(Buffer.isBuffer(metadata.gainMap.image));
|
||||
|
||||
const {
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
channels,
|
||||
depth,
|
||||
space,
|
||||
hasProfile,
|
||||
chromaSubsampling,
|
||||
} = await sharp(metadata.gainMap.image).metadata();
|
||||
|
||||
t.assert.deepEqual(
|
||||
{
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
channels,
|
||||
depth,
|
||||
space,
|
||||
hasProfile,
|
||||
chromaSubsampling,
|
||||
},
|
||||
{
|
||||
format: 'jpeg',
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
channels: 1,
|
||||
depth: 'uchar',
|
||||
space: 'b-w',
|
||||
hasProfile: true,
|
||||
chromaSubsampling: '4:4:4',
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { afterEach, beforeEach, describe, it } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const { isMarkedAsUntransferable } = require('node:worker_threads');
|
||||
|
||||
const sharp = require('../../');
|
||||
const fixtures = require('../fixtures');
|
||||
@@ -1092,4 +1093,39 @@ describe('Input/output', () => {
|
||||
assert.strictEqual(channels, 3);
|
||||
assert.strictEqual(format, 'jpeg');
|
||||
});
|
||||
|
||||
it('toBuffer resolves with an untransferable Buffer', async () => {
|
||||
const data = await sharp(fixtures.inputJpg)
|
||||
.resize({ width: 8, height: 8 })
|
||||
.toBuffer();
|
||||
|
||||
if (isMarkedAsUntransferable) {
|
||||
assert.strictEqual(isMarkedAsUntransferable(data.buffer), true);
|
||||
}
|
||||
assert.strictEqual(ArrayBuffer.isView(data), true);
|
||||
assert.strictEqual(ArrayBuffer.isView(data.buffer), false);
|
||||
});
|
||||
|
||||
it('toUint8Array resolves with a transferable Uint8Array', async () => {
|
||||
const { data, info } = await sharp(fixtures.inputJpg)
|
||||
.resize({ width: 8, height: 8 })
|
||||
.toUint8Array();
|
||||
|
||||
assert.strictEqual(data instanceof Uint8Array, true);
|
||||
if (isMarkedAsUntransferable) {
|
||||
assert.strictEqual(isMarkedAsUntransferable(data.buffer), false);
|
||||
}
|
||||
assert.strictEqual(ArrayBuffer.isView(data), true);
|
||||
assert.strictEqual(info.format, 'jpeg');
|
||||
assert.strictEqual(info.width, 8);
|
||||
assert.strictEqual(info.height, 8);
|
||||
assert.strictEqual(data.byteLength, info.size);
|
||||
assert.strictEqual(data[0], 0xFF);
|
||||
assert.strictEqual(data[1], 0xD8);
|
||||
|
||||
const metadata = await sharp(data).metadata();
|
||||
assert.strictEqual(metadata.format, 'jpeg');
|
||||
assert.strictEqual(metadata.width, 8);
|
||||
assert.strictEqual(metadata.height, 8);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -180,7 +180,7 @@ describe('libvips binaries', () => {
|
||||
process.env.npm_config_arch = 's390x';
|
||||
process.env.npm_config_libc = '';
|
||||
const locatorHash = libvips.yarnLocator();
|
||||
assert.strictEqual(locatorHash, '4ab19140fd');
|
||||
assert.strictEqual(locatorHash, '8cdba194cb');
|
||||
delete process.env.npm_config_platform;
|
||||
delete process.env.npm_config_arch;
|
||||
delete process.env.npm_config_libc;
|
||||
|
||||
@@ -154,7 +154,6 @@ describe('PNG', () => {
|
||||
isProgressive: false,
|
||||
isPalette: true,
|
||||
bitsPerSample: 8,
|
||||
paletteBitDepth: 8,
|
||||
hasProfile: false,
|
||||
hasAlpha: false
|
||||
});
|
||||
@@ -226,12 +225,10 @@ describe('PNG', () => {
|
||||
.png({ colours: 2, palette: false })
|
||||
.toBuffer();
|
||||
|
||||
const { channels, isPalette, bitsPerSample, paletteBitDepth, size, space } = await sharp(data).metadata();
|
||||
const { channels, isPalette, bitsPerSample, space } = await sharp(data).metadata();
|
||||
assert.strictEqual(channels, 1);
|
||||
assert.strictEqual(isPalette, false);
|
||||
assert.strictEqual(bitsPerSample, 1);
|
||||
assert.strictEqual(paletteBitDepth, undefined);
|
||||
assert.strictEqual(size, 89);
|
||||
assert.strictEqual(space, 'b-w');
|
||||
});
|
||||
|
||||
|
||||
@@ -94,24 +94,6 @@ describe('Sharpen', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid sigma', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg).sharpen(-1.5);
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid flat', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg).sharpen(1, -1);
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid jagged', () => {
|
||||
assert.throws(() => {
|
||||
sharp(fixtures.inputJpg).sharpen(1, 1, -1);
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid options.sigma', () => assert.throws(
|
||||
() => sharp().sharpen({ sigma: -1 }),
|
||||
/Expected number between 0\.000001 and 10 for options\.sigma but received -1 of type number/
|
||||
@@ -144,24 +126,23 @@ describe('Sharpen', () => {
|
||||
|
||||
it('sharpened image is larger than non-sharpened', (_t, done) => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.sharpen(false)
|
||||
.resize(32, 24)
|
||||
.toBuffer((err, notSharpened, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, notSharpened.length > 0);
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
assert.strictEqual(240, info.height);
|
||||
assert.strictEqual(32, info.width);
|
||||
assert.strictEqual(24, info.height);
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.sharpen(true)
|
||||
.resize(32, 24)
|
||||
.sharpen()
|
||||
.toBuffer((err, sharpened, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, sharpened.length > 0);
|
||||
assert.strictEqual(true, sharpened.length > notSharpened.length);
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
assert.strictEqual(240, info.height);
|
||||
assert.strictEqual(32, info.width);
|
||||
assert.strictEqual(24, info.height);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user