Compare commits

...

12 Commits

Author SHA1 Message Date
Lovell Fuller
ddc1eb8c4c Release v0.34.0 (part 2 - see c26e67c) 2025-04-04 14:38:27 +01:00
Lovell Fuller
c26e67cc5b Release v0.34.0 2025-04-04 13:44:11 +01:00
Lovell Fuller
a00ee26d17 Prevent glib assertions and checks #4351 2025-04-04 12:41:33 +01:00
Lovell Fuller
6dfb60cda2 Prevent prebuilt Linux sharp binaries from exporting symbols
The prebuilt Linux libvips binaries already do this
2025-04-04 09:49:37 +01:00
Lovell Fuller
dbb7606129 Prerelease v0.34.0-rc.1 2025-04-03 15:29:32 +01:00
Quentin Pinçon
031c808aa5 Expose erode and dilate operations #4243 2025-03-27 13:17:19 +00:00
Lovell Fuller
03e1b19764 Allow wide-gamut HEIF output at higher bitdepths #4344 2025-03-21 17:01:20 +00:00
Lovell Fuller
3e41f8b65e Non-animated GIF output defaults to no-loop #3394 2025-03-21 09:36:25 +00:00
Lovell Fuller
3fd818c4b5 Prevent use of linux-x64 binaries with v1 microarchitecture 2025-03-19 19:54:53 +00:00
Lovell Fuller
d419aba76d Docs: add note about electron-forge and Webpack combo 2025-03-19 09:19:47 +00:00
Lovell Fuller
3fb1091114 Docs: bump Astro 2025-03-19 09:19:18 +00:00
Kleis Auke Wolthuizen
2035492fd9 Ensure Yarn PnP hash calculation works with pre-releases (#4347) 2025-03-16 22:05:52 +00:00
41 changed files with 372 additions and 67 deletions

View File

@@ -208,7 +208,7 @@ jobs:
contents: write
name: wasm32 - prebuild
runs-on: ubuntu-24.04
container: "emscripten/emsdk:4.0.5"
container: "emscripten/emsdk:4.0.6"
steps:
- name: Checkout
uses: actions/checkout@v4

View File

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

View File

@@ -314,3 +314,6 @@ GitHub: https://github.com/happycollision
Name: Florent Zabera
GitHub: https://github.com/florentzabera
Name: Quentin Pinçon
GitHub: https://github.com/qpincon

View File

@@ -284,6 +284,52 @@ const gaussianBlurred = await sharp(input)
```
## dilate
> dilate([width]) ⇒ <code>Sharp</code>
Expand foreground objects using the dilate morphological operator.
**Throws**:
- <code>Error</code> Invalid parameters
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [width] | <code>Number</code> | <code>1</code> | dilation width in pixels. |
**Example**
```js
const output = await sharp(input)
.dilate()
.toBuffer();
```
## erode
> erode([width]) ⇒ <code>Sharp</code>
Shrink foreground objects using the erode morphological operator.
**Throws**:
- <code>Error</code> Invalid parameters
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [width] | <code>Number</code> | <code>1</code> | erosion width in pixels. |
**Example**
```js
const output = await sharp(input)
.erode()
.toBuffer();
```
## flatten
> flatten([options]) ⇒ <code>Sharp</code>

View File

@@ -6,7 +6,7 @@ title: Changelog
Requires libvips v8.16.1
### v0.34.0 - TBD
### v0.34.0 - 4th April 2025
* Breaking: Support array of input images to be joined or animated.
[#1580](https://github.com/lovell/sharp/issues/1580)
@@ -14,6 +14,9 @@ Requires libvips v8.16.1
* Breaking: Ensure `removeAlpha` removes all alpha channels.
[#2266](https://github.com/lovell/sharp/issues/2266)
* Breaking: Non-animated GIF output defaults to no-loop instead of loop-forever.
[#3394](https://github.com/lovell/sharp/issues/3394)
* Breaking: Support `info.size` on wide-character systems via upgrade to C++17.
[#3943](https://github.com/lovell/sharp/issues/3943)
@@ -24,6 +27,8 @@ Requires libvips v8.16.1
* Expose WebP `smartDeblock` output option.
* Prevent use of linux-x64 binaries with v1 microarchitecture.
* Add `autoOrient` operation and constructor option.
[#4151](https://github.com/lovell/sharp/pull/4151)
[@happycollision](https://github.com/happycollision)
@@ -40,10 +45,17 @@ Requires libvips v8.16.1
[#4207](https://github.com/lovell/sharp/pull/4207)
[@calebmer](https://github.com/calebmer)
* Expose erode and dilate operations.
[#4243](https://github.com/lovell/sharp/pull/4243)
[@qpincon](https://github.com/qpincon)
* Add support for RGBE images. Requires libvips compiled with radiance support.
[#4316](https://github.com/lovell/sharp/pull/4316)
[@florentzabera](https://github.com/florentzabera)
* Allow wide-gamut HEIF output at higher bitdepths.
[#4344](https://github.com/lovell/sharp/issues/4344)
## v0.33 - *gauge*
Requires libvips v8.15.3

View File

@@ -286,6 +286,10 @@ option.
}
```
When using `electron-forge` with [Webpack](#webpack),
you may also need to add
[forge-externals-plugin](https://www.npmjs.com/package/@timfish/forge-externals-plugin).
### vite
Ensure `sharp` is excluded from bundling via the

View File

@@ -263,6 +263,8 @@ const Sharp = function (input, options) {
trimBackground: [],
trimThreshold: -1,
trimLineArt: false,
dilateWidth: 0,
erodeWidth: 0,
gamma: 0,
gammaOut: 0,
greyscale: false,
@@ -296,6 +298,8 @@ const Sharp = function (input, options) {
withExif: {},
withExifMerge: true,
resolveWithObject: false,
loop: 1,
delay: [],
// output format
jpegQuality: 80,
jpegProgressive: false,

16
lib/index.d.ts vendored
View File

@@ -504,6 +504,22 @@ declare namespace sharp {
*/
blur(sigma?: number | boolean | BlurOptions): Sharp;
/**
* Expand foreground objects using the dilate morphological operator.
* @param {Number} [width=1] dilation width in pixels.
* @throws {Error} Invalid parameters
* @returns A sharp instance that can be used to chain operations
*/
dilate(width?: number): Sharp;
/**
* Shrink foreground objects using the erode morphological operator.
* @param {Number} [width=1] erosion width in pixels.
* @throws {Error} Invalid parameters
* @returns A sharp instance that can be used to chain operations
*/
erode(width?: number): Sharp;
/**
* Merge alpha transparency channel, if any, with background.
* @param flatten true to enable and false to disable (defaults to true)

View File

@@ -113,7 +113,9 @@ const sha512 = (s) => createHash('sha512').update(s).digest('hex');
const yarnLocator = () => {
try {
const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
const npmVersion = semverCoerce(optionalDependencies[`@img/sharp-libvips-${buildPlatformArch()}`]).version;
const npmVersion = semverCoerce(optionalDependencies[`@img/sharp-libvips-${buildPlatformArch()}`], {
includePrerelease: true
}).version;
return sha512(`${identHash}npm:${npmVersion}`).slice(0, 10);
} catch {}
return '';

View File

@@ -443,6 +443,52 @@ function blur (options) {
return this;
}
/**
* Expand foreground objects using the dilate morphological operator.
*
* @example
* const output = await sharp(input)
* .dilate()
* .toBuffer();
*
* @param {Number} [width=1] dilation width in pixels.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function dilate (width) {
if (!is.defined(width)) {
this.options.dilateWidth = 1;
} else if (is.integer(width) && width > 0) {
this.options.dilateWidth = width;
} else {
throw is.invalidParameterError('dilate', 'positive integer', dilate);
}
return this;
}
/**
* Shrink foreground objects using the erode morphological operator.
*
* @example
* const output = await sharp(input)
* .erode()
* .toBuffer();
*
* @param {Number} [width=1] erosion width in pixels.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function erode (width) {
if (!is.defined(width)) {
this.options.erodeWidth = 1;
} else if (is.integer(width) && width > 0) {
this.options.erodeWidth = width;
} else {
throw is.invalidParameterError('erode', 'positive integer', erode);
}
return this;
}
/**
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
*
@@ -958,6 +1004,8 @@ module.exports = function (Sharp) {
flop,
affine,
sharpen,
erode,
dilate,
median,
blur,
flatten,

View File

@@ -17,9 +17,9 @@ const paths = [
'@img/sharp-wasm32/sharp.node'
];
let sharp;
let path, sharp;
const errors = [];
for (const path of paths) {
for (path of paths) {
try {
sharp = require(path);
break;
@@ -29,6 +29,14 @@ for (const path of paths) {
}
}
/* istanbul ignore next */
if (sharp && path.startsWith('@img/sharp-linux-x64') && !sharp._isUsingX64V2()) {
const err = new Error('Prebuilt binaries for linux-x64 require v2 microarchitecture');
err.code = 'Unsupported CPU';
errors.push(err);
sharp = null;
}
/* istanbul ignore next */
if (sharp) {
module.exports = sharp;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@img/sharp-wasm32",
"version": "0.34.0-rc.0",
"version": "0.34.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.3.1"
"@emnapi/runtime": "^1.4.0"
},
"cpu": [
"wasm32"

View File

@@ -1,6 +1,6 @@
{
"name": "@img/sharp-win32-ia32",
"version": "0.34.0-rc.0",
"version": "0.34.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-rc.0",
"version": "0.34.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-rc.0",
"version": "0.34.0",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://sharp.pixelplumbing.com",
"contributors": [
@@ -142,36 +142,36 @@
"semver": "^7.7.1"
},
"optionalDependencies": {
"@img/sharp-darwin-arm64": "0.34.0-rc.0",
"@img/sharp-darwin-x64": "0.34.0-rc.0",
"@img/sharp-libvips-darwin-arm64": "1.1.0-rc4",
"@img/sharp-libvips-darwin-x64": "1.1.0-rc4",
"@img/sharp-libvips-linux-arm": "1.1.0-rc4.1",
"@img/sharp-libvips-linux-arm64": "1.1.0-rc4",
"@img/sharp-libvips-linux-ppc64": "1.1.0-rc4",
"@img/sharp-libvips-linux-s390x": "1.1.0-rc4",
"@img/sharp-libvips-linux-x64": "1.1.0-rc4",
"@img/sharp-libvips-linuxmusl-arm64": "1.1.0-rc4",
"@img/sharp-libvips-linuxmusl-x64": "1.1.0-rc4",
"@img/sharp-linux-arm": "0.34.0-rc.0",
"@img/sharp-linux-arm64": "0.34.0-rc.0",
"@img/sharp-linux-s390x": "0.34.0-rc.0",
"@img/sharp-linux-x64": "0.34.0-rc.0",
"@img/sharp-linuxmusl-arm64": "0.34.0-rc.0",
"@img/sharp-linuxmusl-x64": "0.34.0-rc.0",
"@img/sharp-wasm32": "0.34.0-rc.0",
"@img/sharp-win32-ia32": "0.34.0-rc.0",
"@img/sharp-win32-x64": "0.34.0-rc.0"
"@img/sharp-darwin-arm64": "0.34.0",
"@img/sharp-darwin-x64": "0.34.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",
"@img/sharp-libvips-linux-arm64": "1.1.0",
"@img/sharp-libvips-linux-ppc64": "1.1.0",
"@img/sharp-libvips-linux-s390x": "1.1.0",
"@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"
},
"devDependencies": {
"@emnapi/runtime": "^1.3.1",
"@img/sharp-libvips-dev": "1.1.0-rc4",
"@img/sharp-libvips-dev-wasm32": "1.1.0-rc4",
"@img/sharp-libvips-win32-ia32": "1.1.0-rc4",
"@img/sharp-libvips-win32-x64": "1.1.0-rc4",
"@emnapi/runtime": "^1.4.0",
"@img/sharp-libvips-dev": "1.1.0",
"@img/sharp-libvips-dev-wasm32": "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.3.1",
"emnapi": "^1.4.0",
"exif-reader": "^2.0.2",
"extract-zip": "^2.0.1",
"icc": "^3.0.0",

View File

@@ -80,6 +80,9 @@
}, {
'target_name': 'sharp-<(platform_and_arch)',
'defines': [
'G_DISABLE_ASSERT',
'G_DISABLE_CAST_CHECKS',
'G_DISABLE_CHECKS',
'NAPI_VERSION=9',
'NODE_ADDON_API_DISABLE_DEPRECATED',
'NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS'
@@ -179,6 +182,7 @@
'-Wl,-s',
'-Wl,--disable-new-dtags',
'-Wl,-z,nodelete',
'-Wl,-Bsymbolic-functions',
'-Wl,-rpath=\'$$ORIGIN/../../sharp-libvips-<(platform_and_arch)/lib\'',
'-Wl,-rpath=\'$$ORIGIN/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'',
'-Wl,-rpath=\'$$ORIGIN/../../node_modules/@img/sharp-libvips-<(platform_and_arch)/lib\'',

View File

@@ -472,4 +472,26 @@ namespace sharp {
}
}
/*
* Dilate an image
*/
VImage Dilate(VImage image, int const width) {
int const maskWidth = 2 * width + 1;
VImage mask = VImage::new_matrix(maskWidth, maskWidth);
return image.morph(
mask,
VIPS_OPERATION_MORPHOLOGY_DILATE).invert();
}
/*
* Erode an image
*/
VImage Erode(VImage image, int const width) {
int const maskWidth = 2 * width + 1;
VImage mask = VImage::new_matrix(maskWidth, maskWidth);
return image.morph(
mask,
VIPS_OPERATION_MORPHOLOGY_ERODE).invert();
}
} // namespace sharp

View File

@@ -120,6 +120,15 @@ namespace sharp {
VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight);
/*
* Dilate an image
*/
VImage Dilate(VImage image, int const maskWidth);
/*
* Erode an image
*/
VImage Erode(VImage image, int const maskWidth);
} // namespace sharp
#endif // SRC_OPERATIONS_H_

View File

@@ -609,6 +609,16 @@ class PipelineWorker : public Napi::AsyncWorker {
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
}
// Dilate - must happen before blurring, due to the utility of dilating after thresholding
if (baton->dilateWidth != 0) {
image = sharp::Dilate(image, baton->dilateWidth);
}
// Erode - must happen before blurring, due to the utility of eroding after thresholding
if (baton->erodeWidth != 0) {
image = sharp::Erode(image, baton->erodeWidth);
}
// Blur
if (shouldBlur) {
image = sharp::Blur(image, baton->blurSigma, baton->precision, baton->minAmpl);
@@ -1032,7 +1042,7 @@ class PipelineWorker : public Napi::AsyncWorker {
(baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
// Write HEIF to buffer
sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
image = sharp::RemoveAnimationProperties(image);
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
->set("keep", baton->keepMetadata)
->set("Q", baton->heifQuality)
@@ -1227,7 +1237,7 @@ class PipelineWorker : public Napi::AsyncWorker {
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
// Write HEIF to file
sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
image = sharp::RemoveAnimationProperties(image);
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("keep", baton->keepMetadata)
->set("Q", baton->heifQuality)
@@ -1621,6 +1631,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
baton->linearB = sharp::AttrAsVectorOfDouble(options, "linearB");
baton->dilateWidth = sharp::AttrAsUint32(options, "dilateWidth");
baton->erodeWidth = sharp::AttrAsUint32(options, "erodeWidth");
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
baton->normalise = sharp::AttrAsBool(options, "normalise");
baton->normaliseLower = sharp::AttrAsUint32(options, "normaliseLower");
@@ -1705,6 +1717,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
}
baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
baton->loop = sharp::AttrAsUint32(options, "loop");
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
// Format-specific
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
@@ -1774,13 +1788,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->jxlEffort = sharp::AttrAsUint32(options, "jxlEffort");
baton->jxlLossless = sharp::AttrAsBool(options, "jxlLossless");
baton->rawDepth = sharp::AttrAsEnum<VipsBandFormat>(options, "rawDepth", VIPS_TYPE_BAND_FORMAT);
// Animated output properties
if (sharp::HasAttr(options, "loop")) {
baton->loop = sharp::AttrAsUint32(options, "loop");
}
if (sharp::HasAttr(options, "delay")) {
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
}
baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
baton->tileAngle = sharp::AttrAsInt32(options, "tileAngle");

View File

@@ -101,6 +101,8 @@ struct PipelineBaton {
int trimOffsetTop;
std::vector<double> linearA;
std::vector<double> linearB;
int dilateWidth;
int erodeWidth;
double gamma;
double gammaOut;
bool greyscale;
@@ -274,6 +276,8 @@ struct PipelineBaton {
trimOffsetTop(0),
linearA{},
linearB{},
dilateWidth(0),
erodeWidth(0),
gamma(0.0),
greyscale(false),
normalise(false),
@@ -380,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

@@ -33,6 +33,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
exports.Set("block", Napi::Function::New(env, block));
exports.Set("_maxColourDistance", Napi::Function::New(env, _maxColourDistance));
exports.Set("_isUsingJemalloc", Napi::Function::New(env, _isUsingJemalloc));
exports.Set("_isUsingX64V2", Napi::Function::New(env, _isUsingX64V2));
exports.Set("stats", Napi::Function::New(env, stats));
return exports;
}

View File

@@ -267,3 +267,20 @@ Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
return Napi::Boolean::New(env, false);
}
#endif
#if defined(__GNUC__) && defined(__x86_64__)
// Are SSE 4.2 intrinsics available at runtime?
Napi::Value _isUsingX64V2(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
unsigned int eax, ebx, ecx, edx;
__asm__ __volatile__("cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "a"(1));
return Napi::Boolean::New(env, (ecx & 1U << 20) != 0);
}
#else
Napi::Value _isUsingX64V2(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
return Napi::Boolean::New(env, false);
}
#endif

View File

@@ -15,5 +15,6 @@ Napi::Value format(const Napi::CallbackInfo& info);
void block(const Napi::CallbackInfo& info);
Napi::Value _maxColourDistance(const Napi::CallbackInfo& info);
Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info);
Napi::Value _isUsingX64V2(const Napi::CallbackInfo& info);
#endif // SRC_UTILITIES_H_

BIN
test/fixtures/dot-and-lines.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

BIN
test/fixtures/expected/dilate-1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

BIN
test/fixtures/expected/erode-1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

View File

@@ -128,6 +128,8 @@ module.exports = {
inputJPGBig: getPath('flowers.jpeg'),
inputPngDotAndLines: getPath('dot-and-lines.png'),
inputPngStripesV: getPath('stripesV.png'),
inputPngStripesH: getPath('stripesH.png'),

View File

@@ -740,3 +740,8 @@ sharp([input, input], {
valign: 'bottom'
}
});
sharp().erode();
sharp().erode(1);
sharp().dilate();
sharp().dilate(1);

38
test/unit/dilate.js Normal file
View File

@@ -0,0 +1,38 @@
'use strict';
const assert = require('assert');
const sharp = require('../../');
const fixtures = require('../fixtures');
describe('Dilate', function () {
it('dilate 1 png', function (done) {
sharp(fixtures.inputPngDotAndLines)
.dilate(1)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height);
fixtures.assertSimilar(fixtures.expected('dilate-1.png'), data, done);
});
});
it('dilate 1 png - default width', function (done) {
sharp(fixtures.inputPngDotAndLines)
.dilate()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height);
fixtures.assertSimilar(fixtures.expected('dilate-1.png'), data, done);
});
});
it('invalid dilation width', function () {
assert.throws(function () {
sharp(fixtures.inputJpg).dilate(-1);
});
});
});

38
test/unit/erode.js Normal file
View File

@@ -0,0 +1,38 @@
'use strict';
const assert = require('assert');
const sharp = require('../../');
const fixtures = require('../fixtures');
describe('Erode', function () {
it('erode 1 png', function (done) {
sharp(fixtures.inputPngDotAndLines)
.erode(1)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height);
fixtures.assertSimilar(fixtures.expected('erode-1.png'), data, done);
});
});
it('erode 1 png - default width', function (done) {
sharp(fixtures.inputPngDotAndLines)
.erode()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height);
fixtures.assertSimilar(fixtures.expected('erode-1.png'), data, done);
});
});
it('invalid erosion width', function () {
assert.throws(function () {
sharp(fixtures.inputJpg).erode(-1);
});
});
});

View File

@@ -224,4 +224,18 @@ describe('GIF input', () => {
const after = await input.gif({ interPaletteMaxError: 100 }).toBuffer();
assert.strict(before.length > after.length);
});
it('non-animated input defaults to no-loop', async () => {
for (const input of [fixtures.inputGif, fixtures.inputPng]) {
const data = await sharp(input)
.resize(8)
.gif({ effort: 1 })
.toBuffer();
const { format, pages, loop } = await sharp(data).metadata();
assert.strictEqual('gif', format);
assert.strictEqual(1, pages);
assert.strictEqual(1, loop);
}
});
});