Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
735793ba99 | ||
|
|
47792df689 | ||
|
|
5c6cdfaece | ||
|
|
115a6b10f6 | ||
|
|
4feee506cf | ||
|
|
83db5f8a2a | ||
|
|
7eb5efa3a3 | ||
|
|
5a9f89fe06 | ||
|
|
02e0c2dfc9 | ||
|
|
968d9d7008 | ||
|
|
7faacd91b0 | ||
|
|
154eaff4ec | ||
|
|
424660278d | ||
|
|
2b01951306 |
10
.github/CONTRIBUTING.md
vendored
@@ -23,22 +23,22 @@ the feature you need.
|
||||
|
||||
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`.
|
||||
|
||||
## 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.
|
||||
|
||||
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)
|
||||
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.
|
||||
|
||||
@@ -93,5 +93,5 @@ Please feel free to ask any questions via a
|
||||
[new issue](https://github.com/lovell/sharp/issues/new).
|
||||
|
||||
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.
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/installation.md
vendored
@@ -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)?
|
||||
|
||||
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 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?
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 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
|
||||
is to convert large images in common formats to
|
||||
@@ -95,10 +95,10 @@ readableStream
|
||||
|
||||
## 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.
|
||||
|
||||
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
||||
[](https://coveralls.io/r/lovell/sharp?branch=main)
|
||||
[](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
|
||||
|
||||
## Licensing
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 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
|
||||
is to convert large images in common formats to
|
||||
@@ -65,7 +65,7 @@ as [pngcrush](https://pmt.sourceforge.io/pngcrush/).
|
||||
|
||||
### 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.
|
||||
|
||||
### Licensing
|
||||
|
||||
@@ -188,13 +188,13 @@ Returns **[boolean][10]**
|
||||
|
||||
[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
|
||||
|
||||
[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
|
||||
|
||||
|
||||
@@ -4,6 +4,23 @@
|
||||
|
||||
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
|
||||
|
||||
* Add support for GIF output to prebuilt binaries.
|
||||
|
||||
@@ -139,6 +139,7 @@ function toBuffer (options, callback) {
|
||||
} else if (this.options.resolveWithObject) {
|
||||
this.options.resolveWithObject = false;
|
||||
}
|
||||
this.options.fileOut = '';
|
||||
return this._pipeline(is.fn(options) ? options : callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ const interpolators = {
|
||||
bilinear: 'bilinear',
|
||||
/** [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). */
|
||||
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',
|
||||
/** [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. */
|
||||
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'
|
||||
};
|
||||
|
||||
|
||||
@@ -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.30.0",
|
||||
"version": "0.30.1",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://github.com/lovell/sharp",
|
||||
"contributors": [
|
||||
@@ -146,7 +146,7 @@
|
||||
"mocha": "^9.2.0",
|
||||
"mock-fs": "^5.1.2",
|
||||
"nyc": "^15.1.0",
|
||||
"prebuild": "^11.0.2",
|
||||
"prebuild": "^11.0.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"semistandard": "^16.0.1"
|
||||
},
|
||||
|
||||
@@ -885,7 +885,7 @@ namespace sharp {
|
||||
}
|
||||
|
||||
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) {
|
||||
// Swap input width and height when requested.
|
||||
std::swap(width, height);
|
||||
@@ -940,9 +940,14 @@ namespace sharp {
|
||||
}
|
||||
}
|
||||
|
||||
// We should not enlarge (oversample) the output image,
|
||||
// if withoutEnlargement is specified.
|
||||
if (withoutEnlargement) {
|
||||
// We should not reduce or enlarge the output image, if
|
||||
// withoutReduction or withoutEnlargement is specified.
|
||||
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);
|
||||
vshrink = std::max(1.0, vshrink);
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ namespace sharp {
|
||||
the required thumbnail width/height and canvas mode.
|
||||
*/
|
||||
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
|
||||
|
||||
|
||||
@@ -138,17 +138,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
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
|
||||
double hshrink;
|
||||
double vshrink;
|
||||
@@ -161,7 +150,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// 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->canvas, swap, baton->withoutEnlargement, baton->withoutReduction);
|
||||
|
||||
// The jpeg preload shrink.
|
||||
int jpegShrinkOnLoad = 1;
|
||||
@@ -184,7 +173,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
if (inputImageType == sharp::ImageType::JPEG) {
|
||||
// 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;
|
||||
if (shrink >= 8 * factor) {
|
||||
jpegShrinkOnLoad = 8;
|
||||
@@ -193,6 +182,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
} else if (shrink >= 2 * factor) {
|
||||
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 ||
|
||||
inputImageType == sharp::ImageType::SVG ||
|
||||
inputImageType == sharp::ImageType::PDF) {
|
||||
@@ -270,35 +263,28 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
}
|
||||
|
||||
// Any pre-shrinking may already have been done
|
||||
int thumbWidth = image.width();
|
||||
int thumbHeight = image.height();
|
||||
inputWidth = image.width();
|
||||
inputHeight = image.height();
|
||||
|
||||
// After pre-shrink, but before the main shrink stage
|
||||
// Reuse the initial pageHeight if we didn't pre-shrink
|
||||
int preshrunkPageHeight = shouldPreShrink ? sharp::GetPageHeight(image) : pageHeight;
|
||||
|
||||
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);
|
||||
if (shouldPreShrink) {
|
||||
pageHeight = sharp::GetPageHeight(image);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// In toilet-roll mode, we must adjust vshrink so that we exactly hit
|
||||
// preshrunkPageHeight or we'll have pixels straddling pixel boundaries
|
||||
if (thumbHeight > preshrunkPageHeight) {
|
||||
// pageHeight or we'll have pixels straddling pixel boundaries
|
||||
if (inputHeight > pageHeight) {
|
||||
targetHeight *= nPages;
|
||||
vshrink = static_cast<double>(thumbHeight) / targetHeight;
|
||||
vshrink = static_cast<double>(inputHeight) / targetHeight;
|
||||
}
|
||||
|
||||
// Ensure we're using a device-independent colour space
|
||||
@@ -532,12 +518,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
MultiPageUnsupported(nPages, "Affine");
|
||||
std::vector<double> background;
|
||||
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)
|
||||
->set("idx", baton->affineIdx)
|
||||
->set("idy", baton->affineIdy)
|
||||
->set("odx", baton->affineOdx)
|
||||
->set("ody", baton->affineOdy)
|
||||
->set("interpolate", baton->affineInterpolator));
|
||||
->set("interpolate", interp));
|
||||
}
|
||||
|
||||
// Extend edges
|
||||
@@ -1442,7 +1430,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->affineIdy = sharp::AttrAsDouble(options, "affineIdy");
|
||||
baton->affineOdx = sharp::AttrAsDouble(options, "affineOdx");
|
||||
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->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
|
||||
if (options.Has("boolean")) {
|
||||
|
||||
@@ -126,7 +126,7 @@ struct PipelineBaton {
|
||||
double affineIdy;
|
||||
double affineOdx;
|
||||
double affineOdy;
|
||||
vips::VInterpolate affineInterpolator;
|
||||
std::string affineInterpolator;
|
||||
int jpegQuality;
|
||||
bool jpegProgressive;
|
||||
std::string jpegChromaSubsampling;
|
||||
@@ -268,7 +268,7 @@ struct PipelineBaton {
|
||||
affineIdy(0),
|
||||
affineOdx(0),
|
||||
affineOdy(0),
|
||||
affineInterpolator(vips::VInterpolate::new_from_name("bicubic")),
|
||||
affineInterpolator("bicubic"),
|
||||
jpegQuality(80),
|
||||
jpegProgressive(false),
|
||||
jpegChromaSubsampling("4:2:0"),
|
||||
|
||||
BIN
test/fixtures/expected/fast-shrink-on-load-false.png
vendored
|
Before Width: | Height: | Size: 270 B |
BIN
test/fixtures/expected/fast-shrink-on-load-true.png
vendored
|
Before Width: | Height: | Size: 265 B |
BIN
test/fixtures/expected/fast-shrink-on-load.png
vendored
Normal file
|
After Width: | Height: | Size: 277 B |
BIN
test/fixtures/expected/icc-cmyk.jpg
vendored
|
Before Width: | Height: | Size: 943 KiB After Width: | Height: | Size: 943 KiB |
BIN
test/fixtures/expected/overlay-gravity-center.jpg
vendored
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
test/fixtures/expected/overlay-gravity-centre.jpg
vendored
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
test/fixtures/expected/overlay-gravity-east.jpg
vendored
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
test/fixtures/expected/overlay-gravity-north.jpg
vendored
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
test/fixtures/expected/overlay-gravity-northeast.jpg
vendored
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
test/fixtures/expected/overlay-gravity-northwest.jpg
vendored
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
test/fixtures/expected/overlay-gravity-south.jpg
vendored
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
test/fixtures/expected/overlay-gravity-southeast.jpg
vendored
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
test/fixtures/expected/overlay-gravity-southwest.jpg
vendored
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
test/fixtures/expected/overlay-gravity-west.jpg
vendored
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -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) {
|
||||
sharp(fixtures.inputJpg).toFile(fixtures.inputJpg, function (err) {
|
||||
assert(err instanceof Error);
|
||||
|
||||
@@ -527,7 +527,7 @@ describe('Image metadata', function () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(64)
|
||||
.withMetadata({ icc: 'cmyk' })
|
||||
.toFile(output, function (err, info) {
|
||||
.toFile(output, function (err) {
|
||||
if (err) throw err;
|
||||
sharp(output).metadata(function (err, metadata) {
|
||||
if (err) throw err;
|
||||
@@ -543,7 +543,7 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual('Relative', profile.intent);
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ describe('Resize dimensions', function () {
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(2800, info.width);
|
||||
assert.strictEqual(2225, info.height);
|
||||
assert.strictEqual(2286, info.height);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -383,46 +383,12 @@ describe('Resize dimensions', function () {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(2725, info.width);
|
||||
assert.strictEqual(2817, info.width);
|
||||
assert.strictEqual(2300, 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: 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) {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize({
|
||||
@@ -637,28 +603,21 @@ describe('Resize dimensions', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('fastShrinkOnLoad: false ensures image is not shifted', function (done) {
|
||||
return sharp(fixtures.inputJpgCenteredImage)
|
||||
.resize(9, 8, { fastShrinkOnLoad: false })
|
||||
.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-false.png'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fastShrinkOnLoad: true (default) might result in shifted image', function (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);
|
||||
});
|
||||
[
|
||||
true,
|
||||
false
|
||||
].forEach(function (value) {
|
||||
it(`fastShrinkOnLoad: ${value} does not causes image shifts`, function (done) {
|
||||
sharp(fixtures.inputJpgCenteredImage)
|
||||
.resize(9, 8, { fastShrinkOnLoad: value })
|
||||
.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.png'), data, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
[
|
||||
@@ -780,6 +739,27 @@ describe('Resize dimensions', function () {
|
||||
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 () {
|
||||
assert.throws(function () {
|
||||
sharp().resize(null, null, { kernel: 'unknown' });
|
||||
|
||||