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.
|
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.
|
||||||
|
|||||||
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)?
|
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?
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
[](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)
|
[](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
|
||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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")) {
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
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) {
|
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);
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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' });
|
||||||
|
|||||||