From a618ce7a1593e3dcc7d2d2170477389efd5fe420 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Sun, 21 Aug 2022 17:51:05 +0100 Subject: [PATCH] Ensure image is unpremultiplied before composite #3334 --- docs/changelog.md | 3 +++ src/pipeline.cc | 24 ++++++++++++------------ test/unit/composite.js | 22 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d83bced2..3ad1b31a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -52,6 +52,9 @@ Requires libvips v8.13.0 Emit warnings when previous calls in the same pipeline will be ignored. [#3319](https://github.com/lovell/sharp/issues/3319) +* Ensure resized image is unpremultiplied before composite. + [#3334](https://github.com/lovell/sharp/issues/3334) + ## v0.30 - *dresser* Requires libvips v8.12.2 diff --git a/src/pipeline.cc b/src/pipeline.cc index 71b0de79..f8ea3678 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -590,6 +590,18 @@ class PipelineWorker : public Napi::AsyncWorker { baton->sharpenX1, baton->sharpenY2, baton->sharpenY3); } + // Reverse premultiplication after all transformations + if (shouldPremultiplyAlpha) { + image = image.unpremultiply(); + // Cast pixel values to integer + if (sharp::Is16Bit(image.interpretation())) { + image = image.cast(VIPS_FORMAT_USHORT); + } else { + image = image.cast(VIPS_FORMAT_UCHAR); + } + } + baton->premultiplied = shouldPremultiplyAlpha; + // Composite if (shouldComposite) { std::vector images = { image }; @@ -670,18 +682,6 @@ class PipelineWorker : public Napi::AsyncWorker { image = VImage::composite(images, modes, VImage::option()->set("x", xs)->set("y", ys)); } - // Reverse premultiplication after all transformations: - if (shouldPremultiplyAlpha) { - image = image.unpremultiply(); - // Cast pixel values to integer - if (sharp::Is16Bit(image.interpretation())) { - image = image.cast(VIPS_FORMAT_USHORT); - } else { - image = image.cast(VIPS_FORMAT_UCHAR); - } - } - baton->premultiplied = shouldPremultiplyAlpha; - // Gamma decoding (brighten) if (baton->gammaOut >= 1 && baton->gammaOut <= 3) { image = sharp::Gamma(image, baton->gammaOut); diff --git a/test/unit/composite.js b/test/unit/composite.js index 56927ccf..e92eec49 100644 --- a/test/unit/composite.js +++ b/test/unit/composite.js @@ -447,4 +447,26 @@ describe('composite', () => { assert.strictEqual(info.width, 40); assert.strictEqual(info.height, 40); }); + + it('Ensure implict unpremultiply after resize but before composite', async () => { + const [r, g, b, a] = await sharp({ + create: { + width: 1, height: 1, channels: 4, background: 'saddlebrown' + } + }) + .resize({ width: 8 }) + .composite([{ + input: Buffer.from([255, 255, 255, 128]), + raw: { width: 1, height: 1, channels: 4 }, + tile: true, + blend: 'dest-in' + }]) + .raw() + .toBuffer(); + + assert.strictEqual(r, 139); + assert.strictEqual(g, 69); + assert.strictEqual(b, 19); + assert.strictEqual(a, 128); + }); });