Compare commits

..

14 Commits

Author SHA1 Message Date
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
30 changed files with 126 additions and 119 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,6 +5,8 @@ labels: installation
--- ---
<!-- Please try to answer as many of these questions as possible. -->
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/install)? Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/install)?
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? 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?
@@ -13,7 +15,7 @@ Are you using the latest version? Is the version currently in use as reported by
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?

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,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,23 @@
Requires libvips v8.12.2 Requires libvips v8.12.2
### 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

@@ -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.1",
"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": [
@@ -146,7 +146,7 @@
"mocha": "^9.2.0", "mocha": "^9.2.0",
"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

@@ -885,7 +885,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 +940,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
@@ -532,12 +518,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
@@ -1442,7 +1430,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"),

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

@@ -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' });