Compare commits

...

24 Commits

Author SHA1 Message Date
Lovell Fuller
956d72ddc0 Prerelease v0.34.2-rc.0 2025-05-14 12:37:15 +01:00
Lovell Fuller
00e66efbee Bump deps 2025-05-14 12:37:08 +01:00
Kleis Auke Wolthuizen
db3a4528eb Simplify 94481a9 2025-05-13 19:22:36 +01:00
Kleis Auke Wolthuizen
d36fd5064d Prefer use of bandjoin_const() and list-initialization 2025-05-13 19:22:36 +01:00
Kleis Auke Wolthuizen
8e17c6f518 Prefer use of vips_interpretation_max_alpha()
This also ensures we handle scRGB correctly, see:
e9c5a31552
2025-05-13 19:22:36 +01:00
Lovell Fuller
94481a967e Ensure fit=contain resizing supports multiple alpha channels #4382 2025-05-13 14:31:51 +01:00
Lovell Fuller
32872ef840 TypeScript: ensure metadata response matches reality #4383 2025-05-13 14:26:25 +01:00
Lovell Fuller
7c7f960b60 Ensure support for wide-char filenames on Windows #4391 2025-05-13 08:53:37 +01:00
Kleis Auke Wolthuizen
0b5f131df8 Improve install error help text for ppc64le architecture (#4392) 2025-05-11 08:44:15 +01:00
Hans
e922ef7450 Add support for prebuilt Windows ARM64 binaries #4375 2025-05-10 14:29:25 +01:00
Lovell Fuller
73bec629cf Docs: website already hosts images, no need to use jsdelivr 2025-05-05 13:20:49 +01:00
Lovell Fuller
758d7e63cc Docs: changelog entry for #4387 2025-05-05 13:19:10 +01:00
Stephen Tse
eba3e9aeb2 TypeScript: Add smartDeblock definition to WebpOptions (#4387) 2025-05-05 13:15:50 +01:00
Lovell Fuller
701143afb3 Ensure animated GIF to WebP conversion retains loop #3394 2025-04-23 15:43:58 +01:00
Steven D'Onfro
38b6f44611 TypeScript: Add missing properties for animated images (#4369) 2025-04-12 11:38:24 +01:00
Lovell Fuller
5b5dfbad77 Ensure pdfBackground constructor property is used #4207
Slightly refactor the way background colours are set
2025-04-09 22:21:14 +01:00
Kleis Auke Wolthuizen
a642767329 Docs: update path to api-resize-fit.svg (#4368) 2025-04-08 10:15:29 +01:00
Lovell Fuller
5cae1abe8f Release v0.34.1 2025-04-07 20:51:11 +01:00
Lovell Fuller
66ffc48707 Changelog entry for #4362 2025-04-07 13:35:40 +01:00
Steven
3c7dbb8fba fix(types): update autoOrient type to include undefined (#4362)
This PR fixes the following error:

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

View File

@@ -110,6 +110,17 @@ jobs:
nodejs_version: "^22.9.0"
nodejs_version_major: 22
platform: win32-x64
- os: windows-11-arm
nodejs_arch: arm64
nodejs_version: "^20.3.0"
nodejs_version_major: 20
platform: win32-arm64
prebuild: true
- os: windows-11-arm
nodejs_arch: arm64
nodejs_version: "^22.9.0"
nodejs_version_major: 22
platform: win32-arm64
steps:
- name: Dependencies (Rocky Linux glibc)
if: contains(matrix.container, 'rockylinux')

View File

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

View File

@@ -18,7 +18,7 @@ export default defineConfig({
tag: 'meta',
attrs: {
'http-equiv': 'Content-Security-Policy',
content: "default-src 'self'; connect-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://cdn.jsdelivr.net/gh/lovell/; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com/beacon.min.js/;"
content: "default-src 'self'; connect-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com/beacon.min.js/;"
}
}, {
tag: 'link',

View File

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

View File

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

View File

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

View File

@@ -6,6 +6,37 @@ title: Changelog
Requires libvips v8.16.1
### v0.34.2 - TBD
* Ensure animated GIF to WebP conversion retains loop (regression in 0.34.0).
[#3394](https://github.com/lovell/sharp/issues/3394)
* Ensure `pdfBackground` constructor property is used.
[#4207](https://github.com/lovell/sharp/pull/4207)
* Add support for prebuilt Windows ARM64 binaries.
[#4375](https://github.com/lovell/sharp/pull/4375)
[@hans00](https://github.com/hans00)
* Ensure resizing with a `fit` of `contain` supports multiple alpha channels.
[#4382](https://github.com/lovell/sharp/issues/4382)
* TypeScript: Ensure `metadata` response more closely matches reality.
[#4383](https://github.com/lovell/sharp/issues/4383)
* TypeScript: Ensure `smartDeblock` property is included in WebP definition.
[#4387](https://github.com/lovell/sharp/pull/4387)
[@Stephen-X](https://github.com/Stephen-X)
* Ensure support for wide-character filenames on Windows (regression in 0.34.0).
[#4391](https://github.com/lovell/sharp/issues/4391)
### v0.34.1 - 7th April 2025
* TypeScript: Ensure new `autoOrient` property is optional.
[#4362](https://github.com/lovell/sharp/pull/4362)
[@styfle](https://github.com/styfle)
### v0.34.0 - 4th April 2025
* Breaking: Support array of input images to be joined or animated.

View File

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

View File

@@ -53,6 +53,7 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
* Linux x64 (glibc >= 2.26, musl >= 1.2.2, CPU with SSE4.2)
* Windows x64
* Windows x86
* Windows ARM64
This provides support for the
JPEG, PNG, WebP, AVIF (limited to 8-bit depth), TIFF, GIF and SVG (input) image formats.

View File

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

View File

@@ -298,7 +298,7 @@ const Sharp = function (input, options) {
withExif: {},
withExifMerge: true,
resolveWithObject: false,
loop: 1,
loop: -1,
delay: [],
// output format
jpegQuality: 80,

61
lib/index.d.ts vendored
View File

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

View File

@@ -3,7 +3,6 @@
'use strict';
const color = require('color');
const is = require('./is');
const sharp = require('./sharp');
@@ -249,7 +248,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
}
// PDF background colour
if (is.defined(inputOptions.pdfBackground)) {
this._setBackgroundColourOption('pdfBackground', inputOptions.pdfBackground);
inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdfBackground);
}
// Create new image
if (is.defined(inputOptions.create)) {
@@ -288,13 +287,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
if (!is.inRange(inputOptions.create.channels, 3, 4)) {
throw is.invalidParameterError('create.channels', 'number between 3 and 4', inputOptions.create.channels);
}
const background = color(inputOptions.create.background);
inputDescriptor.createBackground = [
background.red(),
background.green(),
background.blue(),
Math.round(background.alpha() * 255)
];
inputDescriptor.createBackground = this._getBackgroundColourOption(inputOptions.create.background);
} else {
throw new Error('Expected valid noise or background to create a new input image');
}
@@ -410,13 +403,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
}
}
if (is.defined(inputOptions.join.background)) {
const background = color(inputOptions.join.background);
inputDescriptor.joinBackground = [
background.red(),
background.green(),
background.blue(),
Math.round(background.alpha() * 255)
];
inputDescriptor.joinBackground = this._getBackgroundColourOption(inputOptions.join.background);
}
if (is.defined(inputOptions.join.halign)) {
if (is.string(inputOptions.join.halign) && is.string(this.constructor.align[inputOptions.join.halign])) {

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@img/sharp-darwin-arm64",
"version": "0.34.0",
"version": "0.34.2-rc.0",
"description": "Prebuilt sharp for use with macOS 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@img/sharp-linux-arm",
"version": "0.34.0",
"version": "0.34.2-rc.0",
"description": "Prebuilt sharp for use with Linux (glibc) ARM (32-bit)",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",

View File

@@ -1,6 +1,6 @@
{
"name": "@img/sharp-linux-arm64",
"version": "0.34.0",
"version": "0.34.2-rc.0",
"description": "Prebuilt sharp for use with Linux (glibc) 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",

View File

@@ -1,6 +1,6 @@
{
"name": "@img/sharp-linux-ppc64",
"version": "0.34.0",
"version": "0.34.2-rc.0",
"description": "Prebuilt sharp for use with Linux (glibc) ppc64",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",

View File

@@ -1,6 +1,6 @@
{
"name": "@img/sharp-linux-s390x",
"version": "0.34.0",
"version": "0.34.2-rc.0",
"description": "Prebuilt sharp for use with Linux (glibc) s390x",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@img/sharp-linuxmusl-arm64",
"version": "0.34.0",
"version": "0.34.2-rc.0",
"description": "Prebuilt sharp for use with Linux (musl) 64-bit ARM",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.34.0",
"version": "0.34.2-rc.0",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",
"contributors": [
@@ -138,12 +138,12 @@
],
"dependencies": {
"color": "^4.2.3",
"detect-libc": "^2.0.3",
"semver": "^7.7.1"
"detect-libc": "^2.0.4",
"semver": "^7.7.2"
},
"optionalDependencies": {
"@img/sharp-darwin-arm64": "0.34.0",
"@img/sharp-darwin-x64": "0.34.0",
"@img/sharp-darwin-arm64": "0.34.2-rc.0",
"@img/sharp-darwin-x64": "0.34.2-rc.0",
"@img/sharp-libvips-darwin-arm64": "1.1.0",
"@img/sharp-libvips-darwin-x64": "1.1.0",
"@img/sharp-libvips-linux-arm": "1.1.0",
@@ -153,37 +153,39 @@
"@img/sharp-libvips-linux-x64": "1.1.0",
"@img/sharp-libvips-linuxmusl-arm64": "1.1.0",
"@img/sharp-libvips-linuxmusl-x64": "1.1.0",
"@img/sharp-linux-arm": "0.34.0",
"@img/sharp-linux-arm64": "0.34.0",
"@img/sharp-linux-s390x": "0.34.0",
"@img/sharp-linux-x64": "0.34.0",
"@img/sharp-linuxmusl-arm64": "0.34.0",
"@img/sharp-linuxmusl-x64": "0.34.0",
"@img/sharp-wasm32": "0.34.0",
"@img/sharp-win32-ia32": "0.34.0",
"@img/sharp-win32-x64": "0.34.0"
"@img/sharp-linux-arm": "0.34.2-rc.0",
"@img/sharp-linux-arm64": "0.34.2-rc.0",
"@img/sharp-linux-s390x": "0.34.2-rc.0",
"@img/sharp-linux-x64": "0.34.2-rc.0",
"@img/sharp-linuxmusl-arm64": "0.34.2-rc.0",
"@img/sharp-linuxmusl-x64": "0.34.2-rc.0",
"@img/sharp-wasm32": "0.34.2-rc.0",
"@img/sharp-win32-arm64": "0.34.2-rc.0",
"@img/sharp-win32-ia32": "0.34.2-rc.0",
"@img/sharp-win32-x64": "0.34.2-rc.0"
},
"devDependencies": {
"@emnapi/runtime": "^1.4.0",
"@emnapi/runtime": "^1.4.3",
"@img/sharp-libvips-dev": "1.1.0",
"@img/sharp-libvips-dev-wasm32": "1.1.0",
"@img/sharp-libvips-win32-arm64": "1.1.0",
"@img/sharp-libvips-win32-ia32": "1.1.0",
"@img/sharp-libvips-win32-x64": "1.1.0",
"@types/node": "*",
"cc": "^3.0.1",
"emnapi": "^1.4.0",
"emnapi": "^1.4.3",
"exif-reader": "^2.0.2",
"extract-zip": "^2.0.1",
"icc": "^3.0.0",
"jsdoc-to-markdown": "^9.1.1",
"license-checker": "^25.0.1",
"mocha": "^11.1.0",
"mocha": "^11.2.2",
"node-addon-api": "^8.3.1",
"nyc": "^17.1.0",
"prebuild": "^13.0.1",
"semistandard": "^17.0.0",
"tar-fs": "^3.0.8",
"tsd": "^0.31.2"
"tsd": "^0.32.0"
},
"license": "Apache-2.0",
"engines": {

View File

@@ -19,7 +19,10 @@
'type': 'shared_library',
'defines': [
'_VIPS_PUBLIC=__declspec(dllexport)',
'_ALLOW_KEYWORD_MACROS'
'_ALLOW_KEYWORD_MACROS',
'G_DISABLE_ASSERT',
'G_DISABLE_CAST_CHECKS',
'G_DISABLE_CHECKS'
],
'sources': [
'<(sharp_libvips_cplusplus_dir)/VConnection.cpp',

View File

@@ -651,22 +651,21 @@ namespace sharp {
*/
VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop) {
bool hasDelay = !delay.empty();
// Avoid a copy if none of the animation properties are needed.
if (nPages == 1 && !hasDelay && loop == -1) return image;
if (delay.size() == 1) {
// We have just one delay, repeat that value for all frames.
delay.insert(delay.end(), nPages - 1, delay[0]);
}
// Attaching metadata, need to copy the image.
VImage copy = image.copy();
// Only set page-height if we have more than one page, or this could
// accidentally turn into an animated image later.
if (nPages > 1) copy.set(VIPS_META_PAGE_HEIGHT, pageHeight);
if (hasDelay) copy.set("delay", delay);
if (hasDelay) {
if (delay.size() == 1) {
// We have just one delay, repeat that value for all frames.
delay.insert(delay.end(), nPages - 1, delay[0]);
}
copy.set("delay", delay);
}
if (nPages == 1 && !hasDelay && loop == -1) {
loop = 1;
}
if (loop != -1) copy.set("loop", loop);
return copy;
@@ -952,14 +951,6 @@ namespace sharp {
return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
}
/*
Return the image alpha maximum. Useful for combining alpha bands. scRGB
images are 0 - 1 for image data, but the alpha is 0 - 255.
*/
double MaximumImageAlpha(VipsInterpretation const interpretation) {
return Is16Bit(interpretation) ? 65535.0 : 255.0;
}
/*
Convert RGBA value to another colourspace
*/
@@ -1002,16 +993,16 @@ namespace sharp {
0.0722 * colour[2])
};
}
// Add alpha channel to alphaColour colour
// Add alpha channel(s) to alphaColour colour
if (colour[3] < 255.0 || image.has_alpha()) {
alphaColour.push_back(colour[3] * multiplier);
int extraBands = image.bands() > 4 ? image.bands() - 3 : 1;
alphaColour.insert(alphaColour.end(), extraBands, colour[3] * multiplier);
}
// Ensure alphaColour colour uses correct colourspace
alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation(), premultiply);
// Add non-transparent alpha channel, if required
if (colour[3] < 255.0 && !image.has_alpha()) {
image = image.bandjoin(
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier).cast(image.format()));
image = image.bandjoin_const({ 255 * multiplier });
}
return std::make_tuple(image, alphaColour);
}
@@ -1031,9 +1022,7 @@ namespace sharp {
*/
VImage EnsureAlpha(VImage image, double const value) {
if (!image.has_alpha()) {
std::vector<double> alpha;
alpha.push_back(value * sharp::MaximumImageAlpha(image.interpretation()));
image = image.bandjoin_const(alpha);
image = image.bandjoin_const({ value * vips_interpretation_max_alpha(image.interpretation()) });
}
return image;
}

View File

@@ -357,12 +357,6 @@ namespace sharp {
*/
bool Is16Bit(VipsInterpretation const interpretation);
/*
Return the image alpha maximum. Useful for combining alpha bands. scRGB
images are 0 - 1 for image data, but the alpha is 0 - 255.
*/
double MaximumImageAlpha(VipsInterpretation const interpretation);
/*
Convert RGBA value to another colourspace
*/

View File

@@ -1359,7 +1359,8 @@ class PipelineWorker : public Napi::AsyncWorker {
// Add file size to info
if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
try {
uint32_t const size = static_cast<uint32_t>(std::filesystem::file_size(baton->fileOut));
uint32_t const size = static_cast<uint32_t>(
std::filesystem::file_size(std::filesystem::u8path(baton->fileOut)));
info.Set("size", size);
} catch (...) {}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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