mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Avoid (un)premultiplication for overlay image without alpha channel
Add 'premultiplied' boolean attribute to output info, helps test
This commit is contained in:
parent
301bfbd271
commit
1169afbe90
@ -11,6 +11,8 @@ Overlay (composite) an image over the processed (resized, extracted etc.) image.
|
|||||||
The overlay image must be the same size or smaller than the processed image.
|
The overlay image must be the same size or smaller than the processed image.
|
||||||
If both `top` and `left` options are provided, they take precedence over `gravity`.
|
If both `top` and `left` options are provided, they take precedence over `gravity`.
|
||||||
|
|
||||||
|
If the overlay image contains an alpha channel then composition with premultiplication will occur.
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
|
|
||||||
- `overlay` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String))** Buffer containing image data or String containing the path to an image file.
|
- `overlay` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String))** Buffer containing image data or String containing the path to an image file.
|
||||||
|
@ -27,7 +27,8 @@ A Promises/A+ promise is returned when `callback` is not provided.
|
|||||||
|
|
||||||
- `fileOut` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the path to write the image data to.
|
- `fileOut` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the path to write the image data to.
|
||||||
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?** called on completion with two arguments `(err, info)`.
|
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?** called on completion with two arguments `(err, info)`.
|
||||||
`info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
|
`info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
||||||
|
`channels` and `premultiplied` (indicating if premultiplication was used).
|
||||||
|
|
||||||
|
|
||||||
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
|
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
|
||||||
@ -44,7 +45,8 @@ By default, the format will match the input image, except GIF and SVG input whic
|
|||||||
|
|
||||||
- `err` is an error, if any.
|
- `err` is an error, if any.
|
||||||
- `data` is the output image data.
|
- `data` is the output image data.
|
||||||
- `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
|
- `info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
||||||
|
`channels` and `premultiplied` (indicating if premultiplication was used).
|
||||||
A Promise is returned when `callback` is not provided.
|
A Promise is returned when `callback` is not provided.
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
|
@ -6,6 +6,10 @@ Requires libvips v8.5.2.
|
|||||||
|
|
||||||
#### v0.18.0 - TBD
|
#### v0.18.0 - TBD
|
||||||
|
|
||||||
|
* Avoid costly (un)premultiply when using overlayWith without alpha channel.
|
||||||
|
[#573](https://github.com/lovell/sharp/issues/573)
|
||||||
|
[@strarsis](https://github.com/strarsis)
|
||||||
|
|
||||||
* Expose warnings from libvips via NODE_DEBUG=sharp environment variable.
|
* Expose warnings from libvips via NODE_DEBUG=sharp environment variable.
|
||||||
[#607](https://github.com/lovell/sharp/issues/607)
|
[#607](https://github.com/lovell/sharp/issues/607)
|
||||||
[@puzrin](https://github.com/puzrin)
|
[@puzrin](https://github.com/puzrin)
|
||||||
|
@ -8,6 +8,8 @@ const is = require('./is');
|
|||||||
* The overlay image must be the same size or smaller than the processed image.
|
* The overlay image must be the same size or smaller than the processed image.
|
||||||
* If both `top` and `left` options are provided, they take precedence over `gravity`.
|
* If both `top` and `left` options are provided, they take precedence over `gravity`.
|
||||||
*
|
*
|
||||||
|
* If the overlay image contains an alpha channel then composition with premultiplication will occur.
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* sharp('input.png')
|
* sharp('input.png')
|
||||||
* .rotate(180)
|
* .rotate(180)
|
||||||
|
@ -15,7 +15,8 @@ const sharp = require('../build/Release/sharp.node');
|
|||||||
*
|
*
|
||||||
* @param {String} fileOut - the path to write the image data to.
|
* @param {String} fileOut - the path to write the image data to.
|
||||||
* @param {Function} [callback] - called on completion with two arguments `(err, info)`.
|
* @param {Function} [callback] - called on completion with two arguments `(err, info)`.
|
||||||
* `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
|
* `info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
||||||
|
* `channels` and `premultiplied` (indicating if premultiplication was used).
|
||||||
* @returns {Promise<Object>} - when no callback is provided
|
* @returns {Promise<Object>} - when no callback is provided
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
@ -51,7 +52,8 @@ function toFile (fileOut, callback) {
|
|||||||
* `callback`, if present, gets three arguments `(err, data, info)` where:
|
* `callback`, if present, gets three arguments `(err, data, info)` where:
|
||||||
* - `err` is an error, if any.
|
* - `err` is an error, if any.
|
||||||
* - `data` is the output image data.
|
* - `data` is the output image data.
|
||||||
* - `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
|
* - `info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
||||||
|
* `channels` and `premultiplied` (indicating if premultiplication was used).
|
||||||
* A Promise is returned when `callback` is not provided.
|
* A Promise is returned when `callback` is not provided.
|
||||||
*
|
*
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
|
@ -29,67 +29,32 @@ using vips::VError;
|
|||||||
namespace sharp {
|
namespace sharp {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Alpha composite src over dst with given gravity.
|
Composite overlayImage over image at given position
|
||||||
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
|
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
||||||
*/
|
*/
|
||||||
VImage Composite(VImage src, VImage dst, const int gravity) {
|
VImage Composite(VImage image, VImage overlayImage, int const left, int const top) {
|
||||||
if (IsInputValidForComposition(src, dst)) {
|
if (HasAlpha(overlayImage)) {
|
||||||
// Enlarge overlay src, if required
|
// Alpha composite
|
||||||
if (src.width() < dst.width() || src.height() < dst.height()) {
|
if (overlayImage.width() < image.width() || overlayImage.height() < image.height()) {
|
||||||
// Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity.
|
// Enlarge overlay
|
||||||
int left;
|
std::vector<double> const background { 0.0, 0.0, 0.0, 0.0 };
|
||||||
int top;
|
overlayImage = overlayImage.embed(left, top, image.width(), image.height(), VImage::option()
|
||||||
std::tie(left, top) = CalculateCrop(dst.width(), dst.height(), src.width(), src.height(), gravity);
|
|
||||||
// Embed onto transparent background
|
|
||||||
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
|
|
||||||
src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
|
|
||||||
->set("extend", VIPS_EXTEND_BACKGROUND)
|
->set("extend", VIPS_EXTEND_BACKGROUND)
|
||||||
->set("background", background));
|
->set("background", background));
|
||||||
}
|
}
|
||||||
return CompositeImage(src, dst);
|
return AlphaComposite(image, overlayImage);
|
||||||
|
} else {
|
||||||
|
if (HasAlpha(image)) {
|
||||||
|
// Add alpha channel to overlayImage so channels match
|
||||||
|
double const multiplier = sharp::Is16Bit(overlayImage.interpretation()) ? 256.0 : 1.0;
|
||||||
|
overlayImage = overlayImage.bandjoin(
|
||||||
|
VImage::new_matrix(overlayImage.width(), overlayImage.height()).new_from_image(255 * multiplier));
|
||||||
|
}
|
||||||
|
return image.insert(overlayImage, left, top);
|
||||||
}
|
}
|
||||||
// If the input was not valid for composition the return the input image itself
|
|
||||||
return dst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VImage Composite(VImage src, VImage dst, const int x, const int y) {
|
VImage AlphaComposite(VImage dst, VImage src) {
|
||||||
if (IsInputValidForComposition(src, dst)) {
|
|
||||||
// Enlarge overlay src, if required
|
|
||||||
if (src.width() < dst.width() || src.height() < dst.height()) {
|
|
||||||
// Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity.
|
|
||||||
int left;
|
|
||||||
int top;
|
|
||||||
std::tie(left, top) = CalculateCrop(dst.width(), dst.height(), src.width(), src.height(), x, y);
|
|
||||||
// Embed onto transparent background
|
|
||||||
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
|
|
||||||
src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
|
|
||||||
->set("extend", VIPS_EXTEND_BACKGROUND)
|
|
||||||
->set("background", background));
|
|
||||||
}
|
|
||||||
return CompositeImage(src, dst);
|
|
||||||
}
|
|
||||||
// If the input was not valid for composition the return the input image itself
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsInputValidForComposition(VImage src, VImage dst) {
|
|
||||||
using sharp::CalculateCrop;
|
|
||||||
using sharp::HasAlpha;
|
|
||||||
|
|
||||||
if (!HasAlpha(src)) {
|
|
||||||
throw VError("Overlay image must have an alpha channel");
|
|
||||||
}
|
|
||||||
if (!HasAlpha(dst)) {
|
|
||||||
throw VError("Image to be overlaid must have an alpha channel");
|
|
||||||
}
|
|
||||||
if (src.width() > dst.width() || src.height() > dst.height()) {
|
|
||||||
throw VError("Overlay image must have same dimensions or smaller");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
VImage CompositeImage(VImage src, VImage dst) {
|
|
||||||
// Split src into non-alpha and alpha channels
|
// Split src into non-alpha and alpha channels
|
||||||
VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1));
|
VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1));
|
||||||
VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0);
|
VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0);
|
||||||
|
@ -32,20 +32,14 @@ namespace sharp {
|
|||||||
VImage Composite(VImage src, VImage dst, const int gravity);
|
VImage Composite(VImage src, VImage dst, const int gravity);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Alpha composite src over dst with given x and y offsets.
|
Composite overlayImage over image at given position
|
||||||
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
|
|
||||||
*/
|
*/
|
||||||
VImage Composite(VImage src, VImage dst, const int x, const int y);
|
VImage Composite(VImage image, VImage overlayImage, int const x, int const y);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check if the src and dst Images for composition operation are valid
|
Alpha composite overlayImage over image, assumes matching dimensions
|
||||||
*/
|
*/
|
||||||
bool IsInputValidForComposition(VImage src, VImage dst);
|
VImage AlphaComposite(VImage image, VImage overlayImage);
|
||||||
|
|
||||||
/*
|
|
||||||
Given a valid src and dst, returns the composite of the two images
|
|
||||||
*/
|
|
||||||
VImage CompositeImage(VImage src, VImage dst);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Cutout src over dst with given gravity.
|
Cutout src over dst with given gravity.
|
||||||
|
@ -333,22 +333,29 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure image has an alpha channel when there is an overlay
|
// Ensure image has an alpha channel when there is an overlay with an alpha channel
|
||||||
bool hasOverlay = baton->overlay != nullptr;
|
VImage overlayImage;
|
||||||
if (hasOverlay && !HasAlpha(image)) {
|
ImageType overlayImageType = ImageType::UNKNOWN;
|
||||||
|
bool shouldOverlayWithAlpha = FALSE;
|
||||||
|
if (baton->overlay != nullptr) {
|
||||||
|
std::tie(overlayImage, overlayImageType) = OpenInput(baton->overlay, baton->accessMethod);
|
||||||
|
if (HasAlpha(overlayImage)) {
|
||||||
|
shouldOverlayWithAlpha = !baton->overlayCutout;
|
||||||
|
if (!HasAlpha(image)) {
|
||||||
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
|
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
|
||||||
image = image.bandjoin(
|
image = image.bandjoin(
|
||||||
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
|
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool const shouldShrink = xshrink > 1 || yshrink > 1;
|
bool const shouldShrink = xshrink > 1 || yshrink > 1;
|
||||||
bool const shouldReduce = xresidual != 1.0 || yresidual != 1.0;
|
bool const shouldReduce = xresidual != 1.0 || yresidual != 1.0;
|
||||||
bool const shouldBlur = baton->blurSigma != 0.0;
|
bool const shouldBlur = baton->blurSigma != 0.0;
|
||||||
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
|
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
|
||||||
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
||||||
bool const shouldCutout = baton->overlayCutout;
|
|
||||||
bool const shouldPremultiplyAlpha = HasAlpha(image) &&
|
bool const shouldPremultiplyAlpha = HasAlpha(image) &&
|
||||||
(shouldShrink || shouldReduce || shouldBlur || shouldConv || shouldSharpen || (hasOverlay && !shouldCutout));
|
(shouldShrink || shouldReduce || shouldBlur || shouldConv || shouldSharpen || shouldOverlayWithAlpha);
|
||||||
|
|
||||||
// Premultiply image alpha channel before all transformations to avoid
|
// Premultiply image alpha channel before all transformations to avoid
|
||||||
// dark fringing around bright pixels
|
// dark fringing around bright pixels
|
||||||
@ -584,10 +591,11 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Composite with overlay, if present
|
// Composite with overlay, if present
|
||||||
if (hasOverlay) {
|
if (baton->overlay != nullptr) {
|
||||||
VImage overlayImage;
|
// Verify overlay image is within current dimensions
|
||||||
ImageType overlayImageType = ImageType::UNKNOWN;
|
if (overlayImage.width() > image.width() || overlayImage.height() > image.height()) {
|
||||||
std::tie(overlayImage, overlayImageType) = OpenInput(baton->overlay, baton->accessMethod);
|
throw vips::VError("Overlay image must have same dimensions or smaller");
|
||||||
|
}
|
||||||
// Check if overlay is tiled
|
// Check if overlay is tiled
|
||||||
if (baton->overlayTile) {
|
if (baton->overlayTile) {
|
||||||
int const overlayImageWidth = overlayImage.width();
|
int const overlayImageWidth = overlayImage.width();
|
||||||
@ -620,31 +628,34 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
// the overlayGravity was used for extract_area, therefore set it back to its default value of 0
|
// the overlayGravity was used for extract_area, therefore set it back to its default value of 0
|
||||||
baton->overlayGravity = 0;
|
baton->overlayGravity = 0;
|
||||||
}
|
}
|
||||||
if (shouldCutout) {
|
if (baton->overlayCutout) {
|
||||||
// 'cut out' the image, premultiplication is not required
|
// 'cut out' the image, premultiplication is not required
|
||||||
image = sharp::Cutout(overlayImage, image, baton->overlayGravity);
|
image = sharp::Cutout(overlayImage, image, baton->overlayGravity);
|
||||||
} else {
|
} else {
|
||||||
|
// Ensure overlay is sRGB
|
||||||
|
overlayImage = overlayImage.colourspace(VIPS_INTERPRETATION_sRGB);
|
||||||
|
// Ensure overlay matches premultiplication state
|
||||||
|
if (shouldPremultiplyAlpha) {
|
||||||
// Ensure overlay has alpha channel
|
// Ensure overlay has alpha channel
|
||||||
if (!HasAlpha(overlayImage)) {
|
if (!HasAlpha(overlayImage)) {
|
||||||
double const multiplier = sharp::Is16Bit(overlayImage.interpretation()) ? 256.0 : 1.0;
|
double const multiplier = sharp::Is16Bit(overlayImage.interpretation()) ? 256.0 : 1.0;
|
||||||
overlayImage = overlayImage.bandjoin(
|
overlayImage = overlayImage.bandjoin(
|
||||||
VImage::new_matrix(overlayImage.width(), overlayImage.height()).new_from_image(255 * multiplier));
|
VImage::new_matrix(overlayImage.width(), overlayImage.height()).new_from_image(255 * multiplier));
|
||||||
}
|
}
|
||||||
// Ensure image has alpha channel
|
overlayImage = overlayImage.premultiply();
|
||||||
if (!HasAlpha(image)) {
|
|
||||||
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
|
|
||||||
image = image.bandjoin(
|
|
||||||
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
|
|
||||||
}
|
}
|
||||||
// Ensure overlay is premultiplied sRGB
|
int left;
|
||||||
overlayImage = overlayImage.colourspace(VIPS_INTERPRETATION_sRGB).premultiply();
|
int top;
|
||||||
if (baton->overlayXOffset >= 0 && baton->overlayYOffset >= 0) {
|
if (baton->overlayXOffset >= 0 && baton->overlayYOffset >= 0) {
|
||||||
// Composite images with given offsets
|
// Composite images at given offsets
|
||||||
image = sharp::Composite(overlayImage, image, baton->overlayXOffset, baton->overlayYOffset);
|
std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
|
||||||
|
overlayImage.width(), overlayImage.height(), baton->overlayXOffset, baton->overlayYOffset);
|
||||||
} else {
|
} else {
|
||||||
// Composite images with given gravity
|
// Composite images with given gravity
|
||||||
image = sharp::Composite(overlayImage, image, baton->overlayGravity);
|
std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
|
||||||
|
overlayImage.width(), overlayImage.height(), baton->overlayGravity);
|
||||||
}
|
}
|
||||||
|
image = sharp::Composite(image, overlayImage, left, top);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,6 +669,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
image = image.cast(VIPS_FORMAT_UCHAR);
|
image = image.cast(VIPS_FORMAT_UCHAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
baton->premultiplied = shouldPremultiplyAlpha;
|
||||||
|
|
||||||
// Gamma decoding (brighten)
|
// Gamma decoding (brighten)
|
||||||
if (baton->gamma >= 1 && baton->gamma <= 3) {
|
if (baton->gamma >= 1 && baton->gamma <= 3) {
|
||||||
@ -942,6 +954,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
Set(info, New("width").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(width)));
|
Set(info, New("width").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(width)));
|
||||||
Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(height)));
|
Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(height)));
|
||||||
Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->channels)));
|
Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->channels)));
|
||||||
|
Set(info, New("premultiplied").ToLocalChecked(), New<v8::Boolean>(baton->premultiplied));
|
||||||
if (baton->cropCalcLeft != -1 && baton->cropCalcLeft != -1) {
|
if (baton->cropCalcLeft != -1 && baton->cropCalcLeft != -1) {
|
||||||
Set(info, New("cropCalcLeft").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->cropCalcLeft)));
|
Set(info, New("cropCalcLeft").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->cropCalcLeft)));
|
||||||
Set(info, New("cropCalcTop").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->cropCalcTop)));
|
Set(info, New("cropCalcTop").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->cropCalcTop)));
|
||||||
|
@ -64,6 +64,7 @@ struct PipelineBaton {
|
|||||||
int crop;
|
int crop;
|
||||||
int cropCalcLeft;
|
int cropCalcLeft;
|
||||||
int cropCalcTop;
|
int cropCalcTop;
|
||||||
|
bool premultiplied;
|
||||||
std::string kernel;
|
std::string kernel;
|
||||||
std::string interpolator;
|
std::string interpolator;
|
||||||
bool centreSampling;
|
bool centreSampling;
|
||||||
@ -143,6 +144,7 @@ struct PipelineBaton {
|
|||||||
crop(0),
|
crop(0),
|
||||||
cropCalcLeft(-1),
|
cropCalcLeft(-1),
|
||||||
cropCalcTop(-1),
|
cropCalcTop(-1),
|
||||||
|
premultiplied(false),
|
||||||
centreSampling(false),
|
centreSampling(false),
|
||||||
flatten(false),
|
flatten(false),
|
||||||
negate(false),
|
negate(false),
|
||||||
|
BIN
test/fixtures/expected/overlay-jpeg-with-jpeg.jpg
vendored
Normal file
BIN
test/fixtures/expected/overlay-jpeg-with-jpeg.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
@ -155,20 +155,22 @@ describe('Overlays', function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('Composite JPEG onto PNG', function (done) {
|
it('Composite JPEG onto PNG, no premultiply', function (done) {
|
||||||
sharp(fixtures.inputPngOverlayLayer1)
|
sharp(fixtures.inputPngOverlayLayer1)
|
||||||
.overlayWith(fixtures.inputJpgWithLandscapeExif1)
|
.overlayWith(fixtures.inputJpgWithLandscapeExif1)
|
||||||
.toBuffer(function (error) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (error) return done(error);
|
if (err) throw err;
|
||||||
|
assert.strictEqual(false, info.premultiplied);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Composite opaque JPEG onto JPEG', function (done) {
|
it('Composite opaque JPEG onto JPEG, no premultiply', function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.overlayWith(fixtures.inputJpgWithLandscapeExif1)
|
.overlayWith(fixtures.inputJpgWithLandscapeExif1)
|
||||||
.toBuffer(function (error) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (error) return done(error);
|
if (err) throw err;
|
||||||
|
assert.strictEqual(false, info.premultiplied);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -561,14 +563,15 @@ describe('Overlays', function () {
|
|||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(2048, 1536)
|
.resize(2048, 1536)
|
||||||
.overlayWith(data, { raw: info })
|
.overlayWith(data, { raw: info })
|
||||||
.toBuffer(function (err, data) {
|
.toBuffer(function (err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.premultiplied);
|
||||||
fixtures.assertSimilar(fixtures.expected('overlay-jpeg-with-rgb.jpg'), data, done);
|
fixtures.assertSimilar(fixtures.expected('overlay-jpeg-with-rgb.jpg'), data, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Throws an error when called with an invalid file', function (done) {
|
it('Returns an error when called with an invalid file', function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.overlayWith('notfound.png')
|
.overlayWith('notfound.png')
|
||||||
.toBuffer(function (err) {
|
.toBuffer(function (err) {
|
||||||
@ -576,4 +579,20 @@ describe('Overlays', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Composite JPEG onto JPEG, no premultiply', function (done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(480, 320)
|
||||||
|
.overlayWith(fixtures.inputJpgBooleanTest)
|
||||||
|
.png()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(480, info.width);
|
||||||
|
assert.strictEqual(320, info.height);
|
||||||
|
assert.strictEqual(3, info.channels);
|
||||||
|
assert.strictEqual(false, info.premultiplied);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('overlay-jpeg-with-jpeg.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user