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.
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.

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)?
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?

View File

@@ -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.
[![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)
## Licensing

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
"version": "0.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"
},

View File

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

View File

@@ -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

View File

@@ -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")) {

View File

@@ -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"),

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) {
sharp(fixtures.inputJpg).toFile(fixtures.inputJpg, function (err) {
assert(err instanceof Error);

View File

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

View File

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