Compare commits

...

18 Commits

Author SHA1 Message Date
Lovell Fuller
99bf279de8 Release v0.31.1 2022-09-29 14:51:45 +01:00
Lovell Fuller
1b0eb6ab53 Tests: add assertion to existing scenario #3357 2022-09-29 14:21:37 +01:00
Lovell Fuller
891cf67d0b Upgrade to libvips v8.13.2 2022-09-29 14:19:58 +01:00
Lovell Fuller
eaf8a86bf2 Tests: increase timeout to 20s
Ignore unit coverage of fn used at install time
2022-09-27 14:25:34 +01:00
Lovell Fuller
3400976d61 Tests: ignore a branch for coverage check 2022-09-27 14:10:49 +01:00
Lovell Fuller
2d49f0e93e Tests: require 100% branch coverage to pass
Remove old coverage tooling, coveralls
2022-09-27 13:49:42 +01:00
Lovell Fuller
b0c69f1ee9 CI: use clearer job names 2022-09-27 10:02:20 +01:00
Lovell Fuller
27d0c35a01 CI: use clearer job names 2022-09-27 09:23:30 +01:00
Alex
32a22b5420 CI: GitHub Workflows security hardening (#3377)
Signed-off-by: Alex <aleksandrosansan@gmail.com>
2022-09-26 11:25:49 +01:00
Lovell Fuller
d1004eed02 Ensure greyscale images can be trimmed #3386 2022-09-26 10:15:25 +01:00
Lovell Fuller
70e6bb0162 Ensure close event occurs after end event #3313 2022-09-20 08:52:40 +01:00
Lovell Fuller
32aa3b4b20 Tests: bump/pin benchmark dependencies 2022-09-19 16:21:24 +01:00
Lovell Fuller
df24b30755 Tests: add tfjs to benchmark tests 2022-09-19 16:20:23 +01:00
Lovell Fuller
4de74bea94 Tests: remove assertions from benchmark code 2022-09-19 16:09:31 +01:00
Lovell Fuller
28b87db760 Ensure AVIF output is always 8-bit #3358 2022-09-14 13:33:47 +01:00
Lovell Fuller
fbd4970b57 Ensure auto-rotation works with shrink-on-load #3352
Fixes regression in 0.31.0
2022-09-07 14:17:40 +01:00
Lovell Fuller
f5da147a58 Docs: changelog and credit for #3349 2022-09-07 13:31:26 +01:00
Marcos Casagrande
eee0dd36d9 Ensure limitInputPixels uses uint64 (#3349) 2022-09-06 09:05:51 +01:00
25 changed files with 246 additions and 141 deletions

View File

@@ -2,27 +2,31 @@ name: CI (MacStadium)
on:
- push
- pull_request
permissions: {}
jobs:
CI:
permissions:
contents: write # for npx prebuild to make release
name: Node.js ${{ matrix.nodejs_version }} ${{ matrix.nodejs_arch }} ${{ matrix.prebuild && '- prebuild' }}
runs-on: macos-m1
strategy:
fail-fast: false
matrix:
include:
- nodejs_version: 14
nodejs_architecture: x64
nodejs_arch: x64
- nodejs_version: 18
nodejs_architecture: arm64
nodejs_arch: arm64
prebuild: true
defaults:
run:
shell: /usr/bin/arch -arch arm64e /bin/bash -l {0}
steps:
- name: Dependencies
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.nodejs_version }}
architecture: ${{ matrix.nodejs_architecture }}
architecture: ${{ matrix.nodejs_arch }}
- name: Checkout
uses: actions/checkout@v2
- name: Install

View File

@@ -2,31 +2,34 @@ name: CI (GitHub)
on:
- push
- pull_request
permissions: {}
jobs:
CI:
permissions:
contents: write # for npx prebuild to make release
name: ${{ matrix.container || matrix.os }} - Node.js ${{ matrix.nodejs_version }} ${{ matrix.nodejs_arch }} ${{ matrix.prebuild && '- prebuild' }}
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
- os: ubuntu-22.04
container: centos:7
nodejs_version: 14
coverage: true
prebuild: true
- os: ubuntu-20.04
- os: ubuntu-22.04
container: centos:7
nodejs_version: 16
- os: ubuntu-20.04
- os: ubuntu-22.04
container: rockylinux:8
nodejs_version: 18
- os: ubuntu-20.04
- os: ubuntu-22.04
container: node:14-alpine3.12
prebuild: true
- os: ubuntu-20.04
- os: ubuntu-22.04
container: node:16-alpine3.12
- os: ubuntu-20.04
- os: ubuntu-22.04
container: node:18-alpine3.14
- os: macos-11
nodejs_version: 14
@@ -90,11 +93,6 @@ jobs:
run: npm install --build-from-source --unsafe-perm
- name: Test
run: npm test
- name: Coverage
if: matrix.coverage
uses: coverallsapp/github-action@v1.1.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Prebuild
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/')
env:

View File

@@ -98,7 +98,6 @@ readableStream
A [guide for contributors](https://github.com/lovell/sharp/blob/main/.github/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.svg?branch=main)](https://coveralls.io/r/lovell/sharp?branch=main)
[![Node-API v5](https://img.shields.io/badge/Node--API-v5-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
## Licensing

View File

@@ -2,7 +2,27 @@
## v0.31 - *eagle*
Requires libvips v8.13.1
Requires libvips v8.13.2
### v0.31.1 - 29th September 2022
* Upgrade to libvips v8.13.2 for upstream bug fixes.
* Ensure `close` event occurs after `end` event for Stream-based output.
[#3313](https://github.com/lovell/sharp/issues/3313)
* Ensure `limitInputPixels` constructor option uses uint64.
[#3349](https://github.com/lovell/sharp/pull/3349)
[@marcosc90](https://github.com/marcosc90)
* Ensure auto-rotation works with shrink-on-load and extract (regression in 0.31.0).
[#3352](https://github.com/lovell/sharp/issues/3352)
* Ensure AVIF output is always 8-bit.
[#3358](https://github.com/lovell/sharp/issues/3358)
* Ensure greyscale images can be trimmed (regression in 0.31.0).
[#3386](https://github.com/lovell/sharp/issues/3386)
### v0.31.0 - 5th September 2022

View File

@@ -260,3 +260,6 @@ GitHub: https://github.com/brahima
Name: Anton Marsden
GitHub: https://github.com/antonmarsden
Name: Marcos Casagrande
GitHub: https://github.com/marcosc90

View File

@@ -89,6 +89,7 @@ const removeVendoredLibvips = function () {
rm(vendorPath, { recursive: true, maxRetries: 3, force: true });
};
/* istanbul ignore next */
const pkgConfigPath = function () {
if (process.platform !== 'win32') {
const brewPkgConfigPath = spawnSync(

View File

@@ -1205,7 +1205,7 @@ function _pipeline (callback) {
this.push(data);
}
this.push(null);
this.emit('close');
this.on('end', () => this.emit('close'));
});
});
if (this.streamInFinished) {
@@ -1221,7 +1221,7 @@ function _pipeline (callback) {
this.push(data);
}
this.push(null);
this.emit('close');
this.on('end', () => this.emit('close'));
});
}
return this;

View File

@@ -428,7 +428,7 @@ function extend (extend) {
* @throws {Error} Invalid parameters
*/
function extract (options) {
const suffix = isResizeExpected(this.options) || isRotationExpected(this.options) ? 'Post' : 'Pre';
const suffix = isResizeExpected(this.options) || this.options.widthPre !== -1 ? 'Post' : 'Pre';
if (this.options[`width${suffix}`] !== -1) {
this.options.debuglog('ignoring previous extract options');
}
@@ -511,15 +511,12 @@ function trim (trim) {
}
} else if (is.object(trim)) {
this._setBackgroundColourOption('trimBackground', trim.background);
if (!is.defined(trim.threshold)) {
this.options.trimThreshold = 10;
} else if (is.number(trim.threshold)) {
if (trim.threshold >= 0) {
this.options.trimThreshold = trim.threshold;
} else {
throw is.invalidParameterError('threshold', 'positive number', trim);
}
} else if (is.number(trim.threshold) && trim.threshold >= 0) {
this.options.trimThreshold = trim.threshold;
} else {
throw is.invalidParameterError('threshold', 'positive number', trim);
}
} else {
throw is.invalidParameterError('trim', 'string, number or object', trim);

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.31.0",
"version": "0.31.1",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -92,9 +92,8 @@
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
"test": "npm run test-lint && npm run test-unit && npm run test-licensing",
"test-lint": "semistandard && cpplint",
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=1000 --timeout=60000 ./test/unit/*.js",
"test-unit": "nyc --reporter=lcov --reporter=text --check-coverage --branches=100 mocha --slow=1000 --timeout=20000 ./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",
"docs-build": "documentation lint lib && node docs/build && node docs/search-index/build",
"docs-serve": "cd docs && npx serve",
@@ -156,19 +155,19 @@
},
"license": "Apache-2.0",
"config": {
"libvips": "8.13.1",
"libvips": "8.13.2",
"integrity": {
"darwin-arm64v8": "sha512-JdpGTx67RDbvRkg3ljFvTzqoq+oBXmMdDFEp0expDYXmP5HLH+GCkikmsROlGltgfKE2KqL/qwpxTEhIwMK/3A==",
"darwin-x64": "sha512-0Oh4/hEDnzV+X8MiiyUQ4G/Zh/MHw9rKstfuX0P1czgaxS2hX8Pxdbzdk1oqwTOEYVEGO/hMm9ItCVZ3RVPPaA==",
"linux-arm64v8": "sha512-9pSlPzEojt6ue5vXfASNMhQO1YS1p4i4Wydu+bzOfMtIPSBRXbu/+y8WELbbo03Ts7pftm9KtrMHitCVdy5EXw==",
"linux-armv6": "sha512-sv2FqS/ggpQly7h5/+nh8txQDulolE5ptaE90PO7iwfTont8N42pudeqootWKsuf0fRmkW4M92184VfVVYCvGw==",
"linux-armv7": "sha512-LmQIB8FDfasK6BsFhnE7ZI3LMlxh/rF5tZRNQ/uoTbF2xrtWQqqgiZgCifJByiEM+1tR7RxwNdnjxZhWvM9WmQ==",
"linux-x64": "sha512-JBRf8WBnlVw/K1jpSvmeZpnGZGjeqhG2NDEiQV/hUze3zgDGwDza4oiworaQExQmKcDrc2LJKF14Nsz1qQSNJw==",
"linuxmusl-arm64v8": "sha512-yzUQO5isDwsRpEUxbMXBeWp0sKhWghebrSK46SUF5mvB/kq6hZ7JbRuJ2aZjE84K/HUTyuCc0kE+M3m8naOs+g==",
"linuxmusl-x64": "sha512-H3Vz1QaaZ6X5iEbfPST7TPFwDO01tI8dk1osLm6l4a17BWCaOMaBQlqxgTgYrtd09JJ9CvGoq5fo5j5TPxUc4Q==",
"win32-arm64v8": "sha512-b5Ver+uwOJhdOGqvZVM+qF2KLKcowcac/wKK5Fg0czqlSMqP/KxDF2kxw2eKXUJNgfqe4eDH1QG/yTg2pQSetQ==",
"win32-ia32": "sha512-h/SJ/Yfn0ce9H70vt1wS8rZ4PfHnguCCTsOGik7e6O/e2AlBQOM0mKsPIB9jSOquoCP8rP0qF6AOPOjXKnCk+w==",
"win32-x64": "sha512-p9qpdWdhZooPteib92Kk+qF1vvzcScxvOwdIP8muhgo/A8uDI4/mqXCpEbMBw6vjETKlS3qo2JUbVF6+0/lyWQ=="
"darwin-arm64v8": "sha512-4tsE/HMQDT9srV/ovSJlr7IxKnhvH9qpArCAf5Xpb/uNcAiT7BcZ+HYwX2lbf3UY8REB1TR4ThEL/lmPnzMUHw==",
"darwin-x64": "sha512-D4ZSvlgLpf+KzKB2OD+K8NWl0JKzzIbvWwIjjwBycIHTMkaiams3Kp/AQ/bKudqof02Ks6LtP0X4XWvCaoRoUA==",
"linux-arm64v8": "sha512-9ZvUM2NBluhoeUz9X7/zJ48xJ5d7KzI1cO6lsiv4HKo5fOYw/vEY28XodFJzhyfu9NuKxh3Hs9FtoQGNvvAFkw==",
"linux-armv6": "sha512-vu0R8DF0k7KseU62fzrJadHNk5oeJriFLVn3KxCKEfV+Wkj7rX4lQhiPmOuD7/wRcUY+GGdoZ52vysDwMQhfzA==",
"linux-armv7": "sha512-UdfhJTjGFgrwc3Kaos5G1ZAK2+t/16Prtnl6FAT+m7cG5EXzYAqzgvk4qtakAH7UTnVe8MUgOfbTLt0YiRpfsg==",
"linux-x64": "sha512-sv92VpPyN+3oBv0vi4wDjx51demGdtyhEjd+vDfC3h8S/RSuIUE9Pt/+dBFuf+iv9tRdIq9hH9vzAvsLVy6NYg==",
"linuxmusl-arm64v8": "sha512-TjhK/wHAS/m55l46T8PZ0qvlK+PKYFZGTQfh+c9aG8/z1v/VtG7TQOLNmPWfg0SFDTkXV7YqnJCqvgYLmJPZUg==",
"linuxmusl-x64": "sha512-/su96pn/H9+lDdnlM1xB2whWEoeEDJICFp/RNRJb0+bJPJhnL/IDVIhF4VnVNBq/9AlldBWii3hqMq5rY2eEAA==",
"win32-arm64v8": "sha512-UnSmwCcx3F5u4UOXyrdwTdYsuMK/RtQYc+1y+QxqIkBHiSL7dOlTIH/vKOSQvSaDQTPqxVLFt3wkMN1U7LZwyg==",
"win32-ia32": "sha512-KH/H6vpx5lJ6NEzLQmwxU/QnDg8p1Jxd+WKaPiyWmXq/HpwyKrZhi3WDoyKD4fLwnlfhAXEfVLZbUbhX21pDpQ==",
"win32-x64": "sha512-Xim5F21pqx7MuVQViaQNhSz24zWIiKHC9bm4KCdi7q/ytbvdMhm6bzWDI/mvFGNjI62NRB2SBkTTaqwJvM/pUg=="
},
"runtime": "napi",
"target": 7

View File

@@ -507,9 +507,10 @@ namespace sharp {
}
}
}
// Limit input images to a given number of pixels, where pixels = width * height
if (descriptor->limitInputPixels > 0 &&
static_cast<uint64_t>(image.width() * image.height()) > descriptor->limitInputPixels) {
static_cast<uint64_t>(image.width()) * image.height() > descriptor->limitInputPixels) {
throw vips::VError("Input image exceeds pixel limit");
}
return std::make_tuple(image, imageType);
@@ -556,6 +557,7 @@ namespace sharp {
VImage RemoveExifOrientation(VImage image) {
VImage copy = image.copy();
copy.remove(VIPS_META_ORIENTATION);
copy.remove("exif-ifd0-Orientation");
return copy;
}

View File

@@ -26,8 +26,8 @@
#if (VIPS_MAJOR_VERSION < 8) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 13) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 13 && VIPS_MICRO_VERSION < 1)
#error "libvips version 8.13.1+ is required - please see https://sharp.pixelplumbing.com/install"
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 13 && VIPS_MICRO_VERSION < 2)
#error "libvips version 8.13.2+ is required - please see https://sharp.pixelplumbing.com/install"
#endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))

View File

@@ -289,17 +289,20 @@ namespace sharp {
background = image.extract_area(0, 0, 1, 1)(0, 0);
multiplier = 1.0;
}
if (background.size() == 4) {
if (HasAlpha(image) && background.size() == 4) {
// Just discard the alpha because flattening the background colour with
// itself (effectively what find_trim() does) gives the same result
backgroundAlpha[0] = background[3] * multiplier;
}
background = {
background[0] * multiplier,
background[1] * multiplier,
background[2] * multiplier
};
if (image.bands() > 2) {
background = {
background[0] * multiplier,
background[1] * multiplier,
background[2] * multiplier
};
} else {
background[0] = background[0] * multiplier;
}
int left, top, width, height;
left = image.find_trim(&top, &width, &height, VImage::option()
->set("background", background)

View File

@@ -92,7 +92,10 @@ class PipelineWorker : public Napi::AsyncWorker {
}
// Rotate pre-extract
if (baton->rotateBeforePreExtract) {
bool const shouldRotateBefore = baton->rotateBeforePreExtract &&
(rotation != VIPS_ANGLE_D0 || flip || flop || baton->rotationAngle != 0.0);
if (shouldRotateBefore) {
if (rotation != VIPS_ANGLE_D0) {
image = image.rot(rotation);
}
@@ -167,7 +170,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// - input colourspace is not specified;
bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0 &&
baton->colourspaceInput == VIPS_INTERPRETATION_LAST;
baton->colourspaceInput == VIPS_INTERPRETATION_LAST && !shouldRotateBefore;
if (shouldPreShrink) {
// The common part of the shrink: the bit by which both axes must be shrunk
@@ -899,12 +902,13 @@ class PipelineWorker : public Napi::AsyncWorker {
} else if (baton->formatOut == "heif" ||
(baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
// Write HEIF to buffer
image = sharp::RemoveAnimationProperties(image);
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
->set("strip", !baton->withMetadata)
->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression)
->set("effort", baton->heifEffort)
->set("bitdepth", 8)
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
->set("lossless", baton->heifLossless)));
@@ -1070,12 +1074,13 @@ class PipelineWorker : public Napi::AsyncWorker {
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
// Write HEIF to file
image = sharp::RemoveAnimationProperties(image);
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("strip", !baton->withMetadata)
->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression)
->set("effort", baton->heifEffort)
->set("bitdepth", 8)
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
->set("lossless", baton->heifLossless));

View File

@@ -10,11 +10,12 @@
"devDependencies": {
"@squoosh/cli": "0.7.2",
"@squoosh/lib": "0.4.0",
"@tensorflow/tfjs-node": "3.20.0",
"async": "3.2.4",
"benchmark": "2.1.4",
"gm": "1.23.1",
"gm": "1.24.0",
"imagemagick": "0.1.3",
"jimp": "0.16.1",
"jimp": "0.16.2",
"mapnik": "4.5.9",
"semver": "7.3.7"
},

View File

@@ -5,7 +5,6 @@ const fs = require('fs');
const { exec } = require('child_process');
const async = require('async');
const assert = require('assert');
const Benchmark = require('benchmark');
// Contenders
@@ -15,6 +14,8 @@ const imagemagick = require('imagemagick');
const mapnik = require('mapnik');
const jimp = require('jimp');
const squoosh = require('@squoosh/lib');
process.env.TF_CPP_MIN_LOG_LEVEL = 1;
const tfjs = require('@tensorflow/tfjs-node');
const fixtures = require('../fixtures');
@@ -212,11 +213,10 @@ async.series({
.filter('Lanczos')
.resize(width, height)
.quality(80)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -243,16 +243,33 @@ async.series({
.filter('Lanczos')
.resize(width, height)
.quality(80)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
// tfjs
jpegSuite.add('tfjs-node-buffer-buffer', {
defer: true,
fn: function (deferred) {
const decoded = tfjs.node.decodeJpeg(inputJpgBuffer);
const resized = tfjs.image.resizeBilinear(decoded, [height, width]);
tfjs
.node
.encodeJpeg(resized, 'rgb', 80)
.then(function () {
deferred.resolve();
tfjs.disposeVariables();
})
.catch(function (err) {
throw err;
});
}
});
// sharp
jpegSuite.add('sharp-buffer-file', {
defer: true,
@@ -272,11 +289,10 @@ async.series({
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -311,11 +327,10 @@ async.series({
fn: function (deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -326,8 +341,7 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.toBuffer()
.then(function (buffer) {
assert.notStrictEqual(null, buffer);
.then(function () {
deferred.resolve();
})
.catch(function (err) {
@@ -350,11 +364,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.sharpen()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -365,11 +378,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.sharpen(3, 1, 3)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -380,11 +392,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.blur()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -395,11 +406,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.blur(3)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -410,11 +420,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.gamma()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -425,11 +434,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.normalise()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -440,11 +448,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.greyscale()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -456,11 +463,10 @@ async.series({
.resize(width, height)
.gamma()
.greyscale()
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -471,11 +477,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.jpeg({ progressive: true })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -486,11 +491,10 @@ async.series({
sharp(inputJpgBuffer)
.resize(width, height)
.jpeg({ chromaSubsampling: '4:4:4' })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -501,11 +505,10 @@ async.series({
sharp(inputJpgBuffer)
.rotate(90)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -516,12 +519,11 @@ async.series({
sharp.simd(false);
sharp(inputJpgBuffer)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
sharp.simd(true);
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -531,11 +533,10 @@ async.series({
fn: function (deferred) {
sharp(inputJpgBuffer, { sequentialRead: true })
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -548,11 +549,10 @@ async.series({
fit: 'cover',
position: sharp.strategy.entropy
})
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -565,11 +565,10 @@ async.series({
fit: 'cover',
position: sharp.strategy.attention
})
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -588,11 +587,10 @@ async.series({
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'cubic' })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -602,11 +600,10 @@ async.series({
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'lanczos2' })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -616,11 +613,10 @@ async.series({
fn: function (deferred) {
sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'lanczos3' })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -774,11 +770,10 @@ async.series({
.resize(width, height)
.define('PNG:compression-level=6')
.define('PNG:compression-filter=0')
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -807,11 +802,10 @@ async.series({
sharp(inputPngBuffer)
.resize(width, height)
.png({ compressionLevel: 6 })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -838,11 +832,10 @@ async.series({
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
.resize(width, height)
.png({ compressionLevel: 6 })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -854,11 +847,10 @@ async.series({
sharp(inputPngBuffer)
.resize(width, height)
.png({ compressionLevel: 6, progressive: true })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -870,11 +862,10 @@ async.series({
sharp(inputPngBuffer)
.resize(width, height)
.png({ adaptiveFiltering: true, compressionLevel: 6 })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -886,11 +877,10 @@ async.series({
sharp(inputPngBuffer)
.resize(width, height)
.png({ compressionLevel: 9 })
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -923,11 +913,10 @@ async.series({
fn: function (deferred) {
sharp(inputWebPBuffer)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -950,11 +939,10 @@ async.series({
fn: function (deferred) {
sharp(fixtures.inputWebP)
.resize(width, height)
.toBuffer(function (err, buffer) {
.toBuffer(function (err) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
@@ -966,7 +954,9 @@ async.series({
}).run();
}
}, function (err, results) {
assert(!err, err);
if (err) {
throw err;
}
Object.keys(results).forEach(function (format) {
if (results[format].toString().substr(0, 5) !== 'sharp') {
console.log('sharp was slower than ' + results[format] + ' for ' + format);

View File

@@ -1,6 +0,0 @@
#!/bin/sh
CPPFLAGS="--coverage" LDFLAGS="--coverage" npm rebuild
npm test
geninfo --no-external --base-directory src --output-file coverage/sharp.info build/Release/obj.target/sharp/src
genhtml --title sharp --demangle-cpp --output-directory coverage/sharp coverage/*.info

BIN
test/fixtures/65536-uint32-limit.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

View File

@@ -98,6 +98,7 @@ module.exports = {
inputPngTrimSpecificColour: getPath('Flag_of_the_Netherlands.png'), // https://commons.wikimedia.org/wiki/File:Flag_of_the_Netherlands.svg
inputPngTrimSpecificColour16bit: getPath('Flag_of_the_Netherlands-16bit.png'), // convert Flag_of_the_Netherlands.png -depth 16 Flag_of_the_Netherlands-16bit.png
inputPngTrimSpecificColourIncludeAlpha: getPath('Flag_of_the_Netherlands-alpha.png'), // convert Flag_of_the_Netherlands.png -alpha set -background none -channel A -evaluate multiply 0.5 +channel Flag_of_the_Netherlands-alpha.png
inputPngUint32Limit: getPath('65536-uint32-limit.png'), // https://alexandre.alapetite.fr/doc-alex/large-image/
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp

View File

@@ -103,4 +103,28 @@ describe('AVIF', () => {
width: 10
});
});
it('should cast to uchar', async () => {
const data = await sharp(inputJpg)
.resize(32)
.sharpen()
.avif({ effort: 0 })
.toBuffer();
const { size, ...metadata } = await sharp(data)
.metadata();
assert.deepStrictEqual(metadata, {
channels: 3,
compression: 'av1',
depth: 'uchar',
format: 'heif',
hasAlpha: false,
hasProfile: false,
height: 26,
isProgressive: false,
pagePrimary: 0,
pages: 1,
space: 'srgb',
width: 32
});
});
});

View File

@@ -300,7 +300,7 @@ describe('Partial image extraction', function () {
const s = sharp();
s.on('warning', function (msg) { warningMessage = msg; });
const options = { top: 0, left: 0, width: 1, height: 1 };
s.extract(options);
s.extract(options).extract(options);
assert.strictEqual(warningMessage, '');
s.extract(options);
assert.strictEqual(warningMessage, 'ignoring previous extract options');
@@ -311,7 +311,7 @@ describe('Partial image extraction', function () {
const s = sharp().rotate();
s.on('warning', function (msg) { warningMessage = msg; });
const options = { top: 0, left: 0, width: 1, height: 1 };
s.extract(options);
s.extract(options).extract(options);
assert.strictEqual(warningMessage, '');
s.extract(options);
assert.strictEqual(warningMessage, 'ignoring previous extract options');

View File

@@ -745,6 +745,17 @@ describe('Input/output', function () {
})
);
it('Enabling default limit works and fails for an image with resolution higher than uint32 limit', () =>
sharp(fixtures.inputPngUint32Limit, { limitInputPixels: true })
.toBuffer()
.then(() => {
assert.fail('Expected to fail');
})
.catch(err => {
assert.strictEqual(err.message, 'Input image exceeds pixel limit');
})
);
it('Smaller than input fails', () =>
sharp(fixtures.inputJpg)
.metadata()

View File

@@ -128,11 +128,25 @@ describe('PNG', function () {
assert.strictEqual(alphaMeanAfter, alphaMeanBefore);
});
it('palette decode/encode roundtrip', () =>
sharp(fixtures.inputPngPalette)
it('palette decode/encode roundtrip', async () => {
const data = await sharp(fixtures.inputPngPalette)
.png({ effort: 1, palette: true })
.toBuffer()
);
.toBuffer();
const { size, ...metadata } = await sharp(data).metadata();
assert.deepStrictEqual(metadata, {
format: 'png',
width: 68,
height: 68,
space: 'srgb',
channels: 3,
depth: 'uchar',
isProgressive: false,
paletteBitDepth: 8,
hasProfile: false,
hasAlpha: false
});
});
it('Valid PNG libimagequant palette value does not throw error', function () {
assert.doesNotThrow(function () {

View File

@@ -406,4 +406,16 @@ describe('Rotation', function () {
fixtures.assertSimilar(fixtures.expected('rotate-and-flop.jpg'), data, done);
});
});
it('Auto-rotate and shrink-on-load', async () => {
const [r, g, b] = await sharp(fixtures.inputJpgWithLandscapeExif3)
.rotate()
.resize(8)
.raw()
.toBuffer();
assert.strictEqual(r, 60);
assert.strictEqual(g, 73);
assert.strictEqual(b, 52);
});
});

View File

@@ -175,12 +175,13 @@ describe('Text to image', () => {
});
it('fontfile input', function () {
// Added for code coverage
sharp({
text: {
text: 'text',
fontfile: 'UnknownFont.ttf'
}
assert.doesNotThrow(function () {
sharp({
text: {
text: 'text',
fontfile: 'UnknownFont.ttf'
}
});
});
});

View File

@@ -124,6 +124,32 @@ describe('Trim borders', function () {
assert.strictEqual(trimOffsetLeft, -13);
});
it('Ensure greyscale image can be trimmed', async () => {
const greyscale = await sharp({
create: {
width: 16,
height: 8,
channels: 3,
background: 'silver'
}
})
.extend({ left: 12, right: 24, background: 'gray' })
.toColourspace('b-w')
.png({ compressionLevel: 0 })
.toBuffer();
const { info } = await sharp(greyscale)
.trim()
.raw()
.toBuffer({ resolveWithObject: true });
const { width, height, trimOffsetTop, trimOffsetLeft } = info;
assert.strictEqual(width, 16);
assert.strictEqual(height, 8);
assert.strictEqual(trimOffsetTop, 0);
assert.strictEqual(trimOffsetLeft, -12);
});
it('Ensure trim of image with all pixels same is no-op', async () => {
const { info } = await sharp({
create: {