Compare commits

..

60 Commits

Author SHA1 Message Date
Lovell Fuller
19d0e272e6 Release v0.33.4 2024-05-16 12:48:29 +01:00
Lovell Fuller
16b764f1c1 Prerelease v0.33.4-rc.0 2024-05-16 12:16:41 +01:00
Lovell Fuller
1593ee3838 Bump devDeps 2024-05-16 12:15:49 +01:00
Lovell Fuller
668b5ba8bc Docs: changelog and credit for #4096 2024-05-14 16:59:20 +01:00
Adriaan Meuris
29336f4cc7 Ensure negate op occurs after profile conversion (#4096)
- Adds CMYK to CMYK profile conversion tests
- Fixes existing greyscale plus alpha test expectation
2024-05-13 12:23:11 +01:00
Lovell Fuller
b5fddd7c5e Install: log node-addon-api version if available 2024-05-10 10:43:15 +01:00
Lovell Fuller
da655a1859 Reduce default concurrency for musl thread over-subscription
https://github.com/lovell/sharp-libvips/issues/229
2024-04-30 19:23:44 +01:00
Lovell Fuller
93c615d39f Bump devDeps 2024-04-30 19:23:36 +01:00
Lovell Fuller
f325dc3ec9 Refactor to use C++ boolean literals rather than macros 2024-04-30 19:23:31 +01:00
Skyf0l
0fde71c783 Ensure all contributor email addresses are valid (#4088) 2024-04-28 18:17:44 +01:00
Lovell Fuller
9c8fbaa1cc Issue template: fix markdown links from a9e662e 2024-04-25 11:31:45 +01:00
Lovell Fuller
a9e662e612 Issue template: add a couple of the most commonly-reported problems
This should allow more people to help themselves and prevent
extraneous issues from being opened.
2024-04-25 11:25:14 +01:00
Lovell Fuller
ba843002be Docs: use libc flag for npm Linux cross-installs 2024-04-24 08:30:50 +01:00
Lovell Fuller
7f3d452bc5 Docs: changelog and credit for #4074 2024-04-19 15:21:21 +01:00
Kleis Auke Wolthuizen
aa8bc19362 Simplify StaySequential operation (#4074) 2024-04-19 14:47:49 +01:00
Lovell Fuller
36e60bf040 CI: Upgrade linux-s390x to Debian 11
Debian LTS does not include s390x and Debian 10 is EOL so this
platform will now require a minimum version of glibc 2.31.
2024-04-19 13:13:48 +01:00
Lovell Fuller
a1309aa3b8 Tighten constructor text property validation #4071 2024-04-19 12:48:47 +01:00
Lovell Fuller
3e8a0fc522 Ensure only one StaySequential operation per pipeline
This should reduce memory consumption for more complex pipelines
that combine multiple operations without sequential access support
e.g. flip and Gaussian blur.
2024-04-19 11:27:54 +01:00
Lovell Fuller
397ee492d9 Ensure extend op is sequential for multi-page TIFF #4069 2024-04-18 11:05:04 +01:00
Lovell Fuller
52b9dc0f63 Docs: add a few more stop-words 2024-04-11 10:31:38 +01:00
Lovell Fuller
a715c73fc2 Docs: remove GA, allow for possible use of CF 2024-04-11 10:23:27 +01:00
Lovell Fuller
579cf93030 Install: advanced option to force global libvips #4060 2024-04-10 09:25:53 +01:00
Lovell Fuller
f67228e5ea Remove experimental status from pipelineColourspace 2024-04-09 22:32:17 +01:00
Lovell Fuller
6257994746 Expose bilinear resize kernel and improve docs #4061 2024-04-09 22:21:52 +01:00
Lovell Fuller
7950fc0ea3 Docs: changelog entry for #4048 2024-04-02 19:23:11 +01:00
Lovell Fuller
fc93ab3b82 Bump devDeps 2024-04-02 19:22:42 +01:00
ike
0981b24f60 TypeScript: add missing definitions for OverlayOptions (#4048) 2024-04-02 14:42:10 +01:00
Lovell Fuller
02fd565476 CI: bump write-file-action 2024-03-29 13:01:51 +00:00
Lovell Fuller
e55bb93b10 Bump devDeps 2024-03-29 13:01:32 +00:00
Lovell Fuller
55466f122c Release v0.33.3 2024-03-23 11:58:44 +00:00
Lovell Fuller
ede8217ab3 Prerelease v0.33.3-rc.0 2024-03-22 12:41:51 +00:00
Lovell Fuller
2689fb4e65 Bump deps 2024-03-22 11:19:08 +00:00
Lovell Fuller
eaf31a59e5 Docs: changelog and credit for #4036 2024-03-21 18:34:35 +00:00
Lovell Fuller
aa1bbcb5c1 Guard heif bitdepth property for prebuilt binaries 2024-03-21 18:33:57 +00:00
Mert
3c26080c39 Add bitdepth option to heif output (#4036)
Prebuilt binaries support only AVIF with a bitdepth of 8
2024-03-21 14:36:17 +00:00
Lovell Fuller
dc07fd4e9c Upgrade to libvips v8.15.2 2024-03-18 15:55:01 +00:00
Lovell Fuller
7bc74feb11 Ensure clone takes deep copy of options #4029 2024-03-17 09:42:21 +00:00
Lovell Fuller
c5f318ed4d Docs: add changelog and credit for #4028 2024-03-17 09:10:42 +00:00
Aaron Che
8fbb1cd154 Ensure text.wrap property can accept word-char as value (#4028) 2024-03-17 08:15:03 +00:00
Lovell Fuller
88aee8a887 Bump devDeps 2024-03-06 21:00:44 +00:00
Lovell Fuller
0f77b18078 Rename internal property name to better reflect use
This property is used for the processing pipeline colourspace
rather than the input colourspace, so the name was confusing.
2024-03-06 20:49:47 +00:00
Lovell Fuller
3eeaee71c0 Ensure pipelineColourspace handles CMYK profiles #3906 2024-03-06 20:20:04 +00:00
Lovell Fuller
045d54e2e6 Docs: include use of fetch() in Stream-based example 2024-02-28 13:15:47 +00:00
Lovell Fuller
75fedf1b75 Ensure keepIccProfile retains P3 input profiles #4008 2024-02-26 15:23:50 +00:00
Lovell Fuller
debdacb726 Bump deps 2024-02-23 09:11:35 +00:00
Lovell Fuller
1c8ae67ed2 CI: Upgrade OS on Linux ARM64 runners 2024-02-23 09:08:11 +00:00
Lovell Fuller
0eb57698ec Docs: clarify how to achieve 16-bit PNG output 2024-02-14 10:03:18 +00:00
Lovell Fuller
bc95531f2d Docs: clarify skipBlanks default for tile-based output 2024-02-13 22:40:10 +00:00
Lovell Fuller
60f4048d6c CI: Upgrade to Python 3.12 2024-02-11 20:45:16 +00:00
Lovell Fuller
fb70fbb09f Ensure keepIccProfile retains CMYK input profiles #3906 2024-02-11 20:10:18 +00:00
idchlife
fc439bedf1 Docs: add section to help those bundling via Vite 2024-02-08 09:31:28 +00:00
Lovell Fuller
26d0b7147d Bump devDeps 2024-02-03 21:22:35 +00:00
Lovell Fuller
af6aa8a690 CI: Add macOS 14 (ARM64)
Replaces runner previously and very kindly donated by MacStadium
2024-02-01 13:21:43 +00:00
hugo-syn
bd4f1abba2 TypeScript: Fix typo s/introducted/introduced/ (#3962) 2024-01-19 11:07:41 +00:00
hugo-syn
d2656a3679 Tests: fix typo s/implict/implicit (#3961) 2024-01-19 10:30:04 +00:00
Niels de Vos
a3b45ceccc Docs: update link to new JSDoc site (#3950) 2024-01-16 08:20:45 +00:00
Lovell Fuller
0dcc7d50a8 Docs: add note about libvips 8.15.1 upgrade 2024-01-12 11:25:13 +00:00
Lovell Fuller
bcb22af034 Release v0.33.2 2024-01-12 10:58:45 +00:00
Lovell Fuller
d04dc62666 Prerelease v0.33.2-rc.1 2024-01-12 09:04:00 +00:00
Lovell Fuller
c30d355f97 CI: Fix npm smoke test expectation
libvips v8.15.1 added jfif to suffixes
2024-01-11 18:47:24 +00:00
57 changed files with 629 additions and 317 deletions

View File

@@ -24,7 +24,7 @@ jobs:
linux-arm64-glibc-node-18: linux-arm64-glibc-node-18:
resource_class: arm.medium resource_class: arm.medium
machine: machine:
image: ubuntu-2004:current image: ubuntu-2204:current
steps: steps:
- checkout - checkout
- run: | - run: |
@@ -46,7 +46,7 @@ jobs:
linux-arm64-glibc-node-20: linux-arm64-glibc-node-20:
resource_class: arm.medium resource_class: arm.medium
machine: machine:
image: ubuntu-2004:current image: ubuntu-2204:current
steps: steps:
- checkout - checkout
- run: | - run: |
@@ -69,7 +69,7 @@ jobs:
linux-arm64-musl-node-18: linux-arm64-musl-node-18:
resource_class: arm.medium resource_class: arm.medium
machine: machine:
image: ubuntu-2004:current image: ubuntu-2204:current
steps: steps:
- checkout - checkout
- run: | - run: |
@@ -87,7 +87,7 @@ jobs:
linux-arm64-musl-node-20: linux-arm64-musl-node-20:
resource_class: arm.medium resource_class: arm.medium
machine: machine:
image: ubuntu-2004:current image: ubuntu-2204:current
steps: steps:
- checkout - checkout
- run: | - run: |

View File

@@ -63,7 +63,7 @@ By way of example, the `background()` method present in v0.20.0 was deprecated i
## Documentation ## Documentation
The public API is documented with [JSDoc](http://usejsdoc.org/) annotated comments. The public API is documented with [JSDoc](https://jsdoc.app/) annotated comments.
These can be converted to Markdown by running: These can be converted to Markdown by running:
```sh ```sh

View File

@@ -32,6 +32,22 @@ If you are using another package which depends on a version of `sharp` that is n
<!-- Please provide output of the above command here. --> <!-- Please provide output of the above command here. -->
### Does this problem relate to file caching?
The default behaviour of libvips is to cache input files, which can lead to `EBUSY` or `EPERM` errors on Windows.
Use [`sharp.cache(false)`](https://sharp.pixelplumbing.com/api-utility#cache) to switch this feature off.
- [ ] Adding `sharp.cache(false)` does not fix this problem.
### Does this problem relate to images appearing to have been rotated by 90 degrees?
Images that contain EXIF Orientation metadata are not auto-oriented. By default, EXIF metadata is removed.
- To auto-orient pixel values use the parameter-less [`rotate()`](https://sharp.pixelplumbing.com/api-operation#rotate) operation.
- To retain EXIF Orientation use [`keepExif()`](https://sharp.pixelplumbing.com/api-output#keepexif).
- [ ] Using `rotate()` or `keepExif()` does not fix this problem.
### What are the steps to reproduce? ### What are the steps to reproduce?
<!-- Please enter steps to reproduce here. --> <!-- Please enter steps to reproduce here. -->

View File

@@ -47,6 +47,17 @@ jobs:
nodejs_version: "^20.3.0" nodejs_version: "^20.3.0"
nodejs_version_major: 20 nodejs_version_major: 20
platform: darwin-x64 platform: darwin-x64
- os: macos-14
nodejs_arch: arm64
nodejs_version: "^18.17.0"
nodejs_version_major: 18
platform: darwin-arm64
prebuild: true
- os: macos-14
nodejs_arch: arm64
nodejs_version: "^20.3.0"
nodejs_version_major: 20
platform: darwin-arm64
- os: windows-2019 - os: windows-2019
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
@@ -80,12 +91,12 @@ jobs:
run: apk add build-base git python3 font-noto --update-cache run: apk add build-base git python3 font-noto --update-cache
- name: Dependencies (Python 3.11 - macOS, Windows) - name: Dependencies (Python 3.11 - macOS, Windows)
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows') if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.11' python-version: "3.12"
- name: Dependencies (Node.js) - name: Dependencies (Node.js)
if: "!contains(matrix.platform, 'linuxmusl')" if: "!contains(matrix.platform, 'linuxmusl')"
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.nodejs_version }} node-version: ${{ matrix.nodejs_version }}
architecture: ${{ matrix.nodejs_arch }} architecture: ${{ matrix.nodejs_arch }}
@@ -120,12 +131,14 @@ jobs:
matrix: matrix:
include: include:
- platform: linux-arm - platform: linux-arm
distro: buster
run_on_arch: armv6 run_on_arch: armv6
nodejs_arch: armv6l nodejs_arch: armv6l
nodejs_hostname: unofficial-builds.nodejs.org nodejs_hostname: unofficial-builds.nodejs.org
nodejs_version: "18.17.0" nodejs_version: "18.17.0"
nodejs_version_major: 18 nodejs_version_major: 18
- platform: linux-s390x - platform: linux-s390x
distro: bullseye
run_on_arch: s390x run_on_arch: s390x
nodejs_arch: s390x nodejs_arch: s390x
nodejs_hostname: nodejs.org nodejs_hostname: nodejs.org
@@ -136,7 +149,7 @@ jobs:
- uses: uraimo/run-on-arch-action@v2 - uses: uraimo/run-on-arch-action@v2
with: with:
arch: ${{ matrix.run_on_arch }} arch: ${{ matrix.run_on_arch }}
distro: buster distro: ${{ matrix.distro }}
env: | env: |
prebuild_upload: "${{ startsWith(github.ref, 'refs/tags/') && secrets.GITHUB_TOKEN || '' }}" prebuild_upload: "${{ startsWith(github.ref, 'refs/tags/') && secrets.GITHUB_TOKEN || '' }}"
run: | run: |
@@ -158,14 +171,14 @@ jobs:
contents: write contents: write
name: wasm32 - prebuild name: wasm32 - prebuild
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
container: "emscripten/emsdk:3.1.51" container: "emscripten/emsdk:3.1.56"
steps: steps:
- name: Checkout - 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)
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: "20" node-version: "20"
- name: Install - name: Install
@@ -193,48 +206,3 @@ jobs:
npm_config_nodedir: emscripten npm_config_nodedir: emscripten
prebuild_upload: ${{ secrets.GITHUB_TOKEN }} prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
run: cd src && ln -s ../package.json && emmake npx prebuild --platform=emscripten --arch=wasm32 --strip=0 run: cd src && ln -s ../package.json && emmake npx prebuild --platform=emscripten --arch=wasm32 --strip=0
macstadium-runner:
permissions:
contents: write
name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} ${{ matrix.prebuild && '- prebuild' }}
runs-on: macos-m1
strategy:
fail-fast: false
matrix:
include:
- nodejs_arch: x64
nodejs_version: "^18.17.0"
nodejs_version_major: 18
platform: darwin-x64
- nodejs_arch: arm64
nodejs_version: "^18.17.0"
nodejs_version_major: 18
platform: darwin-arm64
prebuild: true
defaults:
run:
shell: /usr/bin/arch -arch arm64e /bin/bash -l {0}
steps:
- name: Dependencies (Node.js)
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.nodejs_version }}
architecture: ${{ matrix.nodejs_arch }}
- name: Checkout
uses: actions/checkout@v4
- name: Install
run: npm install --build-from-source
- name: Test
run: npm test
- name: Test packaging
run: |
npm run package-from-local-build
npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}"
npm run clean
npm install --ignore-scripts
npm test
- name: Prebuild
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
env:
prebuild_upload: ${{ secrets.GITHUB_TOKEN }}
run: cd src && ln -s ../package.json && npx prebuild

View File

@@ -84,7 +84,7 @@ jobs:
steps: steps:
- name: Install Node.js - name: Install Node.js
if: ${{ matrix.runtime == 'node' }} if: ${{ matrix.runtime == 'node' }}
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: 20 node-version: 20
- name: Install pnpm - name: Install pnpm
@@ -110,7 +110,7 @@ jobs:
script: | script: |
core.setOutput('semver', context.ref.replace('refs/tags/v','')) core.setOutput('semver', context.ref.replace('refs/tags/v',''))
- name: Create package.json - name: Create package.json
uses: DamianReeves/write-file-action@v1.2 uses: DamianReeves/write-file-action@v1.3
with: with:
path: package.json path: package.json
contents: | contents: |
@@ -121,13 +121,13 @@ jobs:
"type": "module" "type": "module"
} }
- name: Create release.mjs - name: Create release.mjs
uses: DamianReeves/write-file-action@v1.2 uses: DamianReeves/write-file-action@v1.3
with: with:
path: release.mjs path: release.mjs
contents: | contents: |
import { deepStrictEqual } from 'node:assert'; import { deepStrictEqual } from 'node:assert';
import sharp from 'sharp'; import sharp from 'sharp';
deepStrictEqual(['.jpg', '.jpeg', '.jpe'], sharp.format.jpeg.input.fileSuffix); deepStrictEqual(['.jpg', '.jpeg', '.jpe', '.jfif'], sharp.format.jpeg.input.fileSuffix);
- name: Run with Node.js + npm - name: Run with Node.js + npm
if: ${{ matrix.package-manager == 'npm' }} if: ${{ matrix.package-manager == 'npm' }}

View File

@@ -66,8 +66,6 @@ The input image will be converted to the provided colourspace at the start of th
All operations will use this colourspace before converting to the output colourspace, All operations will use this colourspace before converting to the output colourspace,
as defined by [toColourspace](#tocolourspace). as defined by [toColourspace](#tocolourspace).
This feature is experimental and has not yet been fully-tested with all operations.
**Throws**: **Throws**:

View File

@@ -66,7 +66,7 @@ where the overall height is the `pageHeight` multiplied by the number of `pages`
| [options.text.dpi] | <code>number</code> | <code>72</code> | the resolution (size) at which to render the text. Does not take effect if `height` is specified. | | [options.text.dpi] | <code>number</code> | <code>72</code> | the resolution (size) at which to render the text. Does not take effect if `height` is specified. |
| [options.text.rgba] | <code>boolean</code> | <code>false</code> | set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`. | | [options.text.rgba] | <code>boolean</code> | <code>false</code> | set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`. |
| [options.text.spacing] | <code>number</code> | <code>0</code> | text line height in points. Will use the font line height if none is specified. | | [options.text.spacing] | <code>number</code> | <code>0</code> | text line height in points. Will use the font line height if none is specified. |
| [options.text.wrap] | <code>string</code> | <code>&quot;&#x27;word&#x27;&quot;</code> | word wrapping style when width is provided, one of: 'word', 'char', 'charWord' (prefer char, fallback to word) or 'none'. | | [options.text.wrap] | <code>string</code> | <code>&quot;&#x27;word&#x27;&quot;</code> | word wrapping style when width is provided, one of: 'word', 'char', 'word-char' (prefer word, fallback to char) or 'none'. |
**Example** **Example**
```js ```js
@@ -79,14 +79,16 @@ sharp('input.jpg')
``` ```
**Example** **Example**
```js ```js
// Read image data from readableStream, // Read image data from remote URL,
// resize to 300 pixels wide, // resize to 300 pixels wide,
// emit an 'info' event with calculated dimensions // emit an 'info' event with calculated dimensions
// and finally write image data to writableStream // and finally write image data to writableStream
var transformer = sharp() const { body } = fetch('https://...');
const readableStream = Readable.fromWeb(body);
const transformer = sharp()
.resize(300) .resize(300)
.on('info', function(info) { .on('info', ({ height }) => {
console.log('Image height is ' + info.height); console.log(`Image height is ${height}`);
}); });
readableStream.pipe(transformer).pipe(writableStream); readableStream.pipe(transformer).pipe(writableStream);
``` ```

View File

@@ -367,10 +367,14 @@ const data = await sharp(input)
Use these PNG options for output image. Use these PNG options for output image.
By default, PNG output is full colour at 8 or 16 bits per pixel. By default, PNG output is full colour at 8 bits per pixel.
Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel. Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
Set `palette` to `true` for slower, indexed PNG output. Set `palette` to `true` for slower, indexed PNG output.
For 16 bits per pixel output, convert to `rgb16` via
[toColourspace](/api-colour#tocolourspace).
**Throws**: **Throws**:
@@ -405,6 +409,14 @@ const data = await sharp(input)
.png({ palette: true }) .png({ palette: true })
.toBuffer(); .toBuffer();
``` ```
**Example**
```js
// Output 16 bits per pixel RGB(A)
const data = await sharp(input)
.toColourspace('rgb16')
.png()
.toBuffer();
```
## webp ## webp
@@ -605,6 +617,7 @@ sharp('input.svg')
Use these AVIF options for output image. 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.
**Throws**: **Throws**:
@@ -620,6 +633,7 @@ AVIF image sequences are not supported.
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression | | [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression |
| [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 9 (slowest) | | [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 9 (slowest) |
| [options.chromaSubsampling] | <code>string</code> | <code>&quot;&#x27;4:4:4&#x27;&quot;</code> | set to '4:2:0' to use chroma subsampling | | [options.chromaSubsampling] | <code>string</code> | <code>&quot;&#x27;4:4:4&#x27;&quot;</code> | set to '4:2:0' to use chroma subsampling |
| [options.bitdepth] | <code>number</code> | <code>8</code> | set bitdepth to 8, 10 or 12 bit |
**Example** **Example**
```js ```js
@@ -658,6 +672,7 @@ globally-installed libvips compiled with support for libheif, libde265 and x265.
| [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression | | [options.lossless] | <code>boolean</code> | <code>false</code> | use lossless compression |
| [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 9 (slowest) | | [options.effort] | <code>number</code> | <code>4</code> | CPU effort, between 0 (fastest) and 9 (slowest) |
| [options.chromaSubsampling] | <code>string</code> | <code>&quot;&#x27;4:4:4&#x27;&quot;</code> | set to '4:2:0' to use chroma subsampling | | [options.chromaSubsampling] | <code>string</code> | <code>&quot;&#x27;4:4:4&#x27;&quot;</code> | set to '4:2:0' to use chroma subsampling |
| [options.bitdepth] | <code>number</code> | <code>8</code> | set bitdepth to 8, 10 or 12 bit |
**Example** **Example**
```js ```js
@@ -763,7 +778,7 @@ The prebuilt binaries do not include this - see
| [options.angle] | <code>number</code> | <code>0</code> | tile angle of rotation, must be a multiple of 90. | | [options.angle] | <code>number</code> | <code>0</code> | tile angle of rotation, must be a multiple of 90. |
| [options.background] | <code>string</code> \| <code>Object</code> | <code>&quot;{r: 255, g: 255, b: 255, alpha: 1}&quot;</code> | background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency. | | [options.background] | <code>string</code> \| <code>Object</code> | <code>&quot;{r: 255, g: 255, b: 255, alpha: 1}&quot;</code> | background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency. |
| [options.depth] | <code>string</code> | | how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout. | | [options.depth] | <code>string</code> | | how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout. |
| [options.skipBlanks] | <code>number</code> | <code>-1</code> | threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images | | [options.skipBlanks] | <code>number</code> | <code>-1</code> | Threshold to skip tile generation. Range is 0-255 for 8-bit images, 0-65535 for 16-bit images. Default is 5 for `google` layout, -1 (no skip) otherwise. |
| [options.container] | <code>string</code> | <code>&quot;&#x27;fs&#x27;&quot;</code> | tile container, with value `fs` (filesystem) or `zip` (compressed file). | | [options.container] | <code>string</code> | <code>&quot;&#x27;fs&#x27;&quot;</code> | tile container, with value `fs` (filesystem) or `zip` (compressed file). |
| [options.layout] | <code>string</code> | <code>&quot;&#x27;dz&#x27;&quot;</code> | filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`. | | [options.layout] | <code>string</code> | <code>&quot;&#x27;dz&#x27;&quot;</code> | filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`. |
| [options.centre] | <code>boolean</code> | <code>false</code> | centre image in tile. | | [options.centre] | <code>boolean</code> | <code>false</code> | centre image in tile. |

View File

@@ -21,18 +21,22 @@ When using a **fit** of `cover` or `contain`, the default **position** is `centr
Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property. Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property.
The experimental strategy-based approach resizes so one dimension is at its target length The strategy-based approach initially 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. 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](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29). - `entropy`: focus on the region with the highest [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29).
- `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones. - `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
Possible interpolation kernels are: Possible downsizing kernels are:
- `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation). - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
- `linear`: Use a [triangle filter](https://en.wikipedia.org/wiki/Triangular_function).
- `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline). - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
- `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf). - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
- `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`. - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
- `lanczos3`: Use a Lanczos kernel with `a=3` (the default). - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
Downsampling kernels without a matching upsampling interpolator map to `cubic`.
Only one resize can occur per pipeline. Only one resize can occur per pipeline.
Previous calls to `resize` in the same pipeline will be ignored. Previous calls to `resize` in the same pipeline will be ignored.
@@ -52,7 +56,7 @@ Previous calls to `resize` in the same pipeline will be ignored.
| [options.fit] | <code>String</code> | <code>&#x27;cover&#x27;</code> | How the image should be resized/cropped to fit the target dimension(s), one of `cover`, `contain`, `fill`, `inside` or `outside`. | | [options.fit] | <code>String</code> | <code>&#x27;cover&#x27;</code> | How the image should be resized/cropped to fit the target dimension(s), one of `cover`, `contain`, `fill`, `inside` or `outside`. |
| [options.position] | <code>String</code> | <code>&#x27;centre&#x27;</code> | A position, gravity or strategy to use when `fit` is `cover` or `contain`. | | [options.position] | <code>String</code> | <code>&#x27;centre&#x27;</code> | A position, gravity or strategy to use when `fit` is `cover` or `contain`. |
| [options.background] | <code>String</code> \| <code>Object</code> | <code>{r: 0, g: 0, b: 0, alpha: 1}</code> | background colour when `fit` is `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency. | | [options.background] | <code>String</code> \| <code>Object</code> | <code>{r: 0, g: 0, b: 0, alpha: 1}</code> | background colour when `fit` is `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency. |
| [options.kernel] | <code>String</code> | <code>&#x27;lanczos3&#x27;</code> | The kernel to use for image reduction. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load. | | [options.kernel] | <code>String</code> | <code>&#x27;lanczos3&#x27;</code> | The kernel to use for image reduction and the inferred interpolator to use for upsampling. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load. |
| [options.withoutEnlargement] | <code>Boolean</code> | <code>false</code> | Do not scale up if the width *or* height are already less than the target dimensions, equivalent to GraphicsMagick's `>` geometry option. This may result in output dimensions smaller than the target dimensions. | | [options.withoutEnlargement] | <code>Boolean</code> | <code>false</code> | Do not scale up if the width *or* height are already less than the target dimensions, equivalent to GraphicsMagick's `>` geometry option. This may result in output dimensions smaller than the target dimensions. |
| [options.withoutReduction] | <code>Boolean</code> | <code>false</code> | Do not scale down if the width *or* height are already greater than the target dimensions, equivalent to GraphicsMagick's `<` geometry option. This may still result in a crop to reach the target dimensions. | | [options.withoutReduction] | <code>Boolean</code> | <code>false</code> | Do not scale down if the width *or* height are already greater than the target dimensions, equivalent to GraphicsMagick's `<` geometry option. This may still result in a crop to reach the target dimensions. |
| [options.fastShrinkOnLoad] | <code>Boolean</code> | <code>true</code> | Take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern or round-down of an auto-scaled dimension. | | [options.fastShrinkOnLoad] | <code>Boolean</code> | <code>true</code> | Take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern or round-down of an auto-scaled dimension. |

View File

@@ -2,9 +2,60 @@
## v0.33 - *gauge* ## v0.33 - *gauge*
Requires libvips v8.15.1 Requires libvips v8.15.2
### v0.33.2 - TBD ### v0.33.4 - 16th May 2024
* Remove experimental status from `pipelineColourspace`.
* Reduce default concurrency when musl thread over-subscription detected.
* TypeScript: add missing definitions for `OverlayOptions`.
[#4048](https://github.com/lovell/sharp/pull/4048)
[@ike-gg](https://github.com/ike-gg)
* Install: add advanced option to force use of a globally-installed libvips.
[#4060](https://github.com/lovell/sharp/issues/4060)
* Expose `bilinear` resizing kernel (and interpolator).
[#4061](https://github.com/lovell/sharp/issues/4061)
* Ensure `extend` operation stays sequential for multi-page TIFF (regression in 0.32.0).
[#4069](https://github.com/lovell/sharp/issues/4069)
* Tighten validation of constructor `text` integer properties.
[#4071](https://github.com/lovell/sharp/issues/4071)
* Simplify internal StaySequential logic.
[#4074](https://github.com/lovell/sharp/pull/4074)
[@kleisauke](https://github.com/kleisauke)
* Ensure negate operation occurs after profile conversion.
[#4096](https://github.com/lovell/sharp/pull/4096)
[@adriaanmeuris](https://github.com/adriaanmeuris)
### v0.33.3 - 23rd March 2024
* Upgrade to libvips v8.15.2 for upstream bug fixes.
* Ensure `keepIccProfile` retains P3 and CMYK input profiles.
[#3906](https://github.com/lovell/sharp/issues/3906)
[#4008](https://github.com/lovell/sharp/issues/4008)
* Ensure `text.wrap` property can accept `word-char` as value.
[#4028](https://github.com/lovell/sharp/pull/4028)
[@yolopunk](https://github.com/yolopunk)
* Ensure `clone` takes a deep copy of existing options.
[#4029](https://github.com/lovell/sharp/issues/4029)
* Add `bitdepth` option to `heif` output (prebuilt binaries support 8-bit only).
[#4036](https://github.com/lovell/sharp/pull/4036)
[@mertalev](https://github.com/mertalev)
### v0.33.2 - 12th January 2024
* Upgrade to libvips v8.15.1 for upstream bug fixes.
* TypeScript: add definition for `keepMetadata`. * TypeScript: add definition for `keepMetadata`.
[#3914](https://github.com/lovell/sharp/pull/3914) [#3914](https://github.com/lovell/sharp/pull/3914)

View File

@@ -284,3 +284,12 @@ GitHub: https://github.com/RReverser
Name: Tamás András Horváth Name: Tamás András Horváth
GitHub: https://github.com/icetee GitHub: https://github.com/icetee
Name: Aaron Che
GitHub: https://github.com/yolopunk
Name: Mert Alev
GitHub: https://github.com/mertalev
Name: Adriaan Meuris
GitHub: https://github.com/adriaanmeuris

View File

@@ -7,15 +7,13 @@
<meta name="description" content="Resize large images in common formats to smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions"> <meta name="description" content="Resize large images in common formats to smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions">
<meta property="og:title" content="sharp - High performance Node.js image processing"> <meta property="og:title" content="sharp - High performance Node.js image processing">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo-600.png"> <meta property="og:image" content="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo-600.png">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; object-src 'none'; style-src 'unsafe-inline'; <meta http-equiv="Content-Security-Policy" content="default-src 'self'; connect-src 'self'; object-src 'none';
img-src 'unsafe-inline' data: https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com; style-src 'unsafe-inline';
connect-src 'self' https://www.google-analytics.com; img-src 'unsafe-inline' data: https://cdn.jsdelivr.net/gh/lovell/;
script-src 'self' 'unsafe-inline' 'unsafe-eval' script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com/beacon.min.js;">
https://www.google-analytics.com/analytics.js;">
<link rel="icon" type="image/svg+xml" href="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg"> <link rel="icon" type="image/svg+xml" href="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg">
<link rel="icon" type="image/png" sizes="32x32" href="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo-32.png"> <link rel="icon" type="image/png" sizes="32x32" href="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo-32.png">
<link rel="author" href="/humans.txt" type="text/plain"> <link rel="author" href="/humans.txt" type="text/plain">
<link rel="dns-prefetch" href="https://www.google-analytics.com">
<script type="application/ld+json"> <script type="application/ld+json">
{ {
"@context": "https://schema.org", "@context": "https://schema.org",
@@ -67,7 +65,6 @@
<div id="docute"></div> <div id="docute"></div>
<script src="docute.min.js"></script> <script src="docute.min.js"></script>
<script> <script>
/* docute-google-analytics@1/dist/index.min.js */ !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).docuteGoogleAnalytics=n()}(this,function(){"use strict";function e(e){var n;window.ga||((n=document.createElement("script")).async=!0,n.src="https://www.google-analytics.com/analytics.js",document.body.appendChild(n),window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)},ga.l=Number(new Date),ga("create",e,"auto"))}function n(n,t){e(t),ga("set","page",n),ga("send","pageview")}return function(e){return{name:"@google-analytics",extend:function(t){!function(e,t){"function"==typeof e?e(function(e){n(e,t)}):e.afterEach(function(e){n(e.fullPath,t)})}(t.router,e)}}}});
const docuteApiTitlePlugin = { const docuteApiTitlePlugin = {
name: 'apiTitle', name: 'apiTitle',
extend(api) { extend(api) {
@@ -126,7 +123,6 @@
detectSystemDarkTheme: true, detectSystemDarkTheme: true,
footer: '<a href="https://pixelplumbing.com/" target="_blank">pixelplumbing.com<a>', footer: '<a href="https://pixelplumbing.com/" target="_blank">pixelplumbing.com<a>',
plugins: [ plugins: [
docuteGoogleAnalytics('UA-13034748-12'),
docuteApiTitlePlugin, docuteApiTitlePlugin,
docuteApiSearchPlugin docuteApiSearchPlugin
], ],

View File

@@ -45,7 +45,7 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
* macOS ARM64 * macOS ARM64
* Linux ARM (glibc >= 2.28) * Linux ARM (glibc >= 2.28)
* Linux ARM64 (glibc >= 2.26, musl >= 1.2.2) * Linux ARM64 (glibc >= 2.26, musl >= 1.2.2)
* Linux s390x (glibc >= 2.28) * Linux s390x (glibc >= 2.31)
* 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
@@ -67,15 +67,17 @@ within the same installation tree and/or using the same lockfile.
Provides limited support via `--os`, `--cpu` and `--libc` flags. Provides limited support via `--os`, `--cpu` and `--libc` flags.
Example to support both Intel and ARM CPUs on macOS: To support macOS with Intel x64 and ARM64 CPUs:
```sh ```sh
npm install --cpu=x64 --os=darwin sharp npm install --cpu=x64 --os=darwin sharp
npm install --cpu=arm64 --os=darwin sharp npm install --cpu=arm64 --os=darwin sharp
``` ```
Example to support both glibc and musl-based Linux: When the cross-target is Linux, the C standard library must be specified.
To support glibc (e.g. Debian) and musl (e.g. Alpine) Linux with Intel x64 CPUs:
```sh ```sh
npm install --cpu=x64 --os=linux sharp npm install --cpu=x64 --os=linux --libc=glibc sharp
npm install --cpu=x64 --os=linux --libc=musl sharp npm install --cpu=x64 --os=linux --libc=musl sharp
``` ```
@@ -103,9 +105,14 @@ and on macOS when running Node.js under Rosetta.
This module will be compiled from source at `npm install` time when: This module will be compiled from source at `npm install` time when:
* a globally-installed libvips is detected (set the `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable to skip this), or * a globally-installed libvips is detected, or
* when the `npm install --build-from-source` flag is used. * when the `npm install --build-from-source` flag is used.
The logic to detect a globally-installed libvips can be skipped by setting the
`SHARP_IGNORE_GLOBAL_LIBVIPS` (never try to use it) or
`SHARP_FORCE_GLOBAL_LIBVIPS` (always try to use it, even when missing or outdated)
environment variables.
Building from source requires: Building from source requires:
* C++11 compiler * C++11 compiler
@@ -249,6 +256,26 @@ option.
} }
``` ```
### vite
Ensure `sharp` is excluded from bundling via the
[build.rollupOptions](https://vitejs.dev/config/build-options.html)
configuration.
```js
import { defineConfig } from 'vite';
export default defineConfig({
build: {
rollupOptions: {
external: [
"sharp"
]
}
}
});
```
## TypeScript ## TypeScript
TypeScript definitions are published as part of TypeScript definitions are published as part of

File diff suppressed because one or more lines are too long

View File

@@ -30,6 +30,7 @@ module.exports = [
'current', 'current',
'date', 'date',
'default', 'default',
'deprecated',
'does', 'does',
'each', 'each',
'either', 'either',
@@ -46,6 +47,7 @@ module.exports = [
'given', 'given',
'has', 'has',
'have', 'have',
'helps',
'how', 'how',
'image', 'image',
'implies', 'implies',
@@ -58,6 +60,7 @@ module.exports = [
'lots', 'lots',
'make', 'make',
'may', 'may',
'meaning',
'more', 'more',
'most', 'most',
'much', 'much',
@@ -79,6 +82,7 @@ module.exports = [
'pre', 'pre',
'previously', 'previously',
'produce', 'produce',
'proper',
'provide', 'provide',
'provided', 'provided',
'ready', 'ready',
@@ -116,6 +120,7 @@ module.exports = [
'unless', 'unless',
'unmaintained', 'unmaintained',
'unsuitable', 'unsuitable',
'unsupported',
'until', 'until',
'use', 'use',
'used', 'used',

View File

@@ -10,8 +10,8 @@ try {
log(msg); log(msg);
log('Attempting to build from source via node-gyp'); log('Attempting to build from source via node-gyp');
try { try {
require('node-addon-api'); const addonApi = require('node-addon-api');
log('Found node-addon-api'); log(`Found node-addon-api ${addonApi.version || ''}`);
} catch (err) { } catch (err) {
log('Please add node-addon-api to your dependencies'); log('Please add node-addon-api to your dependencies');
return; return;

View File

@@ -71,8 +71,6 @@ function grayscale (grayscale) {
* All operations will use this colourspace before converting to the output colourspace, * All operations will use this colourspace before converting to the output colourspace,
* as defined by {@link #tocolourspace|toColourspace}. * as defined by {@link #tocolourspace|toColourspace}.
* *
* This feature is experimental and has not yet been fully-tested with all operations.
*
* @since 0.29.0 * @since 0.29.0
* *
* @example * @example
@@ -90,7 +88,7 @@ function pipelineColourspace (colourspace) {
if (!is.string(colourspace)) { if (!is.string(colourspace)) {
throw is.invalidParameterError('colourspace', 'string', colourspace); throw is.invalidParameterError('colourspace', 'string', colourspace);
} }
this.options.colourspaceInput = colourspace; this.options.colourspacePipeline = colourspace;
return this; return this;
} }

View File

@@ -40,14 +40,16 @@ const debuglog = util.debuglog('sharp');
* }); * });
* *
* @example * @example
* // Read image data from readableStream, * // Read image data from remote URL,
* // resize to 300 pixels wide, * // resize to 300 pixels wide,
* // emit an 'info' event with calculated dimensions * // emit an 'info' event with calculated dimensions
* // and finally write image data to writableStream * // and finally write image data to writableStream
* var transformer = sharp() * const { body } = fetch('https://...');
* const readableStream = Readable.fromWeb(body);
* const transformer = sharp()
* .resize(300) * .resize(300)
* .on('info', function(info) { * .on('info', ({ height }) => {
* console.log('Image height is ' + info.height); * console.log(`Image height is ${height}`);
* }); * });
* readableStream.pipe(transformer).pipe(writableStream); * readableStream.pipe(transformer).pipe(writableStream);
* *
@@ -164,7 +166,7 @@ const debuglog = util.debuglog('sharp');
* @param {number} [options.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified. * @param {number} [options.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
* @param {boolean} [options.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`. * @param {boolean} [options.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`.
* @param {number} [options.text.spacing=0] - text line height in points. Will use the font line height if none is specified. * @param {number} [options.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
* @param {string} [options.text.wrap='word'] - word wrapping style when width is provided, one of: 'word', 'char', 'charWord' (prefer char, fallback to word) or 'none'. * @param {string} [options.text.wrap='word'] - word wrapping style when width is provided, one of: 'word', 'char', 'word-char' (prefer word, fallback to char) or 'none'.
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
@@ -255,7 +257,7 @@ const Sharp = function (input, options) {
removeAlpha: false, removeAlpha: false,
ensureAlpha: -1, ensureAlpha: -1,
colourspace: 'srgb', colourspace: 'srgb',
colourspaceInput: 'last', colourspacePipeline: 'last',
composite: [], composite: [],
// output // output
fileOut: '', fileOut: '',
@@ -323,6 +325,7 @@ const Sharp = function (input, options) {
heifCompression: 'av1', heifCompression: 'av1',
heifEffort: 4, heifEffort: 4,
heifChromaSubsampling: '4:4:4', heifChromaSubsampling: '4:4:4',
heifBitdepth: 8,
jxlDistance: 1, jxlDistance: 1,
jxlDecodingTier: 0, jxlDecodingTier: 0,
jxlEffort: 7, jxlEffort: 7,
@@ -423,13 +426,16 @@ Object.setPrototypeOf(Sharp, stream.Duplex);
function clone () { function clone () {
// Clone existing options // Clone existing options
const clone = this.constructor.call(); const clone = this.constructor.call();
clone.options = Object.assign({}, this.options); const { debuglog, queueListener, ...options } = this.options;
clone.options = structuredClone(options);
clone.options.debuglog = debuglog;
clone.options.queueListener = queueListener;
// Pass 'finish' event to clone for Stream-based input // Pass 'finish' event to clone for Stream-based input
if (this._isStreamInput()) { if (this._isStreamInput()) {
this.on('finish', () => { this.on('finish', () => {
// Clone inherits input data // Clone inherits input data
this._flattenBufferIn(); this._flattenBufferIn();
clone.options.bufferIn = this.options.bufferIn; clone.options.input.buffer = this.options.input.buffer;
clone.emit('finish'); clone.emit('finish');
}); });
} }

16
lib/index.d.ts vendored
View File

@@ -1017,7 +1017,7 @@ declare namespace sharp {
rgba?: boolean; rgba?: boolean;
/** Text line height in points. Will use the font line height if none is specified. (optional, default `0`) */ /** Text line height in points. Will use the font line height if none is specified. (optional, default `0`) */
spacing?: number; spacing?: number;
/** Word wrapping style when width is provided, one of: 'word', 'char', 'charWord' (prefer char, fallback to word) or 'none' */ /** Word wrapping style when width is provided, one of: 'word', 'char', 'word-char' (prefer word, fallback to char) or 'none' */
wrap?: TextWrap; wrap?: TextWrap;
} }
@@ -1244,6 +1244,8 @@ declare namespace sharp {
effort?: number | undefined; effort?: number | undefined;
/** set to '4:2:0' to use chroma subsampling, requires libvips v8.11.0 (optional, default '4:4:4') */ /** set to '4:2:0' to use chroma subsampling, requires libvips v8.11.0 (optional, default '4:4:4') */
chromaSubsampling?: string | undefined; chromaSubsampling?: string | undefined;
/** Set bitdepth to 8, 10 or 12 bit (optional, default 8) */
bitdepth?: 8 | 10 | 12 | undefined;
} }
interface HeifOptions extends OutputOptions { interface HeifOptions extends OutputOptions {
@@ -1257,6 +1259,8 @@ declare namespace sharp {
effort?: number | undefined; effort?: number | undefined;
/** set to '4:2:0' to use chroma subsampling (optional, default '4:4:4') */ /** set to '4:2:0' to use chroma subsampling (optional, default '4:4:4') */
chromaSubsampling?: string | undefined; chromaSubsampling?: string | undefined;
/** Set bitdepth to 8, 10 or 12 bit (optional, default 8) */
bitdepth?: 8 | 10 | 12 | undefined;
} }
interface GifOptions extends OutputOptions, AnimationOptions { interface GifOptions extends OutputOptions, AnimationOptions {
@@ -1475,6 +1479,14 @@ declare namespace sharp {
tile?: boolean | undefined; tile?: boolean | undefined;
/** Set to true to avoid premultipling the image below. Equivalent to the --premultiplied vips option. */ /** Set to true to avoid premultipling the image below. Equivalent to the --premultiplied vips option. */
premultiplied?: boolean | undefined; premultiplied?: boolean | undefined;
/** number representing the DPI for vector overlay image. (optional, default 72)*/
density?: number | undefined;
/** Set to true to read all frames/pages of an animated image. (optional, default false) */
animated?: boolean | undefined;
/** see sharp() constructor, (optional, default 'warning') */
failOn?: FailOnOptions | undefined;
/** see sharp() constructor, (optional, default 268402689) */
limitInputPixels?: number | boolean | undefined;
} }
interface TileOptions { interface TileOptions {
@@ -1613,7 +1625,7 @@ declare namespace sharp {
type TextAlign = 'left' | 'centre' | 'center' | 'right'; type TextAlign = 'left' | 'centre' | 'center' | 'right';
type TextWrap = 'word' | 'char' | 'charWord' | 'none'; type TextWrap = 'word' | 'char' | 'word-char' | 'none';
type TileContainer = 'fs' | 'zip'; type TileContainer = 'fs' | 'zip';

View File

@@ -296,17 +296,17 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
} }
} }
if (is.defined(inputOptions.text.width)) { if (is.defined(inputOptions.text.width)) {
if (is.number(inputOptions.text.width)) { if (is.integer(inputOptions.text.width) && inputOptions.text.width > 0) {
inputDescriptor.textWidth = inputOptions.text.width; inputDescriptor.textWidth = inputOptions.text.width;
} else { } else {
throw is.invalidParameterError('text.textWidth', 'number', inputOptions.text.width); throw is.invalidParameterError('text.width', 'positive integer', inputOptions.text.width);
} }
} }
if (is.defined(inputOptions.text.height)) { if (is.defined(inputOptions.text.height)) {
if (is.number(inputOptions.text.height)) { if (is.integer(inputOptions.text.height) && inputOptions.text.height > 0) {
inputDescriptor.textHeight = inputOptions.text.height; inputDescriptor.textHeight = inputOptions.text.height;
} else { } else {
throw is.invalidParameterError('text.height', 'number', inputOptions.text.height); throw is.invalidParameterError('text.height', 'positive integer', inputOptions.text.height);
} }
} }
if (is.defined(inputOptions.text.align)) { if (is.defined(inputOptions.text.align)) {
@@ -324,10 +324,10 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
} }
} }
if (is.defined(inputOptions.text.dpi)) { if (is.defined(inputOptions.text.dpi)) {
if (is.number(inputOptions.text.dpi) && is.inRange(inputOptions.text.dpi, 1, 100000)) { if (is.integer(inputOptions.text.dpi) && is.inRange(inputOptions.text.dpi, 1, 1000000)) {
inputDescriptor.textDpi = inputOptions.text.dpi; inputDescriptor.textDpi = inputOptions.text.dpi;
} else { } else {
throw is.invalidParameterError('text.dpi', 'number between 1 and 100000', inputOptions.text.dpi); throw is.invalidParameterError('text.dpi', 'integer between 1 and 1000000', inputOptions.text.dpi);
} }
} }
if (is.defined(inputOptions.text.rgba)) { if (is.defined(inputOptions.text.rgba)) {
@@ -338,17 +338,17 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
} }
} }
if (is.defined(inputOptions.text.spacing)) { if (is.defined(inputOptions.text.spacing)) {
if (is.number(inputOptions.text.spacing)) { if (is.integer(inputOptions.text.spacing) && is.inRange(inputOptions.text.spacing, -1000000, 1000000)) {
inputDescriptor.textSpacing = inputOptions.text.spacing; inputDescriptor.textSpacing = inputOptions.text.spacing;
} else { } else {
throw is.invalidParameterError('text.spacing', 'number', inputOptions.text.spacing); throw is.invalidParameterError('text.spacing', 'integer between -1000000 and 1000000', inputOptions.text.spacing);
} }
} }
if (is.defined(inputOptions.text.wrap)) { if (is.defined(inputOptions.text.wrap)) {
if (is.string(inputOptions.text.wrap) && is.inArray(inputOptions.text.wrap, ['word', 'char', 'wordChar', 'none'])) { if (is.string(inputOptions.text.wrap) && is.inArray(inputOptions.text.wrap, ['word', 'char', 'word-char', 'none'])) {
inputDescriptor.textWrap = inputOptions.text.wrap; inputDescriptor.textWrap = inputOptions.text.wrap;
} else { } else {
throw is.invalidParameterError('text.wrap', 'one of: word, char, wordChar, none', inputOptions.text.wrap); throw is.invalidParameterError('text.wrap', 'one of: word, char, word-char, none', inputOptions.text.wrap);
} }
} }
delete inputDescriptor.buffer; delete inputDescriptor.buffer;

View File

@@ -162,15 +162,21 @@ const pkgConfigPath = () => {
} }
}; };
const skipSearch = (status, reason) => {
log(`Detected ${reason}, skipping search for globally-installed libvips`);
return status;
};
const useGlobalLibvips = () => { const useGlobalLibvips = () => {
if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) { if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
log('Detected SHARP_IGNORE_GLOBAL_LIBVIPS, skipping search for globally-installed libvips'); return skipSearch(false, 'SHARP_IGNORE_GLOBAL_LIBVIPS');
return false; }
if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS');
} }
/* istanbul ignore next */ /* istanbul ignore next */
if (isRosetta()) { if (isRosetta()) {
log('Detected Rosetta, skipping search for globally-installed libvips'); return skipSearch(false, 'Rosetta');
return false;
} }
const globalVipsVersion = globalLibvipsVersion(); const globalVipsVersion = globalLibvipsVersion();
return !!globalVipsVersion && /* istanbul ignore next */ return !!globalVipsVersion && /* istanbul ignore next */

View File

@@ -504,10 +504,14 @@ function jpeg (options) {
/** /**
* Use these PNG options for output image. * Use these PNG options for output image.
* *
* By default, PNG output is full colour at 8 or 16 bits per pixel. * By default, PNG output is full colour at 8 bits per pixel.
*
* Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel. * Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
* Set `palette` to `true` for slower, indexed PNG output. * Set `palette` to `true` for slower, indexed PNG output.
* *
* For 16 bits per pixel output, convert to `rgb16` via
* {@link /api-colour#tocolourspace|toColourspace}.
*
* @example * @example
* // Convert any input to full colour PNG output * // Convert any input to full colour PNG output
* const data = await sharp(input) * const data = await sharp(input)
@@ -520,6 +524,13 @@ function jpeg (options) {
* .png({ palette: true }) * .png({ palette: true })
* .toBuffer(); * .toBuffer();
* *
* @example
* // Output 16 bits per pixel RGB(A)
* const data = await sharp(input)
* .toColourspace('rgb16')
* .png()
* .toBuffer();
*
* @param {Object} [options] * @param {Object} [options]
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan * @param {boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {number} [options.compressionLevel=6] - zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest) * @param {number} [options.compressionLevel=6] - zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest)
@@ -1000,6 +1011,7 @@ function tiff (options) {
* Use these AVIF options for output image. * 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.
* *
* @example * @example
* const data = await sharp(input) * const data = await sharp(input)
@@ -1018,6 +1030,7 @@ function tiff (options) {
* @param {boolean} [options.lossless=false] - use lossless compression * @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest) * @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling * @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
* @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid options * @throws {Error} Invalid options
*/ */
@@ -1044,6 +1057,7 @@ function avif (options) {
* @param {boolean} [options.lossless=false] - use lossless compression * @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest) * @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling * @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
* @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid options * @throws {Error} Invalid options
*/ */
@@ -1082,6 +1096,16 @@ function heif (options) {
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling); throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
} }
} }
if (is.defined(options.bitdepth)) {
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [8, 10, 12])) {
if (options.bitdepth !== 8 && this.constructor.versions.heif) {
throw is.invalidParameterError('bitdepth when using prebuilt binaries', 8, options.bitdepth);
}
this.options.heifBitdepth = options.bitdepth;
} else {
throw is.invalidParameterError('bitdepth', '8, 10 or 12', options.bitdepth);
}
}
} else { } else {
throw is.invalidParameterError('options', 'Object', options); throw is.invalidParameterError('options', 'Object', options);
} }
@@ -1233,7 +1257,7 @@ function raw (options) {
* @param {number} [options.angle=0] tile angle of rotation, must be a multiple of 90. * @param {number} [options.angle=0] tile angle of rotation, must be a multiple of 90.
* @param {string|Object} [options.background={r: 255, g: 255, b: 255, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency. * @param {string|Object} [options.background={r: 255, g: 255, b: 255, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency.
* @param {string} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout. * @param {string} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
* @param {number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images * @param {number} [options.skipBlanks=-1] Threshold to skip tile generation. Range is 0-255 for 8-bit images, 0-65535 for 16-bit images. Default is 5 for `google` layout, -1 (no skip) otherwise.
* @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file). * @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
* @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`. * @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`.
* @param {boolean} [options.centre=false] centre image in tile. * @param {boolean} [options.centre=false] centre image in tile.

View File

@@ -68,6 +68,7 @@ const strategy = {
*/ */
const kernel = { const kernel = {
nearest: 'nearest', nearest: 'nearest',
linear: 'linear',
cubic: 'cubic', cubic: 'cubic',
mitchell: 'mitchell', mitchell: 'mitchell',
lanczos2: 'lanczos2', lanczos2: 'lanczos2',
@@ -135,18 +136,22 @@ function isResizeExpected (options) {
* *
* Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property. * Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property.
* *
* The experimental strategy-based approach resizes so one dimension is at its target length * The strategy-based approach initially 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. * 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](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29). * - `entropy`: focus on the region with the highest [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29).
* - `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones. * - `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
* *
* Possible interpolation kernels are: * Possible downsizing kernels are:
* - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation). * - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
* - `linear`: Use a [triangle filter](https://en.wikipedia.org/wiki/Triangular_function).
* - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline). * - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
* - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf). * - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`. * - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default). * - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
* *
* When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
* Downsampling kernels without a matching upsampling interpolator map to `cubic`.
*
* Only one resize can occur per pipeline. * Only one resize can occur per pipeline.
* Previous calls to `resize` in the same pipeline will be ignored. * Previous calls to `resize` in the same pipeline will be ignored.
* *
@@ -239,7 +244,7 @@ function isResizeExpected (options) {
* @param {String} [options.fit='cover'] - How the image should be resized/cropped to fit the target dimension(s), one of `cover`, `contain`, `fill`, `inside` or `outside`. * @param {String} [options.fit='cover'] - How the image should be resized/cropped to fit the target dimension(s), one of `cover`, `contain`, `fill`, `inside` or `outside`.
* @param {String} [options.position='centre'] - A position, gravity or strategy to use when `fit` is `cover` or `contain`. * @param {String} [options.position='centre'] - A position, gravity or strategy to use when `fit` is `cover` or `contain`.
* @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when `fit` is `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency. * @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when `fit` is `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
* @param {String} [options.kernel='lanczos3'] - The kernel to use for image reduction. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load. * @param {String} [options.kernel='lanczos3'] - The kernel to use for image reduction and the inferred interpolator to use for upsampling. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load.
* @param {Boolean} [options.withoutEnlargement=false] - Do not scale up if the width *or* height are already less than the target dimensions, equivalent to GraphicsMagick's `>` geometry option. This may result in output dimensions smaller than the target dimensions. * @param {Boolean} [options.withoutEnlargement=false] - Do not scale up if the width *or* height are already less than the target dimensions, equivalent to GraphicsMagick's `>` geometry option. This may result in output dimensions smaller than the target dimensions.
* @param {Boolean} [options.withoutReduction=false] - Do not scale down if the width *or* height are already greater than the target dimensions, equivalent to GraphicsMagick's `<` geometry option. This may still result in a crop to reach the target dimensions. * @param {Boolean} [options.withoutReduction=false] - Do not scale down if the width *or* height are already greater than the target dimensions, equivalent to GraphicsMagick's `<` geometry option. This may still result in a crop to reach the target dimensions.
* @param {Boolean} [options.fastShrinkOnLoad=true] - Take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern or round-down of an auto-scaled dimension. * @param {Boolean} [options.fastShrinkOnLoad=true] - Take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern or round-down of an auto-scaled dimension.

View File

@@ -153,6 +153,9 @@ function concurrency (concurrency) {
if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) { if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
// Reduce default concurrency to 1 when using glibc memory allocator // Reduce default concurrency to 1 when using glibc memory allocator
sharp.concurrency(1); sharp.concurrency(1);
} else if (detectLibc.familySync() === detectLibc.MUSL && sharp.concurrency() === 1024) {
// Reduce default concurrency when musl thread over-subscription detected
sharp.concurrency(require('node:os').availableParallelism());
} }
/** /**

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-darwin-arm64", "name": "@img/sharp-darwin-arm64",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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.0.1" "@img/sharp-libvips-darwin-arm64": "1.0.2"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-darwin-x64", "name": "@img/sharp-darwin-x64",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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.0.1" "@img/sharp-libvips-darwin-x64": "1.0.2"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-arm", "name": "@img/sharp-linux-arm",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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.0.1" "@img/sharp-libvips-linux-arm": "1.0.2"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-arm64", "name": "@img/sharp-linux-arm64",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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.0.1" "@img/sharp-libvips-linux-arm64": "1.0.2"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-s390x", "name": "@img/sharp-linux-s390x",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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.0.1" "@img/sharp-libvips-linux-s390x": "1.0.2"
}, },
"files": [ "files": [
"lib" "lib"
@@ -33,7 +33,7 @@
"npm": ">=9.6.5", "npm": ">=9.6.5",
"yarn": ">=3.2.0", "yarn": ">=3.2.0",
"pnpm": ">=7.1.0", "pnpm": ">=7.1.0",
"glibc": ">=2.28" "glibc": ">=2.31"
}, },
"os": [ "os": [
"linux" "linux"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linux-x64", "name": "@img/sharp-linux-x64",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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.0.1" "@img/sharp-libvips-linux-x64": "1.0.2"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linuxmusl-arm64", "name": "@img/sharp-linuxmusl-arm64",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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.0.1" "@img/sharp-libvips-linuxmusl-arm64": "1.0.2"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-linuxmusl-x64", "name": "@img/sharp-linuxmusl-x64",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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.0.1" "@img/sharp-libvips-linuxmusl-x64": "1.0.2"
}, },
"files": [ "files": [
"lib" "lib"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp", "name": "@img/sharp",
"version": "0.33.2-rc.0", "version": "0.33.4",
"private": "true", "private": "true",
"workspaces": [ "workspaces": [
"darwin-arm64", "darwin-arm64",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-wasm32", "name": "@img/sharp-wasm32",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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",
@@ -34,7 +34,7 @@
"pnpm": ">=7.1.0" "pnpm": ">=7.1.0"
}, },
"dependencies": { "dependencies": {
"@emnapi/runtime": "^0.45.0" "@emnapi/runtime": "^1.1.1"
}, },
"cpu": [ "cpu": [
"wasm32" "wasm32"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@img/sharp-win32-ia32", "name": "@img/sharp-win32-ia32",
"version": "0.33.2-rc.0", "version": "0.33.4",
"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.33.2-rc.0", "version": "0.33.4",
"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.33.2-rc.0", "version": "0.33.4",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com", "homepage": "https://sharp.pixelplumbing.com",
"contributors": [ "contributors": [
@@ -82,7 +82,7 @@
"Joris Dugué <zaruike10@gmail.com>", "Joris Dugué <zaruike10@gmail.com>",
"Chris Banks <christopher.bradley.banks@gmail.com>", "Chris Banks <christopher.bradley.banks@gmail.com>",
"Ompal Singh <ompal.hitm09@gmail.com>", "Ompal Singh <ompal.hitm09@gmail.com>",
"Brodan <christopher.hranj@gmail.com", "Brodan <christopher.hranj@gmail.com>",
"Ankur Parihar <ankur.github@gmail.com>", "Ankur Parihar <ankur.github@gmail.com>",
"Brahim Ait elhaj <brahima@gmail.com>", "Brahim Ait elhaj <brahima@gmail.com>",
"Mart Jansink <m.jansink@gmail.com>", "Mart Jansink <m.jansink@gmail.com>",
@@ -137,57 +137,57 @@
], ],
"dependencies": { "dependencies": {
"color": "^4.2.3", "color": "^4.2.3",
"detect-libc": "^2.0.2", "detect-libc": "^2.0.3",
"semver": "^7.5.4" "semver": "^7.6.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@img/sharp-darwin-arm64": "0.33.2-rc.0", "@img/sharp-darwin-arm64": "0.33.4",
"@img/sharp-darwin-x64": "0.33.2-rc.0", "@img/sharp-darwin-x64": "0.33.4",
"@img/sharp-libvips-darwin-arm64": "1.0.1", "@img/sharp-libvips-darwin-arm64": "1.0.2",
"@img/sharp-libvips-darwin-x64": "1.0.1", "@img/sharp-libvips-darwin-x64": "1.0.2",
"@img/sharp-libvips-linux-arm": "1.0.1", "@img/sharp-libvips-linux-arm": "1.0.2",
"@img/sharp-libvips-linux-arm64": "1.0.1", "@img/sharp-libvips-linux-arm64": "1.0.2",
"@img/sharp-libvips-linux-s390x": "1.0.1", "@img/sharp-libvips-linux-s390x": "1.0.2",
"@img/sharp-libvips-linux-x64": "1.0.1", "@img/sharp-libvips-linux-x64": "1.0.2",
"@img/sharp-libvips-linuxmusl-arm64": "1.0.1", "@img/sharp-libvips-linuxmusl-arm64": "1.0.2",
"@img/sharp-libvips-linuxmusl-x64": "1.0.1", "@img/sharp-libvips-linuxmusl-x64": "1.0.2",
"@img/sharp-linux-arm": "0.33.2-rc.0", "@img/sharp-linux-arm": "0.33.4",
"@img/sharp-linux-arm64": "0.33.2-rc.0", "@img/sharp-linux-arm64": "0.33.4",
"@img/sharp-linux-s390x": "0.33.2-rc.0", "@img/sharp-linux-s390x": "0.33.4",
"@img/sharp-linux-x64": "0.33.2-rc.0", "@img/sharp-linux-x64": "0.33.4",
"@img/sharp-linuxmusl-arm64": "0.33.2-rc.0", "@img/sharp-linuxmusl-arm64": "0.33.4",
"@img/sharp-linuxmusl-x64": "0.33.2-rc.0", "@img/sharp-linuxmusl-x64": "0.33.4",
"@img/sharp-wasm32": "0.33.2-rc.0", "@img/sharp-wasm32": "0.33.4",
"@img/sharp-win32-ia32": "0.33.2-rc.0", "@img/sharp-win32-ia32": "0.33.4",
"@img/sharp-win32-x64": "0.33.2-rc.0" "@img/sharp-win32-x64": "0.33.4"
}, },
"devDependencies": { "devDependencies": {
"@emnapi/runtime": "^0.45.0", "@emnapi/runtime": "^1.1.1",
"@img/sharp-libvips-dev": "1.0.1", "@img/sharp-libvips-dev": "1.0.2",
"@img/sharp-libvips-dev-wasm32": "1.0.1", "@img/sharp-libvips-dev-wasm32": "1.0.3",
"@img/sharp-libvips-win32-ia32": "1.0.1", "@img/sharp-libvips-win32-ia32": "1.0.2",
"@img/sharp-libvips-win32-x64": "1.0.1", "@img/sharp-libvips-win32-x64": "1.0.2",
"@types/node": "*", "@types/node": "*",
"async": "^3.2.5", "async": "^3.2.5",
"cc": "^3.0.1", "cc": "^3.0.1",
"emnapi": "^0.45.0", "emnapi": "^1.1.1",
"exif-reader": "^2.0.0", "exif-reader": "^2.0.1",
"extract-zip": "^2.0.1", "extract-zip": "^2.0.1",
"icc": "^3.0.0", "icc": "^3.0.0",
"jsdoc-to-markdown": "^8.0.0", "jsdoc-to-markdown": "^8.0.1",
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
"mocha": "^10.2.0", "mocha": "^10.4.0",
"node-addon-api": "^7.0.0", "node-addon-api": "^8.0.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"prebuild": "^12.1.0", "prebuild": "^13.0.1",
"semistandard": "^17.0.0", "semistandard": "^17.0.0",
"tar-fs": "^3.0.4", "tar-fs": "^3.0.6",
"tsd": "^0.30.3" "tsd": "^0.31.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",
"libvips": ">=8.15.1" "libvips": ">=8.15.2"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"

View File

@@ -75,7 +75,7 @@ namespace sharp {
Napi::Buffer<char> buffer = input.Get("buffer").As<Napi::Buffer<char>>(); Napi::Buffer<char> buffer = input.Get("buffer").As<Napi::Buffer<char>>();
descriptor->bufferLength = buffer.Length(); descriptor->bufferLength = buffer.Length();
descriptor->buffer = buffer.Data(); descriptor->buffer = buffer.Data();
descriptor->isBuffer = TRUE; descriptor->isBuffer = true;
} }
descriptor->failOn = AttrAsEnum<VipsFailOn>(input, "failOn", VIPS_TYPE_FAIL_ON); descriptor->failOn = AttrAsEnum<VipsFailOn>(input, "failOn", VIPS_TYPE_FAIL_ON);
// Density for vector-based input // Density for vector-based input
@@ -384,7 +384,7 @@ namespace sharp {
->set("access", descriptor->access) ->set("access", descriptor->access)
->set("fail_on", descriptor->failOn); ->set("fail_on", descriptor->failOn);
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) { if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
option->set("unlimited", TRUE); option->set("unlimited", true);
} }
if (imageType == ImageType::SVG || imageType == ImageType::PDF) { if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", descriptor->density); option->set("dpi", descriptor->density);
@@ -488,7 +488,7 @@ namespace sharp {
->set("access", descriptor->access) ->set("access", descriptor->access)
->set("fail_on", descriptor->failOn); ->set("fail_on", descriptor->failOn);
if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) { if (descriptor->unlimited && ImageTypeSupportsUnlimited(imageType)) {
option->set("unlimited", TRUE); option->set("unlimited", true);
} }
if (imageType == ImageType::SVG || imageType == ImageType::PDF) { if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", descriptor->density); option->set("dpi", descriptor->density);
@@ -768,7 +768,7 @@ namespace sharp {
int *timeout = VIPS_NEW(im, int); int *timeout = VIPS_NEW(im, int);
*timeout = seconds; *timeout = seconds;
g_signal_connect(im, "eval", G_CALLBACK(VipsProgressCallBack), timeout); g_signal_connect(im, "eval", G_CALLBACK(VipsProgressCallBack), timeout);
vips_image_set_progress(im, TRUE); vips_image_set_progress(im, true);
} }
} }
} }
@@ -778,7 +778,7 @@ namespace sharp {
*/ */
void VipsProgressCallBack(VipsImage *im, VipsProgress *progress, int *timeout) { void VipsProgressCallBack(VipsImage *im, VipsProgress *progress, int *timeout) {
if (*timeout > 0 && progress->run >= *timeout) { if (*timeout > 0 && progress->run >= *timeout) {
vips_image_set_kill(im, TRUE); vips_image_set_kill(im, true);
vips_error("timeout", "%d%% complete", progress->percent); vips_error("timeout", "%d%% complete", progress->percent);
*timeout = 0; *timeout = 0;
} }
@@ -1081,9 +1081,10 @@ namespace sharp {
/* /*
Ensure decoding remains sequential. Ensure decoding remains sequential.
*/ */
VImage StaySequential(VImage image, VipsAccess access, bool condition) { VImage StaySequential(VImage image, bool condition) {
if (access == VIPS_ACCESS_SEQUENTIAL && condition) { if (vips_image_is_sequential(image.get_image()) && condition) {
return image.copy_memory(); image = image.copy_memory().copy();
image.remove(VIPS_META_SEQUENTIAL);
} }
return image; return image;
} }

View File

@@ -16,8 +16,8 @@
#if (VIPS_MAJOR_VERSION < 8) || \ #if (VIPS_MAJOR_VERSION < 8) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 15) || \ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 15) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 15 && VIPS_MICRO_VERSION < 1) (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 15 && VIPS_MICRO_VERSION < 2)
#error "libvips version 8.15.1+ is required - please see https://sharp.pixelplumbing.com/install" #error "libvips version 8.15.2+ is required - please see https://sharp.pixelplumbing.com/install"
#endif #endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6))) #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
@@ -79,12 +79,12 @@ namespace sharp {
buffer(nullptr), buffer(nullptr),
failOn(VIPS_FAIL_ON_WARNING), failOn(VIPS_FAIL_ON_WARNING),
limitInputPixels(0x3FFF * 0x3FFF), limitInputPixels(0x3FFF * 0x3FFF),
unlimited(FALSE), unlimited(false),
access(VIPS_ACCESS_RANDOM), access(VIPS_ACCESS_RANDOM),
bufferLength(0), bufferLength(0),
isBuffer(FALSE), isBuffer(false),
density(72.0), density(72.0),
ignoreIcc(FALSE), ignoreIcc(false),
rawDepth(VIPS_FORMAT_UCHAR), rawDepth(VIPS_FORMAT_UCHAR),
rawChannels(0), rawChannels(0),
rawWidth(0), rawWidth(0),
@@ -103,9 +103,9 @@ namespace sharp {
textWidth(0), textWidth(0),
textHeight(0), textHeight(0),
textAlign(VIPS_ALIGN_LOW), textAlign(VIPS_ALIGN_LOW),
textJustify(FALSE), textJustify(false),
textDpi(72), textDpi(72),
textRgba(FALSE), textRgba(false),
textSpacing(0), textSpacing(0),
textWrap(VIPS_TEXT_WRAP_WORD), textWrap(VIPS_TEXT_WRAP_WORD),
textAutofitDpi(0) {} textAutofitDpi(0) {}
@@ -386,7 +386,7 @@ namespace sharp {
/* /*
Ensure decoding remains sequential. Ensure decoding remains sequential.
*/ */
VImage StaySequential(VImage image, VipsAccess access, bool condition = TRUE); VImage StaySequential(VImage image, bool condition = true);
} // namespace sharp } // namespace sharp

View File

@@ -155,7 +155,7 @@ namespace sharp {
return image.conv(blur); return image.conv(blur);
} else { } else {
// Slower, accurate Gaussian blur // Slower, accurate Gaussian blur
return StaySequential(image, VIPS_ACCESS_SEQUENTIAL).gaussblur(sigma); return StaySequential(image).gaussblur(sigma);
} }
} }
@@ -386,7 +386,7 @@ namespace sharp {
pages.reserve(nPages); pages.reserve(nPages);
// Split the image into cropped frames // Split the image into cropped frames
image = StaySequential(image, VIPS_ACCESS_SEQUENTIAL); image = StaySequential(image);
for (int i = 0; i < nPages; i++) { for (int i = 0; i < nPages; i++) {
pages.push_back( pages.push_back(
image.extract_area(left, *pageHeight * i + top, width, height)); image.extract_area(left, *pageHeight * i + top, width, height));

View File

@@ -54,7 +54,7 @@ class PipelineWorker : public Napi::AsyncWorker {
sharp::ImageType inputImageType; sharp::ImageType inputImageType;
std::tie(image, inputImageType) = sharp::OpenInput(baton->input); std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
VipsAccess access = baton->input->access; VipsAccess access = baton->input->access;
image = sharp::EnsureColourspace(image, baton->colourspaceInput); image = sharp::EnsureColourspace(image, baton->colourspacePipeline);
int nPages = baton->input->pages; int nPages = baton->input->pages;
if (nPages == -1) { if (nPages == -1) {
@@ -70,8 +70,8 @@ class PipelineWorker : public Napi::AsyncWorker {
// Calculate angle of rotation // Calculate angle of rotation
VipsAngle rotation = VIPS_ANGLE_D0; VipsAngle rotation = VIPS_ANGLE_D0;
VipsAngle autoRotation = VIPS_ANGLE_D0; VipsAngle autoRotation = VIPS_ANGLE_D0;
bool autoFlip = FALSE; bool autoFlip = false;
bool autoFlop = FALSE; bool autoFlop = false;
if (baton->useExifOrientation) { if (baton->useExifOrientation) {
// Rotate and flip image according to Exif orientation // Rotate and flip image according to Exif orientation
@@ -88,7 +88,7 @@ class PipelineWorker : public Napi::AsyncWorker {
baton->rotationAngle != 0.0); baton->rotationAngle != 0.0);
if (shouldRotateBefore) { if (shouldRotateBefore) {
image = sharp::StaySequential(image, access, image = sharp::StaySequential(image,
rotation != VIPS_ANGLE_D0 || rotation != VIPS_ANGLE_D0 ||
autoRotation != VIPS_ANGLE_D0 || autoRotation != VIPS_ANGLE_D0 ||
autoFlip || autoFlip ||
@@ -104,17 +104,17 @@ class PipelineWorker : public Napi::AsyncWorker {
} }
if (autoFlip) { if (autoFlip) {
image = image.flip(VIPS_DIRECTION_VERTICAL); image = image.flip(VIPS_DIRECTION_VERTICAL);
autoFlip = FALSE; autoFlip = false;
} else if (baton->flip) { } else if (baton->flip) {
image = image.flip(VIPS_DIRECTION_VERTICAL); image = image.flip(VIPS_DIRECTION_VERTICAL);
baton->flip = FALSE; baton->flip = false;
} }
if (autoFlop) { if (autoFlop) {
image = image.flip(VIPS_DIRECTION_HORIZONTAL); image = image.flip(VIPS_DIRECTION_HORIZONTAL);
autoFlop = FALSE; autoFlop = false;
} else if (baton->flop) { } else if (baton->flop) {
image = image.flip(VIPS_DIRECTION_HORIZONTAL); image = image.flip(VIPS_DIRECTION_HORIZONTAL);
baton->flop = FALSE; baton->flop = false;
} }
if (rotation != VIPS_ANGLE_D0) { if (rotation != VIPS_ANGLE_D0) {
if (rotation != VIPS_ANGLE_D180) { if (rotation != VIPS_ANGLE_D180) {
@@ -126,7 +126,7 @@ class PipelineWorker : public Napi::AsyncWorker {
if (baton->rotationAngle != 0.0) { if (baton->rotationAngle != 0.0) {
MultiPageUnsupported(nPages, "Rotate"); MultiPageUnsupported(nPages, "Rotate");
std::vector<double> background; std::vector<double> background;
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, FALSE); std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, false);
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)).copy_memory(); image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)).copy_memory();
} }
} }
@@ -134,7 +134,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// Trim // Trim
if (baton->trimThreshold >= 0.0) { if (baton->trimThreshold >= 0.0) {
MultiPageUnsupported(nPages, "Trim"); MultiPageUnsupported(nPages, "Trim");
image = sharp::StaySequential(image, access); image = sharp::StaySequential(image);
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold, baton->trimLineArt); image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold, baton->trimLineArt);
baton->trimOffsetLeft = image.xoffset(); baton->trimOffsetLeft = image.xoffset();
baton->trimOffsetTop = image.yoffset(); baton->trimOffsetTop = image.yoffset();
@@ -189,7 +189,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// - input colourspace is not specified; // - input colourspace is not specified;
bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) && bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold < 0.0 && baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold < 0.0 &&
baton->colourspaceInput == VIPS_INTERPRETATION_LAST && !shouldRotateBefore; baton->colourspacePipeline == VIPS_INTERPRETATION_LAST && !shouldRotateBefore;
if (shouldPreShrink) { if (shouldPreShrink) {
// The common part of the shrink: the bit by which both axes must be shrunk // The common part of the shrink: the bit by which both axes must be shrunk
@@ -331,12 +331,13 @@ class PipelineWorker : public Napi::AsyncWorker {
sharp::HasProfile(image) && sharp::HasProfile(image) &&
image.interpretation() != VIPS_INTERPRETATION_LABS && image.interpretation() != VIPS_INTERPRETATION_LABS &&
image.interpretation() != VIPS_INTERPRETATION_GREY16 && image.interpretation() != VIPS_INTERPRETATION_GREY16 &&
baton->colourspacePipeline != VIPS_INTERPRETATION_CMYK &&
!baton->input->ignoreIcc !baton->input->ignoreIcc
) { ) {
// Convert to sRGB/P3 using embedded profile // Convert to sRGB/P3 using embedded profile
try { try {
image = image.icc_transform(processingProfile, VImage::option() image = image.icc_transform(processingProfile, VImage::option()
->set("embedded", TRUE) ->set("embedded", true)
->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8) ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
->set("intent", VIPS_INTENT_PERCEPTUAL)); ->set("intent", VIPS_INTENT_PERCEPTUAL));
} catch(...) { } catch(...) {
@@ -344,7 +345,7 @@ class PipelineWorker : public Napi::AsyncWorker {
} }
} else if ( } else if (
image.interpretation() == VIPS_INTERPRETATION_CMYK && image.interpretation() == VIPS_INTERPRETATION_CMYK &&
baton->colourspaceInput != VIPS_INTERPRETATION_CMYK baton->colourspacePipeline != VIPS_INTERPRETATION_CMYK
) { ) {
image = image.icc_transform(processingProfile, VImage::option() image = image.icc_transform(processingProfile, VImage::option()
->set("input_profile", "cmyk") ->set("input_profile", "cmyk")
@@ -356,11 +357,6 @@ class PipelineWorker : public Napi::AsyncWorker {
image = sharp::Flatten(image, baton->flattenBackground); image = sharp::Flatten(image, baton->flattenBackground);
} }
// Negate the colours in the image
if (baton->negate) {
image = sharp::Negate(image, baton->negateAlpha);
}
// Gamma encoding (darken) // Gamma encoding (darken)
if (baton->gamma >= 1 && baton->gamma <= 3) { if (baton->gamma >= 1 && baton->gamma <= 3) {
image = sharp::Gamma(image, 1.0 / baton->gamma); image = sharp::Gamma(image, 1.0 / baton->gamma);
@@ -396,7 +392,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("kernel", baton->kernel)); ->set("kernel", baton->kernel));
} }
image = sharp::StaySequential(image, access, image = sharp::StaySequential(image,
autoRotation != VIPS_ANGLE_D0 || autoRotation != VIPS_ANGLE_D0 ||
baton->flip || baton->flip ||
autoFlip || autoFlip ||
@@ -432,7 +428,7 @@ class PipelineWorker : public Napi::AsyncWorker {
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) { for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
baton->joinChannelIn[i]->access = access; baton->joinChannelIn[i]->access = access;
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]); std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput); joinImage = sharp::EnsureColourspace(joinImage, baton->colourspacePipeline);
image = image.bandjoin(joinImage); image = image.bandjoin(joinImage);
} }
image = image.copy(VImage::option()->set("interpretation", baton->colourspace)); image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
@@ -499,7 +495,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// Attention-based or Entropy-based crop // Attention-based or Entropy-based crop
MultiPageUnsupported(nPages, "Resize strategy"); MultiPageUnsupported(nPages, "Resize strategy");
image = sharp::StaySequential(image, access); image = sharp::StaySequential(image);
image = image.smartcrop(baton->width, baton->height, VImage::option() image = image.smartcrop(baton->width, baton->height, VImage::option()
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION) ->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
->set("premultiplied", shouldPremultiplyAlpha) ->set("premultiplied", shouldPremultiplyAlpha)
@@ -518,7 +514,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// Rotate post-extract non-90 angle // Rotate post-extract non-90 angle
if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) { if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
MultiPageUnsupported(nPages, "Rotate"); MultiPageUnsupported(nPages, "Rotate");
image = sharp::StaySequential(image, access); image = sharp::StaySequential(image);
std::vector<double> background; std::vector<double> background;
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha); std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)); image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
@@ -542,7 +538,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// Affine transform // Affine transform
if (!baton->affineMatrix.empty()) { if (!baton->affineMatrix.empty()) {
MultiPageUnsupported(nPages, "Affine"); MultiPageUnsupported(nPages, "Affine");
image = sharp::StaySequential(image, access); image = sharp::StaySequential(image);
std::vector<double> background; std::vector<double> background;
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha); std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
vips::VInterpolate interp = vips::VInterpolate::new_from_name( vips::VInterpolate interp = vips::VInterpolate::new_from_name(
@@ -565,6 +561,7 @@ class PipelineWorker : public Napi::AsyncWorker {
std::vector<double> background; std::vector<double> background;
std::tie(image, background) = sharp::ApplyAlpha(image, baton->extendBackground, shouldPremultiplyAlpha); std::tie(image, background) = sharp::ApplyAlpha(image, baton->extendBackground, shouldPremultiplyAlpha);
image = sharp::StaySequential(image, nPages > 1);
image = nPages > 1 image = nPages > 1
? sharp::EmbedMultiPage(image, ? sharp::EmbedMultiPage(image,
baton->extendLeft, baton->extendTop, baton->width, baton->height, baton->extendLeft, baton->extendTop, baton->width, baton->height,
@@ -573,7 +570,7 @@ class PipelineWorker : public Napi::AsyncWorker {
VImage::option()->set("extend", baton->extendWith)->set("background", background)); VImage::option()->set("extend", baton->extendWith)->set("background", background));
} else { } else {
std::vector<double> ignoredBackground(1); std::vector<double> ignoredBackground(1);
image = sharp::StaySequential(image, baton->input->access); image = sharp::StaySequential(image);
image = nPages > 1 image = nPages > 1
? sharp::EmbedMultiPage(image, ? sharp::EmbedMultiPage(image,
baton->extendLeft, baton->extendTop, baton->width, baton->height, baton->extendLeft, baton->extendTop, baton->width, baton->height,
@@ -642,7 +639,7 @@ 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->colourspaceInput); compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline);
// Verify within current dimensions // Verify within current dimensions
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) { if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
throw vips::VError("Image to composite must have same dimensions or smaller"); throw vips::VError("Image to composite must have same dimensions or smaller");
@@ -669,7 +666,7 @@ class PipelineWorker : public Napi::AsyncWorker {
if (across != 0 || down != 0) { if (across != 0 || down != 0) {
int left; int left;
int top; int top;
compositeImage = sharp::StaySequential(compositeImage, access).replicate(across, down); compositeImage = sharp::StaySequential(compositeImage).replicate(across, down);
if (composite->hasOffset) { if (composite->hasOffset) {
std::tie(left, top) = sharp::CalculateCrop( std::tie(left, top) = sharp::CalculateCrop(
compositeImage.width(), compositeImage.height(), image.width(), image.height(), compositeImage.width(), compositeImage.height(), image.width(), image.height(),
@@ -727,13 +724,13 @@ class PipelineWorker : public Napi::AsyncWorker {
// Apply normalisation - stretch luminance to cover full dynamic range // Apply normalisation - stretch luminance to cover full dynamic range
if (baton->normalise) { if (baton->normalise) {
image = sharp::StaySequential(image, access); image = sharp::StaySequential(image);
image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper); image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
} }
// Apply contrast limiting adaptive histogram equalization (CLAHE) // Apply contrast limiting adaptive histogram equalization (CLAHE)
if (baton->claheWidth != 0 && baton->claheHeight != 0) { if (baton->claheWidth != 0 && baton->claheHeight != 0) {
image = sharp::StaySequential(image, access); image = sharp::StaySequential(image);
image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope); image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
} }
@@ -743,7 +740,7 @@ class PipelineWorker : public Napi::AsyncWorker {
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN; sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
baton->boolean->access = access; baton->boolean->access = access;
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean); std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput); booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspacePipeline);
image = sharp::Boolean(image, booleanImage, baton->booleanOp); image = sharp::Boolean(image, booleanImage, baton->booleanOp);
image = sharp::RemoveGifPalette(image); image = sharp::RemoveGifPalette(image);
} }
@@ -776,10 +773,10 @@ class PipelineWorker : public Napi::AsyncWorker {
// Convert colourspace, pass the current known interpretation so libvips doesn't have to guess // Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation())); image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
// Transform colours from embedded profile to output profile // Transform colours from embedded profile to output profile
if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) && if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) && baton->colourspacePipeline != VIPS_INTERPRETATION_CMYK &&
baton->withIccProfile.empty() && sharp::HasProfile(image)) { baton->withIccProfile.empty() && sharp::HasProfile(image)) {
image = image.icc_transform("srgb", VImage::option() image = image.icc_transform(processingProfile, VImage::option()
->set("embedded", TRUE) ->set("embedded", true)
->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8) ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
->set("intent", VIPS_INTENT_PERCEPTUAL)); ->set("intent", VIPS_INTENT_PERCEPTUAL));
} }
@@ -810,7 +807,7 @@ class PipelineWorker : public Napi::AsyncWorker {
try { try {
image = image.icc_transform(const_cast<char*>(baton->withIccProfile.data()), VImage::option() image = image.icc_transform(const_cast<char*>(baton->withIccProfile.data()), VImage::option()
->set("input_profile", processingProfile) ->set("input_profile", processingProfile)
->set("embedded", TRUE) ->set("embedded", true)
->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8) ->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
->set("intent", VIPS_INTENT_PERCEPTUAL)); ->set("intent", VIPS_INTENT_PERCEPTUAL));
} catch(...) { } catch(...) {
@@ -819,6 +816,12 @@ class PipelineWorker : public Napi::AsyncWorker {
} else if (baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) { } else if (baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) {
image = sharp::SetProfile(image, inputProfile); image = sharp::SetProfile(image, inputProfile);
} }
// Negate the colours in the image
if (baton->negate) {
image = sharp::Negate(image, baton->negateAlpha);
}
// Override EXIF Orientation tag // Override EXIF Orientation tag
if (baton->withMetadataOrientation != -1) { if (baton->withMetadataOrientation != -1) {
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation); image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
@@ -988,7 +991,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("Q", baton->heifQuality) ->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression) ->set("compression", baton->heifCompression)
->set("effort", baton->heifEffort) ->set("effort", baton->heifEffort)
->set("bitdepth", 8) ->set("bitdepth", baton->heifBitdepth)
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4" ->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON) ? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
->set("lossless", baton->heifLossless))); ->set("lossless", baton->heifLossless)));
@@ -1003,7 +1006,7 @@ class PipelineWorker : public Napi::AsyncWorker {
if (!sharp::HasAlpha(image)) { if (!sharp::HasAlpha(image)) {
baton->tileBackground.pop_back(); baton->tileBackground.pop_back();
} }
image = sharp::StaySequential(image, access, baton->tileAngle != 0); image = sharp::StaySequential(image, baton->tileAngle != 0);
vips::VOption *options = BuildOptionsDZ(baton); vips::VOption *options = BuildOptionsDZ(baton);
VipsArea *area = reinterpret_cast<VipsArea*>(image.dzsave_buffer(options)); VipsArea *area = reinterpret_cast<VipsArea*>(image.dzsave_buffer(options));
baton->bufferOut = static_cast<char*>(area->data); baton->bufferOut = static_cast<char*>(area->data);
@@ -1181,7 +1184,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("Q", baton->heifQuality) ->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression) ->set("compression", baton->heifCompression)
->set("effort", baton->heifEffort) ->set("effort", baton->heifEffort)
->set("bitdepth", 8) ->set("bitdepth", baton->heifBitdepth)
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4" ->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON) ? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
->set("lossless", baton->heifLossless)); ->set("lossless", baton->heifLossless));
@@ -1205,7 +1208,7 @@ class PipelineWorker : public Napi::AsyncWorker {
if (!sharp::HasAlpha(image)) { if (!sharp::HasAlpha(image)) {
baton->tileBackground.pop_back(); baton->tileBackground.pop_back();
} }
image = sharp::StaySequential(image, access, baton->tileAngle != 0); image = sharp::StaySequential(image, baton->tileAngle != 0);
vips::VOption *options = BuildOptionsDZ(baton); vips::VOption *options = BuildOptionsDZ(baton);
image.dzsave(const_cast<char*>(baton->fileOut.data()), options); image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
baton->formatOut = "dz"; baton->formatOut = "dz";
@@ -1337,16 +1340,16 @@ class PipelineWorker : public Napi::AsyncWorker {
std::tuple<VipsAngle, bool, bool> std::tuple<VipsAngle, bool, bool>
CalculateExifRotationAndFlip(int const exifOrientation) { CalculateExifRotationAndFlip(int const exifOrientation) {
VipsAngle rotate = VIPS_ANGLE_D0; VipsAngle rotate = VIPS_ANGLE_D0;
bool flip = FALSE; bool flip = false;
bool flop = FALSE; bool flop = false;
switch (exifOrientation) { switch (exifOrientation) {
case 6: rotate = VIPS_ANGLE_D90; break; case 6: rotate = VIPS_ANGLE_D90; break;
case 3: rotate = VIPS_ANGLE_D180; break; case 3: rotate = VIPS_ANGLE_D180; break;
case 8: rotate = VIPS_ANGLE_D270; break; case 8: rotate = VIPS_ANGLE_D270; break;
case 2: flop = TRUE; break; // flop 1 case 2: flop = true; break; // flop 1
case 7: flip = TRUE; rotate = VIPS_ANGLE_D90; break; // flip 6 case 7: flip = true; rotate = VIPS_ANGLE_D90; break; // flip 6
case 4: flop = TRUE; rotate = VIPS_ANGLE_D180; break; // flop 3 case 4: flop = true; rotate = VIPS_ANGLE_D180; break; // flop 3
case 5: flip = TRUE; rotate = VIPS_ANGLE_D270; break; // flip 8 case 5: flip = true; rotate = VIPS_ANGLE_D270; break; // flip 8
} }
return std::make_tuple(rotate, flip, flop); return std::make_tuple(rotate, flip, flop);
} }
@@ -1394,7 +1397,7 @@ class PipelineWorker : public Napi::AsyncWorker {
std::string suffix; std::string suffix;
if (baton->tileFormat == "png") { if (baton->tileFormat == "png") {
std::vector<std::pair<std::string, std::string>> options { std::vector<std::pair<std::string, std::string>> options {
{"interlace", baton->pngProgressive ? "TRUE" : "FALSE"}, {"interlace", baton->pngProgressive ? "true" : "false"},
{"compression", std::to_string(baton->pngCompressionLevel)}, {"compression", std::to_string(baton->pngCompressionLevel)},
{"filter", baton->pngAdaptiveFiltering ? "all" : "none"} {"filter", baton->pngAdaptiveFiltering ? "all" : "none"}
}; };
@@ -1403,25 +1406,25 @@ class PipelineWorker : public Napi::AsyncWorker {
std::vector<std::pair<std::string, std::string>> options { std::vector<std::pair<std::string, std::string>> options {
{"Q", std::to_string(baton->webpQuality)}, {"Q", std::to_string(baton->webpQuality)},
{"alpha_q", std::to_string(baton->webpAlphaQuality)}, {"alpha_q", std::to_string(baton->webpAlphaQuality)},
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"}, {"lossless", baton->webpLossless ? "true" : "false"},
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"}, {"near_lossless", baton->webpNearLossless ? "true" : "false"},
{"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"}, {"smart_subsample", baton->webpSmartSubsample ? "true" : "false"},
{"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)}, {"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
{"min_size", baton->webpMinSize ? "TRUE" : "FALSE"}, {"min_size", baton->webpMinSize ? "true" : "false"},
{"mixed", baton->webpMixed ? "TRUE" : "FALSE"}, {"mixed", baton->webpMixed ? "true" : "false"},
{"effort", std::to_string(baton->webpEffort)} {"effort", std::to_string(baton->webpEffort)}
}; };
suffix = AssembleSuffixString(".webp", options); suffix = AssembleSuffixString(".webp", options);
} else { } else {
std::vector<std::pair<std::string, std::string>> options { std::vector<std::pair<std::string, std::string>> options {
{"Q", std::to_string(baton->jpegQuality)}, {"Q", std::to_string(baton->jpegQuality)},
{"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"}, {"interlace", baton->jpegProgressive ? "true" : "false"},
{"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"}, {"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
{"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"}, {"trellis_quant", baton->jpegTrellisQuantisation ? "true" : "false"},
{"quant_table", std::to_string(baton->jpegQuantisationTable)}, {"quant_table", std::to_string(baton->jpegQuantisationTable)},
{"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"}, {"overshoot_deringing", baton->jpegOvershootDeringing ? "true": "false"},
{"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"}, {"optimize_scans", baton->jpegOptimiseScans ? "true": "false"},
{"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"} {"optimize_coding", baton->jpegOptimiseCoding ? "true": "false"}
}; };
std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg"; std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
suffix = AssembleSuffixString(extname, options); suffix = AssembleSuffixString(extname, options);
@@ -1607,10 +1610,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i); baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
} }
} }
baton->colourspaceInput = sharp::AttrAsEnum<VipsInterpretation>( baton->colourspacePipeline = sharp::AttrAsEnum<VipsInterpretation>(
options, "colourspaceInput", VIPS_TYPE_INTERPRETATION); options, "colourspacePipeline", VIPS_TYPE_INTERPRETATION);
if (baton->colourspaceInput == VIPS_INTERPRETATION_ERROR) { if (baton->colourspacePipeline == VIPS_INTERPRETATION_ERROR) {
baton->colourspaceInput = VIPS_INTERPRETATION_LAST; baton->colourspacePipeline = VIPS_INTERPRETATION_LAST;
} }
baton->colourspace = sharp::AttrAsEnum<VipsInterpretation>(options, "colourspace", VIPS_TYPE_INTERPRETATION); baton->colourspace = sharp::AttrAsEnum<VipsInterpretation>(options, "colourspace", VIPS_TYPE_INTERPRETATION);
if (baton->colourspace == VIPS_INTERPRETATION_ERROR) { if (baton->colourspace == VIPS_INTERPRETATION_ERROR) {
@@ -1695,6 +1698,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
options, "heifCompression", VIPS_TYPE_FOREIGN_HEIF_COMPRESSION); options, "heifCompression", VIPS_TYPE_FOREIGN_HEIF_COMPRESSION);
baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort"); baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling"); baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
baton->heifBitdepth = sharp::AttrAsUint32(options, "heifBitdepth");
baton->jxlDistance = sharp::AttrAsDouble(options, "jxlDistance"); baton->jxlDistance = sharp::AttrAsDouble(options, "jxlDistance");
baton->jxlDecodingTier = sharp::AttrAsUint32(options, "jxlDecodingTier"); baton->jxlDecodingTier = sharp::AttrAsUint32(options, "jxlDecodingTier");
baton->jxlEffort = sharp::AttrAsUint32(options, "jxlEffort"); baton->jxlEffort = sharp::AttrAsUint32(options, "jxlEffort");

View File

@@ -181,6 +181,7 @@ struct PipelineBaton {
int heifEffort; int heifEffort;
std::string heifChromaSubsampling; std::string heifChromaSubsampling;
bool heifLossless; bool heifLossless;
int heifBitdepth;
double jxlDistance; double jxlDistance;
int jxlDecodingTier; int jxlDecodingTier;
int jxlEffort; int jxlEffort;
@@ -205,7 +206,7 @@ struct PipelineBaton {
int extractChannel; int extractChannel;
bool removeAlpha; bool removeAlpha;
double ensureAlpha; double ensureAlpha;
VipsInterpretation colourspaceInput; VipsInterpretation colourspacePipeline;
VipsInterpretation colourspace; VipsInterpretation colourspace;
std::vector<int> delay; std::vector<int> delay;
int loop; int loop;
@@ -349,6 +350,7 @@ struct PipelineBaton {
heifEffort(4), heifEffort(4),
heifChromaSubsampling("4:4:4"), heifChromaSubsampling("4:4:4"),
heifLossless(false), heifLossless(false),
heifBitdepth(8),
jxlDistance(1.0), jxlDistance(1.0),
jxlDecodingTier(0), jxlDecodingTier(0),
jxlEffort(7), jxlEffort(7),
@@ -369,7 +371,7 @@ struct PipelineBaton {
extractChannel(-1), extractChannel(-1),
removeAlpha(false), removeAlpha(false),
ensureAlpha(-1.0), ensureAlpha(-1.0),
colourspaceInput(VIPS_INTERPRETATION_LAST), colourspacePipeline(VIPS_INTERPRETATION_LAST),
colourspace(VIPS_INTERPRETATION_LAST), colourspace(VIPS_INTERPRETATION_LAST),
loop(-1), loop(-1),
tileSize(256), tileSize(256),

BIN
test/fixtures/XCMYK 2017.icc vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
test/fixtures/fogra-0-100-100-0.tif vendored Normal file

Binary file not shown.

View File

@@ -114,6 +114,7 @@ module.exports = {
inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif
inputTiff8BitDepth: getPath('8bit_depth.tiff'), inputTiff8BitDepth: getPath('8bit_depth.tiff'),
inputTifftagPhotoshop: getPath('tifftag-photoshop.tiff'), // https://github.com/lovell/sharp/issues/1600 inputTifftagPhotoshop: getPath('tifftag-photoshop.tiff'), // https://github.com/lovell/sharp/issues/1600
inputTiffFogra: getPath('fogra-0-100-100-0.tif'), // https://github.com/lovell/sharp/issues/4045
inputJp2: getPath('relax.jp2'), // https://www.fnordware.com/j2k/relax.jp2 inputJp2: getPath('relax.jp2'), // https://www.fnordware.com/j2k/relax.jp2
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

View File

@@ -410,7 +410,7 @@ sharp({
// Taken from API documentation at // Taken from API documentation at
// https://sharp.pixelplumbing.com/api-operation#clahe // https://sharp.pixelplumbing.com/api-operation#clahe
// introducted // introduced
sharp('input.jpg').clahe({ width: 10, height: 10 }).toFile('output.jpg'); sharp('input.jpg').clahe({ width: 10, height: 10 }).toFile('output.jpg');
sharp('input.jpg').clahe({ width: 10, height: 10, maxSlope: 5 }).toFile('outfile.jpg'); sharp('input.jpg').clahe({ width: 10, height: 10, maxSlope: 5 }).toFile('outfile.jpg');
@@ -595,7 +595,7 @@ sharp({
rgba: true, rgba: true,
justify: true, justify: true,
spacing: 10, spacing: 10,
wrap: 'charWord', wrap: 'word-char',
}, },
}) })
.png() .png()
@@ -680,3 +680,24 @@ sharp(input)
.keepIccProfile() .keepIccProfile()
.withIccProfile('filename') .withIccProfile('filename')
.withIccProfile('filename', { attach: false }); .withIccProfile('filename', { attach: false });
// Added missing types for OverlayOptions
// https://github.com/lovell/sharp/pull/4048
sharp(input).composite([
{
input: 'image.gif',
animated: true,
limitInputPixels: 536805378,
density: 144,
failOn: "warning"
}
])
sharp(input).composite([
{
input: 'image.png',
animated: false,
limitInputPixels: 178935126,
density: 72,
failOn: "truncated"
}
])

View File

@@ -144,4 +144,10 @@ describe('AVIF', () => {
/Processed image is too large for the HEIF format/ /Processed image is too large for the HEIF format/
) )
); );
it('Invalid bitdepth value throws error', async () => {
assert.rejects(
() => sharp().avif({ bitdepth: 11 }),
/Error: Expected 8, 10 or 12 for bitdepth but received 11 of type number/);
});
}); });

View File

@@ -77,4 +77,26 @@ describe('Clone', function () {
assert.strictEqual(0, original.listenerCount('finish')); assert.strictEqual(0, original.listenerCount('finish'));
assert.strictEqual(0, clone.listenerCount('finish')); assert.strictEqual(0, clone.listenerCount('finish'));
}); });
it('Ensure deep clone of properties, including arrays', async () => {
const alpha = await sharp({
create: { width: 320, height: 240, channels: 3, background: 'red' }
}).toColourspace('b-w').png().toBuffer();
const original = sharp();
const joiner = original.clone().joinChannel(alpha);
const negater = original.clone().negate();
fs.createReadStream(fixtures.inputJpg320x240).pipe(original);
const joined = await joiner.png({ effort: 1 }).toBuffer();
const negated = await negater.png({ effort: 1 }).toBuffer();
const joinedMetadata = await sharp(joined).metadata();
assert.strictEqual(joinedMetadata.channels, 4);
assert.strictEqual(joinedMetadata.hasAlpha, true);
const negatedMetadata = await sharp(negated).metadata();
assert.strictEqual(negatedMetadata.channels, 3);
assert.strictEqual(negatedMetadata.hasAlpha, false);
});
}); });

View File

@@ -106,6 +106,43 @@ describe('Colour space conversion', function () {
); );
}); });
it('CMYK profile to CMYK profile conversion using perceptual intent', async () => {
const data = await sharp(fixtures.inputTiffFogra)
.resize(320, 240)
.toColourspace('cmyk')
.pipelineColourspace('cmyk')
.withIccProfile(fixtures.path('XCMYK 2017.icc'))
.raw()
.toBuffer();
const [c, m, y, k] = data;
assert.deepStrictEqual(
{ c, m, y, k },
{ c: 1, m: 239, y: 227, k: 5 }
);
});
it('CMYK profile to CMYK profile with negate', (done) => {
sharp(fixtures.inputTiffFogra)
.resize(320, 240)
.toColourspace('cmyk')
.pipelineColourspace('cmyk')
.withIccProfile(fixtures.path('XCMYK 2017.icc'))
.negate()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('tiff', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertSimilar(
fixtures.expected('colourspace.cmyk-to-cmyk-negated.tif'),
data,
{ threshold: 0 },
done
);
});
});
it('From sRGB with RGB16 pipeline, resize with gamma, to sRGB', function (done) { it('From sRGB with RGB16 pipeline, resize with gamma, to sRGB', function (done) {
sharp(fixtures.inputPngGradients) sharp(fixtures.inputPngGradients)
.pipelineColourspace('rgb16') .pipelineColourspace('rgb16')

View File

@@ -451,7 +451,7 @@ describe('composite', () => {
assert.strictEqual(info.height, 40); assert.strictEqual(info.height, 40);
}); });
it('Ensure implict unpremultiply after resize but before composite', async () => { it('Ensure implicit unpremultiply after resize but before composite', async () => {
const [r, g, b, a] = await sharp({ const [r, g, b, a] = await sharp({
create: { create: {
width: 1, height: 1, channels: 4, background: 'saddlebrown' width: 1, height: 1, channels: 4, background: 'saddlebrown'

View File

@@ -73,20 +73,6 @@ describe('Extend', function () {
}); });
}); });
it('extend top with mirroring uses ordered read', async () => {
const data = await sharp(fixtures.inputJpg)
.extend({
extendWith: 'mirror',
top: 1
})
.png({ compressionLevel: 0 })
.toBuffer();
const { width, height } = await sharp(data).metadata();
assert.strictEqual(2725, width);
assert.strictEqual(2226, height);
});
it(`extend sides unequally with RGBA (${extendWith})`, function (done) { it(`extend sides unequally with RGBA (${extendWith})`, function (done) {
sharp(fixtures.inputPngWithTransparency16bit) sharp(fixtures.inputPngWithTransparency16bit)
.resize(120) .resize(120)
@@ -127,6 +113,39 @@ describe('Extend', function () {
}); });
}); });
it('extend top with mirroring uses ordered read', async () => {
const data = await sharp(fixtures.inputJpg)
.extend({
extendWith: 'mirror',
top: 1
})
.png({ compressionLevel: 0 })
.toBuffer();
const { width, height } = await sharp(data).metadata();
assert.strictEqual(2725, width);
assert.strictEqual(2226, height);
});
it('multi-page extend uses ordered read', async () => {
const multiPageTiff = await sharp(fixtures.inputGifAnimated, { animated: true })
.resize({ width: 8, height: 48 })
.tiff()
.toBuffer();
const data = await sharp(multiPageTiff, { pages: -1 })
.extend({
background: 'red',
top: 1
})
.png({ compressionLevel: 0 })
.toBuffer();
const { width, height } = await sharp(data).metadata();
assert.strictEqual(8, width);
assert.strictEqual(1470, height);
});
it('missing parameter fails', function () { it('missing parameter fails', function () {
assert.throws(function () { assert.throws(function () {
sharp().extend(); sharp().extend();

View File

@@ -78,4 +78,21 @@ describe('HEIF', () => {
sharp().heif({ compression: 'av1', chromaSubsampling: '4:4:4' }); sharp().heif({ compression: 'av1', chromaSubsampling: '4:4:4' });
}); });
}); });
it('valid bitdepth value does not throw an error', () => {
const { heif } = sharp.versions;
delete sharp.versions.heif;
assert.doesNotThrow(() => {
sharp().heif({ compression: 'av1', bitdepth: 12 });
});
sharp.versions.heif = '1.2.3';
assert.throws(() => {
sharp().heif({ compression: 'av1', bitdepth: 10 });
}, /Error: Expected 8 for bitdepth when using prebuilt binaries but received 10 of type number/);
sharp.versions.heif = heif;
});
it('invalid bitdepth value should throw an error', () => {
assert.throws(() => {
sharp().heif({ compression: 'av1', bitdepth: 11 });
}, /Error: Expected 8, 10 or 12 for bitdepth but received 11 of type number/);
});
}); });

View File

@@ -66,6 +66,14 @@ describe('libvips binaries', function () {
delete process.env.SHARP_IGNORE_GLOBAL_LIBVIPS; delete process.env.SHARP_IGNORE_GLOBAL_LIBVIPS;
}); });
it('useGlobalLibvips can be forced via an env var', function () {
process.env.SHARP_FORCE_GLOBAL_LIBVIPS = 1;
const useGlobalLibvips = libvips.useGlobalLibvips();
assert.strictEqual(true, useGlobalLibvips);
delete process.env.SHARP_FORCE_GLOBAL_LIBVIPS;
});
}); });
describe('Build time platform detection', () => { describe('Build time platform detection', () => {
@@ -162,7 +170,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, 'f2bd19138a'); assert.strictEqual(locatorHash, '45978c229d');
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

@@ -576,6 +576,19 @@ describe('Image metadata', function () {
assert.strictEqual(description, 'Generic RGB Profile'); assert.strictEqual(description, 'Generic RGB Profile');
}); });
it('keep existing CMYK ICC profile', async () => {
const data = await sharp(fixtures.inputJpgWithCmykProfile)
.pipelineColourspace('cmyk')
.toColourspace('cmyk')
.keepIccProfile()
.toBuffer();
const metadata = await sharp(data).metadata();
assert.strictEqual(metadata.channels, 4);
const { description } = icc.parse(metadata.icc);
assert.strictEqual(description, 'U.S. Web Coated (SWOP) v2');
});
it('transform to ICC profile and attach', async () => { it('transform to ICC profile and attach', async () => {
const data = await sharp({ create }) const data = await sharp({ create })
.png() .png()

View File

@@ -228,26 +228,34 @@ describe('Text to image', function () {
}); });
}); });
it('bad width input', function () { it('invalid width', () => {
assert.throws(function () { assert.throws(
sharp({ () => sharp({ text: { text: 'text', width: 'bad' } }),
text: { /Expected positive integer for text\.width but received bad of type string/
text: 'text', );
width: 'bad' assert.throws(
} () => sharp({ text: { text: 'text', width: 0.1 } }),
}); /Expected positive integer for text\.width but received 0.1 of type number/
}); );
assert.throws(
() => sharp({ text: { text: 'text', width: -1 } }),
/Expected positive integer for text\.width but received -1 of type number/
);
}); });
it('bad height input', function () { it('invalid height', () => {
assert.throws(function () { assert.throws(
sharp({ () => sharp({ text: { text: 'text', height: 'bad' } }),
text: { /Expected positive integer for text\.height but received bad of type string/
text: 'text', );
height: 'bad' assert.throws(
} () => sharp({ text: { text: 'text', height: 0.1 } }),
}); /Expected positive integer for text\.height but received 0.1 of type number/
}); );
assert.throws(
() => sharp({ text: { text: 'text', height: -1 } }),
/Expected positive integer for text\.height but received -1 of type number/
);
}); });
it('bad align input', function () { it('bad align input', function () {
@@ -272,15 +280,19 @@ describe('Text to image', function () {
}); });
}); });
it('bad dpi input', function () { it('invalid dpi', () => {
assert.throws(function () { assert.throws(
sharp({ () => sharp({ text: { text: 'text', dpi: 'bad' } }),
text: { /Expected integer between 1 and 1000000 for text\.dpi but received bad of type string/
text: 'text', );
dpi: -10 assert.throws(
} () => sharp({ text: { text: 'text', dpi: 0.1 } }),
}); /Expected integer between 1 and 1000000 for text\.dpi but received 0.1 of type number/
}); );
assert.throws(
() => sharp({ text: { text: 'text', dpi: -1 } }),
/Expected integer between 1 and 1000000 for text\.dpi but received -1 of type number/
);
}); });
it('bad rgba input', function () { it('bad rgba input', function () {
@@ -294,15 +306,19 @@ describe('Text to image', function () {
}); });
}); });
it('bad spacing input', function () { it('invalid spacing', () => {
assert.throws(function () { assert.throws(
sharp({ () => sharp({ text: { text: 'text', spacing: 'bad' } }),
text: { /Expected integer between -1000000 and 1000000 for text\.spacing but received bad of type string/
text: 'text', );
spacing: 'number expected' assert.throws(
} () => sharp({ text: { text: 'text', spacing: 0.1 } }),
}); /Expected integer between -1000000 and 1000000 for text\.spacing but received 0.1 of type number/
}); );
assert.throws(
() => sharp({ text: { text: 'text', spacing: -1000001 } }),
/Expected integer between -1000000 and 1000000 for text\.spacing but received -1000001 of type number/
);
}); });
it('only height or dpi not both', function () { it('only height or dpi not both', function () {
@@ -319,21 +335,21 @@ describe('Text to image', function () {
it('valid wrap throws', () => { it('valid wrap throws', () => {
assert.doesNotThrow(() => sharp({ text: { text: 'text', wrap: 'none' } })); assert.doesNotThrow(() => sharp({ text: { text: 'text', wrap: 'none' } }));
assert.doesNotThrow(() => sharp({ text: { text: 'text', wrap: 'wordChar' } })); assert.doesNotThrow(() => sharp({ text: { text: 'text', wrap: 'word-char' } }));
}); });
it('invalid wrap throws', () => { it('invalid wrap throws', () => {
assert.throws( assert.throws(
() => sharp({ text: { text: 'text', wrap: 1 } }), () => sharp({ text: { text: 'text', wrap: 1 } }),
/Expected one of: word, char, wordChar, none for text\.wrap but received 1 of type number/ /Expected one of: word, char, word-char, none for text\.wrap but received 1 of type number/
); );
assert.throws( assert.throws(
() => sharp({ text: { text: 'text', wrap: false } }), () => sharp({ text: { text: 'text', wrap: false } }),
/Expected one of: word, char, wordChar, none for text\.wrap but received false of type boolean/ /Expected one of: word, char, word-char, none for text\.wrap but received false of type boolean/
); );
assert.throws( assert.throws(
() => sharp({ text: { text: 'text', wrap: 'invalid' } }), () => sharp({ text: { text: 'text', wrap: 'invalid' } }),
/Expected one of: word, char, wordChar, none for text\.wrap but received invalid of type string/ /Expected one of: word, char, word-char, none for text\.wrap but received invalid of type string/
); );
}); });
}); });