Compare commits

..

26 Commits

Author SHA1 Message Date
Lovell Fuller
80d169b7c2 Release v0.30.2 2022-03-02 11:24:35 +00:00
Lovell Fuller
003279a0b0 CI: switch 32-bit Windows from Appveyor to Actions 2022-03-02 11:16:21 +00:00
Lovell Fuller
af80d7e389 Improve error message for missing file that might be SVG 2022-03-02 09:58:55 +00:00
Lovell Fuller
21a960796c Ignore greyscale ICC profiles due to lcms bug #3112 2022-02-28 11:28:08 +00:00
Lovell Fuller
fc3b4a683d Expand pkgconfig search path for wider BSD support #3106 2022-02-27 09:39:21 +00:00
Lovell Fuller
808133e7dc Docs: changelog entry for #3110 2022-02-26 19:40:17 +00:00
Lovell Fuller
801b6fea6c Bump devDeps 2022-02-26 19:39:46 +00:00
Kleis Auke Wolthuizen
c2ecde6a16 Windows: ensure C++ runtime is linked statically (#3110)
And remove the empty invalid parameter handler, which should
be present in the C layer instead.

This partially reverts commit
659cdabd8e,
the added test case in that commit is still preserved.
2022-02-26 19:15:37 +00:00
Lovell Fuller
55efe5602b Bump deps 2022-02-16 19:12:27 +00:00
Lovell Fuller
c62002554b Improve performance and accuracy of multi-image composite #2286 2022-02-16 19:04:23 +00:00
Lovell Fuller
7f83ecd255 Issue templates: small formatting fixes 2022-02-15 10:54:36 +00:00
Lovell Fuller
dc5f4dcd28 Issue templates: improve guidance, increase filtering 2022-02-15 10:50:26 +00:00
Lovell Fuller
735793ba99 Release v0.30.1 2022-02-09 16:38:21 +00:00
Lovell Fuller
47792df689 Docs: update interpolator links 2022-02-09 15:45:05 +00:00
Lovell Fuller
5c6cdfaece Docs: changelog entry for #3083 2022-02-09 15:27:52 +00:00
Kleis Auke Wolthuizen
115a6b10f6 Ensure affineInterpolator is correctly finalised (#3083) 2022-02-09 14:45:11 +00:00
Lovell Fuller
4feee506cf Docs: changelog entry for #3081 2022-02-08 22:08:07 +00:00
Kleis Auke Wolthuizen
83db5f8a2a Ensure withoutReduction does not interfere with contain/crop/embed (#3081) 2022-02-08 21:22:23 +00:00
Kleis Auke Wolthuizen
7eb5efa3a3 Update tests and comments after 7faacd9 (#3080) 2022-02-08 21:12:38 +00:00
Lovell Fuller
5a9f89fe06 Docs: update references to default branch 2022-02-08 21:01:40 +00:00
Lovell Fuller
02e0c2dfc9 Tests: arm64 requires slight threshold increase 2022-02-08 21:00:30 +00:00
Lovell Fuller
968d9d7008 Bump devDeps 2022-02-08 18:29:25 +00:00
Kleis Auke Wolthuizen
7faacd91b0 Avoid fastShrinkOnLoad workaround 2022-02-08 18:20:25 +00:00
Lovell Fuller
154eaff4ec Issue template: include npm v8+ 2022-02-08 18:20:11 +00:00
Lovell Fuller
424660278d Allow use of toBuffer+toFile w/ same instance #3044 2022-02-03 22:01:46 +00:00
Lovell Fuller
2b01951306 Skip JPEG shrink-on-load for known rounding errors #3066
Co-authored-by: Kleis Auke Wolthuizen <github@kleisauke.nl>
2022-02-03 21:19:48 +00:00
47 changed files with 297 additions and 211 deletions

View File

@@ -23,22 +23,22 @@ the feature you need.
Thank you! To prevent the problem occurring again, please add unit tests that would have failed. Thank you! To prevent the problem occurring again, please add unit tests that would have failed.
Please select the `master` branch as the destination for your Pull Request so your fix can be included in the next minor release. Please select the `main` branch as the destination for your Pull Request so your fix can be included in the next minor release.
Please squash your changes into a single commit using a command like `git rebase -i upstream/master`. Please squash your changes into a single commit using a command like `git rebase -i upstream/main`.
To test C++ changes, you can compile the module using `npm install --build-from-source` 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 ## Submit a Pull Request with a new feature
Please add JavaScript [unit tests](https://github.com/lovell/sharp/tree/master/test/unit) to cover your new feature. Please add JavaScript [unit tests](https://github.com/lovell/sharp/tree/main/test/unit) to cover your new feature.
A test coverage report for the JavaScript code is generated in the `coverage/lcov-report` directory. A test coverage report for the JavaScript code is generated in the `coverage/lcov-report` directory.
Where possible, the functional tests use gradient-based perceptual hashes Where possible, the functional tests use gradient-based perceptual hashes
based on [dHash](http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html) based on [dHash](http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html)
to compare expected vs actual images. to compare expected vs actual images.
You deserve to add your details to the [list of contributors](https://github.com/lovell/sharp/blob/master/package.json#L5). You deserve to add your details to the [list of contributors](https://github.com/lovell/sharp/blob/main/package.json#L5).
Any change that modifies the existing public API should be added to the relevant work-in-progress branch for inclusion in the next major release. Any change that modifies the existing public API should be added to the relevant work-in-progress branch for inclusion in the next major release.
@@ -93,5 +93,5 @@ Please feel free to ask any questions via a
[new issue](https://github.com/lovell/sharp/issues/new). [new issue](https://github.com/lovell/sharp/issues/new).
If you're unable to post details publicly, please If you're unable to post details publicly, please
[e-mail](https://github.com/lovell/sharp/blob/master/package.json#L5) [e-mail](https://github.com/lovell/sharp/blob/main/package.json#L5)
for private, paid consulting. for private, paid consulting.

View File

@@ -5,12 +5,24 @@ labels: enhancement
--- ---
What are you trying to achieve? ## Feature request
Have you searched for similar feature requests? ### What are you trying to achieve?
What would you expect the API to look like? <!-- Please provide context here. -->
What alternatives have you considered? ### When you searched for similar feature requests, what did you find that might be related?
Is there a sample image that helps explain? <!-- Please demonstrate your research here. -->
### What would you expect the API to look like?
<!-- Please provide your suggestions here. -->
### What alternatives have you considered?
<!-- Please provide your ideas here. -->
### Please provide sample image(s) that help explain this feature
<!-- Please provide links to one or more images here. -->

View File

@@ -5,18 +5,41 @@ labels: installation
--- ---
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/install)? <!-- Please try to answer as many of these questions as possible. -->
Have you ensured the architecture and platform of Node.js used for `npm install` is the same as the architecture and platform of Node.js used at runtime? ## Possible install-time or require-time problem
Are you using the latest version? Is the version currently in use as reported by `npm ls sharp` the same as the latest version as reported by `npm view sharp dist-tags.latest`? <!-- Please place an [x] in the box to confirm. -->
- [ ] I have read the [documentation relating to installation](https://sharp.pixelplumbing.com/install).
- [ ] I have ensured that the architecture and platform of Node.js used for `npm install` is the same as the architecture and platform of Node.js used at runtime.
### Are you using the latest version of sharp?
<!-- Please place an [x] in the box to confirm. -->
- [ ] I am using the latest version of `sharp` as reported by `npm view sharp dist-tags.latest`.
If you cannot confirm this, please upgrade to the latest version and try again before opening an issue.
If you are using another package which depends on a version of `sharp` that is not the latest, please open an issue against that package instead.
### Is this a problem with filesystem permissions?
If you are using npm v6 or earlier and installing as a `root` or `sudo` user, have you tried with the `npm install --unsafe-perm` flag? If you are using npm v6 or earlier and installing as a `root` or `sudo` user, have you tried with the `npm install --unsafe-perm` flag?
If you are using npm v7, does the user running `npm install` own the directory it is run in? If you are using npm v7 or later, does the user running `npm install` own the directory it is run in?
If you are using the `ignore-scripts` feature of `npm`, have you tried with the `npm install --ignore-scripts=false` flag? If you are using the `ignore-scripts` feature of `npm`, have you tried with the `npm install --ignore-scripts=false` flag?
What is the complete output of running `npm install --verbose sharp`? Have you checked this output for useful error messages? ### What is the complete output of running `npm install --verbose sharp`?
What is the output of running `npx envinfo --binaries --system`? <details>
<!-- Please provide output of `npm install --verbose sharp` here. -->
</details>
### What is the output of running `npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp`?
<!-- Please provide output of `npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp` here. -->

View File

@@ -7,14 +7,43 @@ labels: triage
<!-- If this issue relates to installation, please use https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md instead. --> <!-- If this issue relates to installation, please use https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md instead. -->
Are you using the latest version? Is the version currently in use as reported by `npm ls sharp` the same as the latest version as reported by `npm view sharp dist-tags.latest`? ## Possible bug
What are the steps to reproduce? ### Is this a possible bug in a feature of sharp, unrelated to installation?
What is the expected behaviour? <!-- Please place an [x] in the box to confirm. -->
Are you able to provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem? - [ ] Running `npm install sharp` completes without error.
- [ ] Running `node -e "require('sharp')"` completes without error.
Are you able to provide a sample image that helps explain the problem? If you cannot confirm both of these, please open an [installation issue](https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md) instead.
What is the output of running `npx envinfo --binaries --system`? ### Are you using the latest version of sharp?
<!-- Please place an [x] in the box to confirm. -->
- [ ] I am using the latest version of `sharp` as reported by `npm view sharp dist-tags.latest`.
If you cannot confirm this, please upgrade to the latest version and try again before opening an issue.
If you are using another package which depends on a version of `sharp` that is not the latest, please open an issue against that package instead.
### What is the output of running `npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp`?
<!-- Please provide output of the above command here. -->
### What are the steps to reproduce?
<!-- Please enter steps to reproduce here. -->
### What is the expected behaviour?
<!-- Please enter the expected behaviour here. -->
### Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem
<!-- Please provide either formatted code or a link to a repo/gist that allows someone else to reproduce here. -->
### Please provide sample image(s) that help explain this problem
<!-- Please provide links to one or more images here. -->

View File

@@ -7,10 +7,20 @@ labels: question
<!-- If this issue relates to installation, please use https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md instead. --> <!-- If this issue relates to installation, please use https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md instead. -->
What are you trying to achieve? ## Question about an existing feature
Have you searched for similar questions? ### What are you trying to achieve?
Are you able to provide a minimal, standalone code sample that demonstrates this question? <!-- Please provide context here. -->
Are you able to provide a sample image that helps explain the question? ### When you searched for similar issues, what did you find that might be related?
<!-- Please demonstrate your research here. -->
### Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this question
<!-- Please provide either formatted code or a link to a repo/gist that helps someone else understand here. -->
### Please provide sample image(s) that help explain this question
<!-- Please provide links to one or more images here. -->

View File

@@ -33,17 +33,33 @@ jobs:
- os: macos-10.15 - os: macos-10.15
nodejs_version: 12 nodejs_version: 12
prebuild: true prebuild: true
nodejs_arch: x64
- os: macos-10.15 - os: macos-10.15
nodejs_version: 14 nodejs_version: 14
nodejs_arch: x64
- os: macos-10.15 - os: macos-10.15
nodejs_version: 16 nodejs_version: 16
nodejs_arch: x64
- os: windows-2019 - os: windows-2019
nodejs_version: 12 nodejs_version: 12
nodejs_arch: x86
prebuild: true prebuild: true
- os: windows-2019 - os: windows-2019
nodejs_version: 14 nodejs_version: 14
nodejs_arch: x86
- os: windows-2019 - os: windows-2019
nodejs_version: 16 nodejs_version: 16
nodejs_arch: x86
- os: windows-2019
nodejs_version: 12
nodejs_arch: x64
prebuild: true
- os: windows-2019
nodejs_version: 14
nodejs_arch: x64
- os: windows-2019
nodejs_version: 16
nodejs_arch: x64
steps: steps:
- name: Dependencies (Linux glibc) - name: Dependencies (Linux glibc)
if: contains(matrix.container, 'centos') if: contains(matrix.container, 'centos')
@@ -57,9 +73,10 @@ jobs:
run: apk add build-base git python3 --update-cache run: apk add build-base git python3 --update-cache
- name: Dependencies (macOS, Windows) - name: Dependencies (macOS, Windows)
if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows') if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows')
uses: actions/setup-node@v1 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.nodejs_version }} node-version: ${{ matrix.nodejs_version }}
architecture: ${{ matrix.nodejs_arch }}
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Fix working directory ownership - name: Fix working directory ownership

View File

@@ -1,6 +1,6 @@
# sharp # sharp
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right"> <img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
The typical use case for this high speed Node.js module The typical use case for this high speed Node.js module
is to convert large images in common formats to is to convert large images in common formats to
@@ -95,10 +95,10 @@ readableStream
## Contributing ## Contributing
A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md) A [guide for contributors](https://github.com/lovell/sharp/blob/main/.github/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes. covers reporting bugs, requesting features and submitting code changes.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.svg?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master) [![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) [![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 ## Licensing

View File

@@ -1,17 +0,0 @@
os: Visual Studio 2019
version: "{build}"
build: off
platform: x86
environment:
matrix:
- nodejs_version: "12"
prebuild: true
- nodejs_version: "14"
- nodejs_version: "16"
install:
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)
- npm install --build-from-source
test_script:
- npm test
on_success:
- if [%prebuild%] == [true] if [%APPVEYOR_REPO_TAG%] == [true] npx prebuild --runtime napi --target 5

View File

@@ -39,7 +39,6 @@
'VCCLCompilerTool': { 'VCCLCompilerTool': {
'ExceptionHandling': 1, 'ExceptionHandling': 1,
'Optimization': 1, 'Optimization': 1,
'RuntimeLibrary': '2', # /MD
'WholeProgramOptimization': 'true' 'WholeProgramOptimization': 'true'
}, },
'VCLibrarianTool': { 'VCLibrarianTool': {
@@ -206,7 +205,6 @@
'VCCLCompilerTool': { 'VCCLCompilerTool': {
'ExceptionHandling': 1, 'ExceptionHandling': 1,
'Optimization': 1, 'Optimization': 1,
'RuntimeLibrary': '2', # /MD
'WholeProgramOptimization': 'true' 'WholeProgramOptimization': 'true'
}, },
'VCLibrarianTool': { 'VCLibrarianTool': {

View File

@@ -1,6 +1,6 @@
# sharp # sharp
<img src="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right"> <img src="https://cdn.jsdelivr.net/gh/lovell/sharp@main/docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
The typical use case for this high speed Node.js module The typical use case for this high speed Node.js module
is to convert large images in common formats to is to convert large images in common formats to
@@ -65,7 +65,7 @@ as [pngcrush](https://pmt.sourceforge.io/pngcrush/).
### Contributing ### Contributing
A [guide for contributors](https://github.com/lovell/sharp/blob/master/.github/CONTRIBUTING.md) A [guide for contributors](https://github.com/lovell/sharp/blob/main/.github/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes. covers reporting bugs, requesting features and submitting code changes.
### Licensing ### Licensing

View File

@@ -188,13 +188,13 @@ Returns **[boolean][10]**
[5]: http://en.wikipedia.org/wiki/Bicubic_interpolation [5]: http://en.wikipedia.org/wiki/Bicubic_interpolation
[6]: https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100 [6]: https://github.com/libvips/libvips/blob/master/libvips/resample/lbb.cpp#L100
[7]: http://en.wikipedia.org/wiki/Acutance [7]: http://en.wikipedia.org/wiki/Acutance
[8]: http://eprints.soton.ac.uk/268086/ [8]: http://eprints.soton.ac.uk/268086/
[9]: https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48 [9]: https://github.com/libvips/libvips/blob/master/libvips/resample/vsqbs.cpp#L48
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean [10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean

View File

@@ -4,6 +4,38 @@
Requires libvips v8.12.2 Requires libvips v8.12.2
### v0.30.2 - 2nd March 2022
* Improve performance and accuracy when compositing multiple images.
[#2286](https://github.com/lovell/sharp/issues/2286)
* Expand pkgconfig search path for wider BSD support.
[#3106](https://github.com/lovell/sharp/issues/3106)
* Ensure Windows C++ runtime is linked statically (regression in 0.30.0).
[#3110](https://github.com/lovell/sharp/pull/3110)
[@kleisauke](https://github.com/kleisauke)
* Temporarily ignore greyscale ICC profiles to workaround lcms bug.
[#3112](https://github.com/lovell/sharp/issues/3112)
### v0.30.1 - 9th February 2022
* Allow use of `toBuffer` and `toFile` on the same instance.
[#3044](https://github.com/lovell/sharp/issues/3044)
* Skip shrink-on-load for known libjpeg rounding errors.
[#3066](https://github.com/lovell/sharp/issues/3066)
[@kleisauke](https://github.com/kleisauke)
* Ensure withoutReduction does not interfere with contain/crop/embed.
[#3081](https://github.com/lovell/sharp/pull/3081)
[@kleisauke](https://github.com/kleisauke)
* Ensure affine interpolator is correctly finalised.
[#3083](https://github.com/lovell/sharp/pull/3083)
[@kleisauke](https://github.com/kleisauke)
### v0.30.0 - 1st February 2022 ### v0.30.0 - 1st February 2022
* Add support for GIF output to prebuilt binaries. * Add support for GIF output to prebuilt binaries.

View File

@@ -162,7 +162,6 @@ function composite (images) {
throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied); throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
} }
} }
return composite; return composite;
}); });
return this; return this;

View File

@@ -86,9 +86,14 @@ const removeVendoredLibvips = function () {
const pkgConfigPath = function () { const pkgConfigPath = function () {
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || ''; const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
return [brewPkgConfigPath.trim(), env.PKG_CONFIG_PATH, '/usr/local/lib/pkgconfig', '/usr/lib/pkgconfig'] return [
.filter(function (p) { return !!p; }) brewPkgConfigPath.trim(),
.join(':'); env.PKG_CONFIG_PATH,
'/usr/local/lib/pkgconfig',
'/usr/lib/pkgconfig',
'/usr/local/libdata/pkgconfig',
'/usr/libdata/pkgconfig'
].filter(Boolean).join(':');
} else { } else {
return ''; return '';
} }

View File

@@ -139,6 +139,7 @@ function toBuffer (options, callback) {
} else if (this.options.resolveWithObject) { } else if (this.options.resolveWithObject) {
this.options.resolveWithObject = false; this.options.resolveWithObject = false;
} }
this.options.fileOut = '';
return this._pipeline(is.fn(options) ? options : callback); return this._pipeline(is.fn(options) ? options : callback);
} }

View File

@@ -30,11 +30,11 @@ const interpolators = {
bilinear: 'bilinear', bilinear: 'bilinear',
/** [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). */ /** [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). */
bicubic: 'bicubic', bicubic: 'bicubic',
/** [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100). Prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2. */ /** [LBB interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/lbb.cpp#L100). Prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2. */
locallyBoundedBicubic: 'lbb', locallyBoundedBicubic: 'lbb',
/** [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. */ /** [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. */
nohalo: 'nohalo', nohalo: 'nohalo',
/** [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */ /** [VSQBS interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */
vertexSplitQuadraticBasisSpline: 'vsqbs' vertexSplitQuadraticBasisSpline: 'vsqbs'
}; };

View File

@@ -1,7 +1,7 @@
{ {
"name": "sharp", "name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.30.0", "version": "0.30.2",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp", "homepage": "https://github.com/lovell/sharp",
"contributors": [ "contributors": [
@@ -126,8 +126,8 @@
"vips" "vips"
], ],
"dependencies": { "dependencies": {
"color": "^4.2.0", "color": "^4.2.1",
"detect-libc": "^2.0.0", "detect-libc": "^2.0.1",
"node-addon-api": "^4.3.0", "node-addon-api": "^4.3.0",
"prebuild-install": "^7.0.1", "prebuild-install": "^7.0.1",
"semver": "^7.3.5", "semver": "^7.3.5",
@@ -143,10 +143,10 @@
"exif-reader": "^1.0.3", "exif-reader": "^1.0.3",
"icc": "^2.0.0", "icc": "^2.0.0",
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
"mocha": "^9.2.0", "mocha": "^9.2.1",
"mock-fs": "^5.1.2", "mock-fs": "^5.1.2",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"prebuild": "^11.0.2", "prebuild": "^11.0.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"semistandard": "^16.0.1" "semistandard": "^16.0.1"
}, },

View File

@@ -398,6 +398,10 @@ namespace sharp {
// From filesystem // From filesystem
imageType = DetermineImageType(descriptor->file.data()); imageType = DetermineImageType(descriptor->file.data());
if (imageType == ImageType::MISSING) { if (imageType == ImageType::MISSING) {
if (descriptor->file.find("<svg") != std::string::npos) {
throw vips::VError("Input file is missing, did you mean "
"sharp(Buffer.from('" + descriptor->file.substr(0, 8) + "...')?");
}
throw vips::VError("Input file is missing"); throw vips::VError("Input file is missing");
} }
if (imageType != ImageType::UNKNOWN) { if (imageType != ImageType::UNKNOWN) {
@@ -885,7 +889,7 @@ namespace sharp {
} }
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight, std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
Canvas canvas, bool swap, bool withoutEnlargement) { Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction) {
if (swap) { if (swap) {
// Swap input width and height when requested. // Swap input width and height when requested.
std::swap(width, height); std::swap(width, height);
@@ -940,9 +944,14 @@ namespace sharp {
} }
} }
// We should not enlarge (oversample) the output image, // We should not reduce or enlarge the output image, if
// if withoutEnlargement is specified. // withoutReduction or withoutEnlargement is specified.
if (withoutEnlargement) { if (withoutReduction) {
// Equivalent of VIPS_SIZE_UP
hshrink = std::min(1.0, hshrink);
vshrink = std::min(1.0, vshrink);
} else if (withoutEnlargement) {
// Equivalent of VIPS_SIZE_DOWN
hshrink = std::max(1.0, hshrink); hshrink = std::max(1.0, hshrink);
vshrink = std::max(1.0, vshrink); vshrink = std::max(1.0, vshrink);
} }

View File

@@ -345,7 +345,7 @@ namespace sharp {
the required thumbnail width/height and canvas mode. the required thumbnail width/height and canvas mode.
*/ */
std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight, std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
Canvas canvas, bool swap, bool withoutEnlargement); Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction);
} // namespace sharp } // namespace sharp

View File

@@ -138,17 +138,6 @@ class PipelineWorker : public Napi::AsyncWorker {
pageHeight = inputHeight; pageHeight = inputHeight;
} }
// If withoutReduction is specified,
// Override target width and height if less than respective value from input file
if (baton->withoutReduction) {
if (baton->width < inputWidth) {
baton->width = inputWidth;
}
if (baton->height < inputHeight) {
baton->height = inputHeight;
}
}
// Scaling calculations // Scaling calculations
double hshrink; double hshrink;
double vshrink; double vshrink;
@@ -161,7 +150,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// Shrink to pageHeight, so we work for multi-page images // Shrink to pageHeight, so we work for multi-page images
std::tie(hshrink, vshrink) = sharp::ResolveShrink( std::tie(hshrink, vshrink) = sharp::ResolveShrink(
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight, inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
baton->canvas, swap, baton->withoutEnlargement); baton->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
// The jpeg preload shrink. // The jpeg preload shrink.
int jpegShrinkOnLoad = 1; int jpegShrinkOnLoad = 1;
@@ -184,7 +173,7 @@ class PipelineWorker : public Napi::AsyncWorker {
if (inputImageType == sharp::ImageType::JPEG) { if (inputImageType == sharp::ImageType::JPEG) {
// Leave at least a factor of two for the final resize step, when fastShrinkOnLoad: false // Leave at least a factor of two for the final resize step, when fastShrinkOnLoad: false
// for more consistent results and avoid occasional small image shifting // for more consistent results and to avoid extra sharpness to the image
int factor = baton->fastShrinkOnLoad ? 1 : 2; int factor = baton->fastShrinkOnLoad ? 1 : 2;
if (shrink >= 8 * factor) { if (shrink >= 8 * factor) {
jpegShrinkOnLoad = 8; jpegShrinkOnLoad = 8;
@@ -193,6 +182,10 @@ class PipelineWorker : public Napi::AsyncWorker {
} else if (shrink >= 2 * factor) { } else if (shrink >= 2 * factor) {
jpegShrinkOnLoad = 2; jpegShrinkOnLoad = 2;
} }
// Lower shrink-on-load for known libjpeg rounding errors
if (jpegShrinkOnLoad > 1 && static_cast<int>(shrink) == jpegShrinkOnLoad) {
jpegShrinkOnLoad /= 2;
}
} else if (inputImageType == sharp::ImageType::WEBP || } else if (inputImageType == sharp::ImageType::WEBP ||
inputImageType == sharp::ImageType::SVG || inputImageType == sharp::ImageType::SVG ||
inputImageType == sharp::ImageType::PDF) { inputImageType == sharp::ImageType::PDF) {
@@ -270,35 +263,28 @@ class PipelineWorker : public Napi::AsyncWorker {
} }
// Any pre-shrinking may already have been done // Any pre-shrinking may already have been done
int thumbWidth = image.width(); inputWidth = image.width();
int thumbHeight = image.height(); inputHeight = image.height();
// After pre-shrink, but before the main shrink stage // After pre-shrink, but before the main shrink stage
// Reuse the initial pageHeight if we didn't pre-shrink // Reuse the initial pageHeight if we didn't pre-shrink
int preshrunkPageHeight = shouldPreShrink ? sharp::GetPageHeight(image) : pageHeight; if (shouldPreShrink) {
pageHeight = sharp::GetPageHeight(image);
if (baton->fastShrinkOnLoad && jpegShrinkOnLoad > 1) {
// JPEG shrink-on-load rounds the output dimensions down, which
// may cause incorrect dimensions when fastShrinkOnLoad is enabled
// Just recalculate vshrink / hshrink on the main image instead of
// the pre-shrunk image when this is the case
hshrink = static_cast<double>(thumbWidth) / (static_cast<double>(inputWidth) / hshrink);
vshrink = static_cast<double>(preshrunkPageHeight) / (static_cast<double>(pageHeight) / vshrink);
} else {
// Shrink to preshrunkPageHeight, so we work for multi-page images
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
thumbWidth, preshrunkPageHeight, targetResizeWidth, targetResizeHeight,
baton->canvas, swap, baton->withoutEnlargement);
} }
int targetHeight = static_cast<int>(std::rint(static_cast<double>(preshrunkPageHeight) / vshrink)); // Shrink to pageHeight, so we work for multi-page images
std::tie(hshrink, vshrink) = sharp::ResolveShrink(
inputWidth, pageHeight, targetResizeWidth, targetResizeHeight,
baton->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
int targetHeight = static_cast<int>(std::rint(static_cast<double>(pageHeight) / vshrink));
int targetPageHeight = targetHeight; int targetPageHeight = targetHeight;
// In toilet-roll mode, we must adjust vshrink so that we exactly hit // In toilet-roll mode, we must adjust vshrink so that we exactly hit
// preshrunkPageHeight or we'll have pixels straddling pixel boundaries // pageHeight or we'll have pixels straddling pixel boundaries
if (thumbHeight > preshrunkPageHeight) { if (inputHeight > pageHeight) {
targetHeight *= nPages; targetHeight *= nPages;
vshrink = static_cast<double>(thumbHeight) / targetHeight; vshrink = static_cast<double>(inputHeight) / targetHeight;
} }
// Ensure we're using a device-independent colour space // Ensure we're using a device-independent colour space
@@ -306,7 +292,8 @@ class PipelineWorker : public Napi::AsyncWorker {
if ( if (
sharp::HasProfile(image) && sharp::HasProfile(image) &&
image.interpretation() != VIPS_INTERPRETATION_LABS && image.interpretation() != VIPS_INTERPRETATION_LABS &&
image.interpretation() != VIPS_INTERPRETATION_GREY16 image.interpretation() != VIPS_INTERPRETATION_GREY16 &&
image.interpretation() != VIPS_INTERPRETATION_B_W
) { ) {
// Convert to sRGB/P3 using embedded profile // Convert to sRGB/P3 using embedded profile
try { try {
@@ -532,12 +519,14 @@ class PipelineWorker : public Napi::AsyncWorker {
MultiPageUnsupported(nPages, "Affine"); MultiPageUnsupported(nPages, "Affine");
std::vector<double> background; std::vector<double> background;
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha); std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
vips::VInterpolate interp = vips::VInterpolate::new_from_name(
const_cast<char*>(baton->affineInterpolator.data()));
image = image.affine(baton->affineMatrix, VImage::option()->set("background", background) image = image.affine(baton->affineMatrix, VImage::option()->set("background", background)
->set("idx", baton->affineIdx) ->set("idx", baton->affineIdx)
->set("idy", baton->affineIdy) ->set("idy", baton->affineIdy)
->set("odx", baton->affineOdx) ->set("odx", baton->affineOdx)
->set("ody", baton->affineOdy) ->set("ody", baton->affineOdy)
->set("interpolate", baton->affineInterpolator)); ->set("interpolate", interp));
} }
// Extend edges // Extend edges
@@ -593,6 +582,8 @@ class PipelineWorker : public Napi::AsyncWorker {
// Composite // Composite
if (shouldComposite) { if (shouldComposite) {
std::vector<VImage> images = { image };
std::vector<int> modes, xs, ys;
for (Composite *composite : baton->composite) { for (Composite *composite : baton->composite) {
VImage compositeImage; VImage compositeImage;
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN; sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
@@ -638,12 +629,12 @@ class PipelineWorker : public Napi::AsyncWorker {
// gravity was used for extract_area, set it back to its default value of 0 // gravity was used for extract_area, set it back to its default value of 0
composite->gravity = 0; composite->gravity = 0;
} }
// Ensure image to composite is sRGB with premultiplied alpha // Ensure image to composite is sRGB with unpremultiplied alpha
compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB); compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
if (!sharp::HasAlpha(compositeImage)) { if (!sharp::HasAlpha(compositeImage)) {
compositeImage = sharp::EnsureAlpha(compositeImage, 1); compositeImage = sharp::EnsureAlpha(compositeImage, 1);
} }
if (!composite->premultiplied) compositeImage = compositeImage.premultiply(); if (composite->premultiplied) compositeImage = compositeImage.unpremultiply();
// Calculate position // Calculate position
int left; int left;
int top; int top;
@@ -661,12 +652,12 @@ class PipelineWorker : public Napi::AsyncWorker {
std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(), std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
compositeImage.width(), compositeImage.height(), composite->gravity); compositeImage.width(), compositeImage.height(), composite->gravity);
} }
// Composite images.push_back(compositeImage);
image = image.composite2(compositeImage, composite->mode, VImage::option() modes.push_back(composite->mode);
->set("premultiplied", TRUE) xs.push_back(left);
->set("x", left) ys.push_back(top);
->set("y", top));
} }
image = image.composite(images, modes, VImage::option()->set("x", xs)->set("y", ys));
} }
// Reverse premultiplication after all transformations: // Reverse premultiplication after all transformations:
@@ -1442,7 +1433,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->affineIdy = sharp::AttrAsDouble(options, "affineIdy"); baton->affineIdy = sharp::AttrAsDouble(options, "affineIdy");
baton->affineOdx = sharp::AttrAsDouble(options, "affineOdx"); baton->affineOdx = sharp::AttrAsDouble(options, "affineOdx");
baton->affineOdy = sharp::AttrAsDouble(options, "affineOdy"); baton->affineOdy = sharp::AttrAsDouble(options, "affineOdy");
baton->affineInterpolator = vips::VInterpolate::new_from_name(sharp::AttrAsStr(options, "affineInterpolator").data()); baton->affineInterpolator = sharp::AttrAsStr(options, "affineInterpolator");
baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha"); baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha"); baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
if (options.Has("boolean")) { if (options.Has("boolean")) {

View File

@@ -126,7 +126,7 @@ struct PipelineBaton {
double affineIdy; double affineIdy;
double affineOdx; double affineOdx;
double affineOdy; double affineOdy;
vips::VInterpolate affineInterpolator; std::string affineInterpolator;
int jpegQuality; int jpegQuality;
bool jpegProgressive; bool jpegProgressive;
std::string jpegChromaSubsampling; std::string jpegChromaSubsampling;
@@ -268,7 +268,7 @@ struct PipelineBaton {
affineIdy(0), affineIdy(0),
affineOdx(0), affineOdx(0),
affineOdy(0), affineOdy(0),
affineInterpolator(vips::VInterpolate::new_from_name("bicubic")), affineInterpolator("bicubic"),
jpegQuality(80), jpegQuality(80),
jpegProgressive(false), jpegProgressive(false),
jpegChromaSubsampling("4:2:0"), jpegChromaSubsampling("4:2:0"),

View File

@@ -13,7 +13,6 @@
// limitations under the License. // limitations under the License.
#include <napi.h> #include <napi.h>
#include <cstdlib>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
@@ -22,14 +21,6 @@
#include "utilities.h" #include "utilities.h"
#include "stats.h" #include "stats.h"
#if defined(_MSC_VER) && _MSC_VER >= 1400 // MSVC 2005/8
static void empty_invalid_parameter_handler(const wchar_t* expression,
const wchar_t* function, const wchar_t* file, unsigned int line,
uintptr_t reserved) {
// No-op.
}
#endif
static void* sharp_vips_init(void*) { static void* sharp_vips_init(void*) {
g_setenv("VIPS_MIN_STACK_SIZE", "2m", FALSE); g_setenv("VIPS_MIN_STACK_SIZE", "2m", FALSE);
vips_init("sharp"); vips_init("sharp");
@@ -43,13 +34,6 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING), g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr); static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
// Tell the CRT to not exit the application when an invalid parameter is
// passed. The main issue is that invalid FDs will trigger this behaviour.
// See: https://github.com/libvips/libvips/pull/2571.
#if defined(_MSC_VER) && _MSC_VER >= 1400 // MSVC 2005/8
_set_invalid_parameter_handler(empty_invalid_parameter_handler);
#endif
// Methods available to JavaScript // Methods available to JavaScript
exports.Set("metadata", Napi::Function::New(env, metadata)); exports.Set("metadata", Napi::Function::New(env, metadata));
exports.Set("pipeline", Napi::Function::New(env, pipeline)); exports.Set("pipeline", Napi::Function::New(env, pipeline));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 B

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 B

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 943 KiB

After

Width:  |  Height:  |  Size: 943 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -45,22 +45,20 @@ const blends = [
// Test // Test
describe('composite', () => { describe('composite', () => {
it('blend', () => Promise.all( blends.forEach(blend => {
blends.map(blend => { it(`blend ${blend}`, async () => {
const filename = `composite.blend.${blend}.png`; const filename = `composite.blend.${blend}.png`;
const actual = fixtures.path(`output.${filename}`); const actual = fixtures.path(`output.${filename}`);
const expected = fixtures.expected(filename); const expected = fixtures.expected(filename);
return sharp(redRect) await sharp(redRect)
.composite([{ .composite([{
input: blueRect, input: blueRect,
blend blend
}]) }])
.toFile(actual) .toFile(actual);
.then(() => { fixtures.assertMaxColourDistance(actual, expected);
fixtures.assertMaxColourDistance(actual, expected); });
}); });
})
));
it('premultiplied true', () => { it('premultiplied true', () => {
const filename = 'composite.premultiplied.png'; const filename = 'composite.premultiplied.png';
@@ -121,11 +119,11 @@ describe('composite', () => {
}); });
}); });
it('multiple', () => { it('multiple', async () => {
const filename = 'composite-multiple.png'; const filename = 'composite-multiple.png';
const actual = fixtures.path(`output.${filename}`); const actual = fixtures.path(`output.${filename}`);
const expected = fixtures.expected(filename); const expected = fixtures.expected(filename);
return sharp(redRect) await sharp(redRect)
.composite([{ .composite([{
input: blueRect, input: blueRect,
gravity: 'northeast' gravity: 'northeast'
@@ -133,10 +131,8 @@ describe('composite', () => {
input: greenRect, input: greenRect,
gravity: 'southwest' gravity: 'southwest'
}]) }])
.toFile(actual) .toFile(actual);
.then(() => { fixtures.assertMaxColourDistance(actual, expected);
fixtures.assertMaxColourDistance(actual, expected);
});
}); });
it('zero offset', done => { it('zero offset', done => {

View File

@@ -140,7 +140,7 @@ describe('Extend', function () {
}); });
it('Premultiply background when compositing', async () => { it('Premultiply background when compositing', async () => {
const background = '#bf1942cc'; const background = { r: 191, g: 25, b: 66, alpha: 0.8 };
const data = await sharp({ const data = await sharp({
create: { create: {
width: 1, height: 1, channels: 4, background: '#fff0' width: 1, height: 1, channels: 4, background: '#fff0'
@@ -158,10 +158,6 @@ describe('Extend', function () {
}) })
.raw() .raw()
.toBuffer(); .toBuffer();
const [r1, g1, b1, a1, r2, g2, b2, a2] = data; assert.deepStrictEqual(Array.from(data), [191, 25, 65, 204, 238, 31, 82, 204]);
assert.strictEqual(true, Math.abs(r2 - r1) < 2);
assert.strictEqual(true, Math.abs(g2 - g1) < 2);
assert.strictEqual(true, Math.abs(b2 - b1) < 2);
assert.strictEqual(true, Math.abs(a2 - a1) < 2);
}); });
}); });

View File

@@ -328,6 +328,20 @@ describe('Input/output', function () {
}); });
}); });
it('Allow use of toBuffer and toFile with same instance', async () => {
const instance = sharp({
create: {
width: 8,
height: 8,
channels: 3,
background: 'red'
}
});
await instance.toFile(fixtures.path('output.jpg'));
const data = await instance.toBuffer();
assert.strictEqual(Buffer.isBuffer(data), true);
});
it('Fail when output File is input File', function (done) { it('Fail when output File is input File', function (done) {
sharp(fixtures.inputJpg).toFile(fixtures.inputJpg, function (err) { sharp(fixtures.inputJpg).toFile(fixtures.inputJpg, function (err) {
assert(err instanceof Error); assert(err instanceof Error);

View File

@@ -527,7 +527,7 @@ describe('Image metadata', function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(64) .resize(64)
.withMetadata({ icc: 'cmyk' }) .withMetadata({ icc: 'cmyk' })
.toFile(output, function (err, info) { .toFile(output, function (err) {
if (err) throw err; if (err) throw err;
sharp(output).metadata(function (err, metadata) { sharp(output).metadata(function (err, metadata) {
if (err) throw err; if (err) throw err;
@@ -543,7 +543,7 @@ describe('Image metadata', function () {
assert.strictEqual('Relative', profile.intent); assert.strictEqual('Relative', profile.intent);
assert.strictEqual('Printer', profile.deviceClass); assert.strictEqual('Printer', profile.deviceClass);
}); });
fixtures.assertSimilar(output, fixtures.expected('icc-cmyk.jpg'), { threshold: 0 }, done); fixtures.assertSimilar(output, fixtures.expected('icc-cmyk.jpg'), { threshold: 1 }, done);
}); });
}); });

View File

@@ -368,7 +368,7 @@ describe('Resize dimensions', function () {
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(2800, info.width); assert.strictEqual(2800, info.width);
assert.strictEqual(2225, info.height); assert.strictEqual(2286, info.height);
done(); done();
}); });
}); });
@@ -383,46 +383,12 @@ describe('Resize dimensions', function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(2725, info.width); assert.strictEqual(2817, info.width);
assert.strictEqual(2300, info.height); assert.strictEqual(2300, info.height);
done(); done();
}); });
}); });
it('Do not crop when fit = cover and withoutReduction = true and width >= outputWidth, and height < outputHeight', function (done) {
sharp(fixtures.inputJpg)
.resize({
width: 3000,
height: 1000,
withoutReduction: true
})
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(3000, info.width);
assert.strictEqual(2225, info.height);
done();
});
});
it('Do not crop when fit = cover and withoutReduction = true and width < outputWidth, and height >= outputHeight', function (done) {
sharp(fixtures.inputJpg)
.resize({
width: 1500,
height: 2226,
withoutReduction: true
})
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(2226, info.height);
done();
});
});
it('Do enlarge when input width is less than output width', function (done) { it('Do enlarge when input width is less than output width', function (done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize({ .resize({
@@ -637,28 +603,21 @@ describe('Resize dimensions', function () {
}); });
}); });
it('fastShrinkOnLoad: false ensures image is not shifted', function (done) { [
return sharp(fixtures.inputJpgCenteredImage) true,
.resize(9, 8, { fastShrinkOnLoad: false }) false
.png() ].forEach(function (value) {
.toBuffer(function (err, data, info) { it(`fastShrinkOnLoad: ${value} does not causes image shifts`, function (done) {
if (err) throw err; sharp(fixtures.inputJpgCenteredImage)
assert.strictEqual(9, info.width); .resize(9, 8, { fastShrinkOnLoad: value })
assert.strictEqual(8, info.height); .png()
fixtures.assertSimilar(fixtures.expected('fast-shrink-on-load-false.png'), data, done); .toBuffer(function (err, data, info) {
}); if (err) throw err;
}); assert.strictEqual(9, info.width);
assert.strictEqual(8, info.height);
it('fastShrinkOnLoad: true (default) might result in shifted image', function (done) { fixtures.assertSimilar(fixtures.expected('fast-shrink-on-load.png'), data, done);
return sharp(fixtures.inputJpgCenteredImage) });
.resize(9, 8) });
.png()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(9, info.width);
assert.strictEqual(8, info.height);
fixtures.assertSimilar(fixtures.expected('fast-shrink-on-load-true.png'), data, done);
});
}); });
[ [
@@ -780,6 +739,27 @@ describe('Resize dimensions', function () {
assert.strictEqual(info.height, 1); assert.strictEqual(info.height, 1);
}); });
it('Skip JPEG shrink-on-load for known libjpeg rounding errors', async () => {
const input = await sharp({
create: {
width: 1000,
height: 667,
channels: 3,
background: 'red'
}
})
.jpeg()
.toBuffer();
const output = await sharp(input)
.resize({ width: 500 })
.toBuffer();
const { width, height } = await sharp(output).metadata();
assert.strictEqual(width, 500);
assert.strictEqual(height, 334);
});
it('unknown kernel throws', function () { it('unknown kernel throws', function () {
assert.throws(function () { assert.throws(function () {
sharp().resize(null, null, { kernel: 'unknown' }); sharp().resize(null, null, { kernel: 'unknown' });

View File

@@ -135,4 +135,11 @@ describe('SVG input', function () {
assert.strictEqual(info.height, 240); assert.strictEqual(info.height, 240);
assert.strictEqual(info.channels, 4); assert.strictEqual(info.channels, 4);
}); });
it('Detects SVG passed as a string', () =>
assert.rejects(
() => sharp('<svg></svg>').toBuffer(),
/Input file is missing, did you mean/
)
);
}); });