Compare commits

...

16 Commits

Author SHA1 Message Date
Lovell Fuller
956f7e29db Release v0.27.2 2021-02-22 20:30:42 +00:00
Lovell Fuller
4264c0577e Improve experience for those using Apple M1 devices #2460
- For Rosetta x64, prevent use of global ARM64 libvips
- For ARM64, improve error message when global libvips not found
2021-02-22 13:49:31 +00:00
Lovell Fuller
cc37b59309 Switch to libvips' recently-exposed has_alpha #2569 2021-02-22 12:32:20 +00:00
Lovell Fuller
9f2f92095d Skip prebuilt binaries for musl >=1.2.0 #2570 2021-02-20 19:40:40 +00:00
Lovell Fuller
0c1075c089 Docs: local compilation requires --build-from-source flag 2021-02-20 15:43:48 +00:00
allx
9c64710c8b Allow code bundling of utility functions (#2586) 2021-02-20 15:39:25 +00:00
Lovell Fuller
f6f16b91db Allow use of recomb op with 1/2 channel input #2584 2021-02-19 16:37:29 +00:00
Lovell Fuller
1986b5cfe6 Bump deps 2021-02-19 15:49:21 +00:00
Lovell Fuller
6445b72d41 Docs: Changelog entry and credit for #2581 2021-02-19 15:48:59 +00:00
Florian Busch
df7b8ba738 Add support for non lower case extensions with toFormat 2021-02-17 20:46:13 +00:00
Pedro Poveda
202083999e Docs: add closing parenthesis so code example runs 2021-02-14 15:57:06 +00:00
aprat84
315f519e1d Docs: correct type for AVIF speed output option (#2568) 2021-02-08 20:37:56 +00:00
Lovell Fuller
d7d580ae6f Tests: using parallel fails on latest Node.js 15.8.0 2021-02-08 13:12:35 +00:00
Lovell Fuller
7017af303d Improve error message when attempting toFile/GIF without magick 2021-02-08 11:46:13 +00:00
Lovell Fuller
0dc325daa4 Docs: add section about Webpack configuration 2021-01-29 11:29:24 +00:00
Lovell Fuller
6dffb47973 Docs: small search index improvements 2021-01-29 11:28:48 +00:00
21 changed files with 158 additions and 63 deletions

View File

@@ -27,7 +27,7 @@ Please select the `master` branch as the destination for your Pull Request so yo
Please squash your changes into a single commit using a command like `git rebase -i upstream/master`.
To test C++ changes, you can compile the module using `npm install` and then run the tests using `npm test`.
To test C++ changes, you can compile the module using `npm install --build-from-source` and then run the tests using `npm test`.
## Submit a Pull Request with a new feature

View File

@@ -116,7 +116,7 @@ await sharp({
sigma: 30
}
}
}.toFile('noise.png');
}).toFile('noise.png');
```
- Throws **[Error][10]** Invalid parameters

View File

@@ -326,7 +326,7 @@ most web browsers do not display these properly.
- `options` **[Object][6]?** output options
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
- `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
- `options.speed` **[boolean][7]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
- `options.speed` **[number][9]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
- `options.chromaSubsampling` **[string][2]** set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0 (optional, default `'4:2:0'`)

View File

@@ -4,6 +4,24 @@
Requires libvips v8.10.5
### v0.27.2 - 22nd February 2021
* macOS: Prevent use of globally-installed ARM64 libvips with Rosetta x64 emulation.
[#2460](https://github.com/lovell/sharp/issues/2460)
* Linux (musl): Prevent use of prebuilt linuxmusl-x64 binaries with musl >= 1.2.0.
[#2570](https://github.com/lovell/sharp/issues/2570)
* Improve 16-bit grey+alpha support by using libvips' `has_alpha` detection.
[#2569](https://github.com/lovell/sharp/issues/2569)
* Allow the use of non lower case extensions with `toFormat`.
[#2581](https://github.com/lovell/sharp/pull/2581)
[@florian-busch](https://github.com/florian-busch)
* Allow use of `recomb` operation with single channel input.
[#2584](https://github.com/lovell/sharp/issues/2584)
### v0.27.1 - 27th January 2021
* Ensure TIFF is cast when using float predictor.

View File

@@ -206,3 +206,6 @@ GitHub: https://github.com/stefanprobst
Name: Thomas Beiganz
GitHub: https://github.com/beig
Name: Florian Busch
GitHub: https://github.com/florian-busch

View File

@@ -19,7 +19,7 @@
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
<link rel="author" href="/humans.txt" type="text/plain">
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.27.1/docs/README.md" as="fetch" type="text/markdown" crossorigin>
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.27.2/docs/README.md" as="fetch" type="text/markdown" crossorigin>
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" as="image" type="image/svg+xml" crossorigin>
<link rel="dns-prefetch" href="https://pixel.plumbing">
<link rel="dns-prefetch" href="https://www.google-analytics.com">
@@ -139,7 +139,7 @@
docuteApiTitlePlugin,
docuteApiSearchPlugin
],
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.27.1/docs',
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.27.2/docs',
nav: [
{
title: 'Funding',

View File

@@ -18,7 +18,7 @@ Ready-compiled sharp and libvips binaries are provided for use with
Node.js v10+ on the most common platforms:
* macOS x64 (>= 10.13)
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
* Linux x64 (glibc >= 2.17, musl >=1.1.24 <1.2.0)
* Linux ARM64 (glibc >= 2.29)
* Windows x64
* Windows x86
@@ -60,7 +60,13 @@ Check the output of running `npm install --verbose sharp` for useful error messa
## Apple M1
libvips must currently be installed via Homebrew before installing sharp.
If you are using ARM64 Node.js, which can be checked using:
```sh
node -p "process.arch === 'arm64'"
```
then libvips must currently be installed via Homebrew before installing sharp.
```sh
brew install vips
@@ -192,6 +198,18 @@ docker run -v "$PWD":/var/task lambci/lambda:build-nodejs12.x npm install sharp
To get the best performance select the largest memory available.
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
## Webpack
Ensure sharp is added to the
[externals](https://webpack.js.org/configuration/externals/)
configuration.
```js
externals: {
'sharp': 'commonjs sharp'
}
```
## Worker threads
The main thread must call `require('sharp')`

File diff suppressed because one or more lines are too long

View File

@@ -17,7 +17,7 @@ const extractKeywords = (str) =>
str
.split(/[ -/]/)
.map((word) => word.toLowerCase().replace(/[^a-z]/g, ''))
.filter((word) => word.length > 2 && !stopWords.includes(word))
.filter((word) => word.length > 2 && word.length < 15 && !stopWords.includes(word))
)
].join(' ');

View File

@@ -18,7 +18,9 @@ module.exports = [
'does',
'each',
'either',
'ensure',
'etc',
'every',
'for',
'from',
'get',

View File

@@ -74,6 +74,9 @@ try {
if (arch === 'ia32' && !platformAndArch.startsWith('win32')) {
throw new Error(`Intel Architecture 32-bit systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
}
if (platformAndArch === 'darwin-arm64') {
throw new Error("Please run 'brew install vips' to install libvips on Apple M1 (ARM64) systems");
}
if (platformAndArch === 'freebsd-x64' || platformAndArch === 'openbsd-x64' || platformAndArch === 'sunos-x64') {
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
}
@@ -82,6 +85,11 @@ try {
throw new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
}
}
if (detectLibc.family === detectLibc.MUSL && detectLibc.version) {
if (!semver.satisfies(detectLibc.version, '>=1.1.24 <1.2.0')) {
throw new Error(`Use with musl ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
}
}
const supportedNodeVersion = process.env.npm_package_engines_node || require('../package.json').engines.node;
if (!semver.satisfies(process.versions.node, supportedNodeVersion)) {

View File

@@ -117,7 +117,7 @@ const debuglog = util.debuglog('sharp');
* sigma: 30
* }
* }
* }.toFile('noise.png');
* }).toFile('noise.png');
*
* @param {(Buffer|Uint8Array|Uint8ClampedArray|string)} [input] - if present, can be
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or

View File

@@ -37,6 +37,15 @@ const cachePath = function () {
return libvipsCachePath;
};
const isRosetta = function () {
/* istanbul ignore next */
if (process.platform === 'darwin' && process.arch === 'x64') {
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
return (translated || '').trim() === 'sysctl.proc_translated: 1';
}
return false;
};
const globalLibvipsVersion = function () {
if (process.platform !== 'win32') {
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
@@ -82,7 +91,10 @@ const useGlobalLibvips = function () {
if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
return false;
}
/* istanbul ignore next */
if (isRosetta()) {
return false;
}
const globalVipsVersion = globalLibvipsVersion();
return !!globalVipsVersion && /* istanbul ignore next */
semver.gte(globalVipsVersion, minimumLibvipsVersion);

View File

@@ -16,6 +16,8 @@ const formats = new Map([
['gif', 'gif']
]);
const errMagickSave = new Error('GIF output requires libvips with support for ImageMagick');
/**
* Write output image data to a file.
*
@@ -47,25 +49,23 @@ const formats = new Map([
* @throws {Error} Invalid parameters
*/
function toFile (fileOut, callback) {
if (!fileOut || fileOut.length === 0) {
const errOutputInvalid = new Error('Missing output file path');
let err;
if (!is.string(fileOut)) {
err = new Error('Missing output file path');
} else if (this.options.input.file === fileOut) {
err = new Error('Cannot use same file for input and output');
} else if (this.options.formatOut === 'input' && fileOut.toLowerCase().endsWith('.gif') && !this.constructor.format.magick.output.file) {
err = errMagickSave;
}
if (err) {
if (is.fn(callback)) {
callback(errOutputInvalid);
callback(err);
} else {
return Promise.reject(errOutputInvalid);
return Promise.reject(err);
}
} else {
if (this.options.input.file === fileOut) {
const errOutputIsInput = new Error('Cannot use same file for input and output');
if (is.fn(callback)) {
callback(errOutputIsInput);
} else {
return Promise.reject(errOutputIsInput);
}
} else {
this.options.fileOut = fileOut;
return this._pipeline(callback);
}
this.options.fileOut = fileOut;
return this._pipeline(callback);
}
return this;
}
@@ -188,7 +188,7 @@ function withMetadata (options) {
* @throws {Error} unsupported format or options
*/
function toFormat (format, options) {
const actualFormat = formats.get(is.object(format) && is.string(format.id) ? format.id : format);
const actualFormat = formats.get((is.object(format) && is.string(format.id) ? format.id : format).toLowerCase());
if (!actualFormat) {
throw is.invalidParameterError('format', `one of: ${[...formats.keys()].join(', ')}`, format);
}
@@ -426,7 +426,7 @@ function webp (options) {
/* istanbul ignore next */
function gif (options) {
if (!this.constructor.format.magick.output.buffer) {
throw new Error('The gif operation requires libvips to have been installed with support for ImageMagick');
throw errMagickSave;
}
trySetAnimationOptions(options, this.options);
return this._updateFormatOut('gif', options);
@@ -582,7 +582,7 @@ function tiff (options) {
* @param {Object} [options] - output options
* @param {number} [options.quality=50] - quality, integer 1-100
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {boolean} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
* @returns {Sharp}
* @throws {Error} Invalid options

View File

@@ -157,14 +157,10 @@ simd(true);
* @private
*/
module.exports = function (Sharp) {
[
cache,
concurrency,
counters,
simd
].forEach(function (f) {
Sharp[f.name] = f;
});
Sharp.cache = cache;
Sharp.concurrency = concurrency;
Sharp.counters = counters;
Sharp.simd = simd;
Sharp.format = format;
Sharp.interpolators = interpolators;
Sharp.versions = versions;

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images",
"version": "0.27.1",
"version": "0.27.2",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -80,7 +80,7 @@
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
"test": "semistandard && cpplint && npm run test-unit && npm run test-licensing",
"test-unit": "nyc --reporter=lcov --branches=99 mocha --parallel --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
"test-coverage": "./test/coverage/report.sh",
"test-leak": "./test/leak/leak.sh",
@@ -122,7 +122,7 @@
"detect-libc": "^1.0.3",
"node-addon-api": "^3.1.0",
"npmlog": "^4.1.2",
"prebuild-install": "^6.0.0",
"prebuild-install": "^6.0.1",
"semver": "^7.3.4",
"simple-get": "^4.0.0",
"tar-fs": "^2.1.1",
@@ -136,7 +136,7 @@
"exif-reader": "^1.0.3",
"icc": "^2.0.0",
"license-checker": "^25.0.1",
"mocha": "^8.2.1",
"mocha": "^8.3.0",
"mock-fs": "^4.13.0",
"nyc": "^15.1.0",
"prebuild": "^10.0.1",

View File

@@ -421,12 +421,7 @@ namespace sharp {
Uses colour space interpretation with number of channels to guess this.
*/
bool HasAlpha(VImage image) {
int const bands = image.bands();
VipsInterpretation const interpretation = image.interpretation();
return (
(bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) ||
(bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) ||
(bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK));
return image.has_alpha();
}
/*

View File

@@ -149,8 +149,8 @@ namespace sharp {
*/
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix) {
double *m = matrix.get();
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
return image
.colourspace(VIPS_INTERPRETATION_sRGB)
.recomb(image.bands() == 3
? VImage::new_from_memory(
m, 9 * sizeof(double), 3, 3, 1, VIPS_FORMAT_DOUBLE

View File

@@ -63,12 +63,17 @@ describe('GIF input', () => {
);
if (!sharp.format.magick.output.buffer) {
it('GIF output should fail due to missing ImageMagick', () => {
it('GIF buffer output should fail due to missing ImageMagick', () => {
assert.throws(
() => {
sharp().gif();
},
/The gif operation requires libvips to have been installed with support for ImageMagick/
() => sharp().gif(),
/GIF output requires libvips with support for ImageMagick/
);
});
it('GIF file output should fail due to missing ImageMagick', () => {
assert.rejects(
async () => await sharp().toFile('test.gif'),
/GIF output requires libvips with support for ImageMagick/
);
});
}

View File

@@ -5,15 +5,17 @@ const assert = require('assert');
const sharp = require('../../');
const fixtures = require('../fixtures');
const sepia = [
[0.3588, 0.7044, 0.1368],
[0.299, 0.587, 0.114],
[0.2392, 0.4696, 0.0912]
];
describe('Recomb', function () {
it('applies a sepia filter using recomb', function (done) {
const output = fixtures.path('output.recomb-sepia.jpg');
sharp(fixtures.inputJpgWithLandscapeExif1)
.recomb([
[0.3588, 0.7044, 0.1368],
[0.299, 0.587, 0.114],
[0.2392, 0.4696, 0.0912]
])
.recomb(sepia)
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
@@ -31,11 +33,7 @@ describe('Recomb', function () {
it('applies a sepia filter using recomb to an PNG with Alpha', function (done) {
const output = fixtures.path('output.recomb-sepia.png');
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
.recomb([
[0.3588, 0.7044, 0.1368],
[0.299, 0.587, 0.114],
[0.2392, 0.4696, 0.0912]
])
.recomb(sepia)
.toFile(output, function (err, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
@@ -50,6 +48,20 @@ describe('Recomb', function () {
});
});
it('recomb with a single channel input', async () => {
const { info } = await sharp(Buffer.alloc(64), {
raw: {
width: 8,
height: 8,
channels: 1
}
})
.recomb(sepia)
.toBuffer({ resolveWithObject: true });
assert.strictEqual(3, info.channels);
});
it('applies a different sepia filter using recomb', function (done) {
const output = fixtures.path('output.recomb-sepia2.jpg');
sharp(fixtures.inputJpgWithLandscapeExif1)

26
test/unit/toFormat.js Normal file
View File

@@ -0,0 +1,26 @@
'use strict';
const assert = require('assert');
const sharp = require('../../');
const fixtures = require('../fixtures');
describe('toFormat', () => {
it('accepts upper case characters as format parameter (string)', function (done) {
sharp(fixtures.inputJpg)
.toFormat('PNG')
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
done();
});
});
it('accepts upper case characters as format parameter (object)', function (done) {
sharp(fixtures.inputJpg)
.toFormat({ id: 'PNG' })
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
done();
});
});
});