diff --git a/docs/changelog.md b/docs/changelog.md index 92cc299b..c904d2bc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -20,6 +20,9 @@ Requires libvips v8.10.6 [#2612](https://github.com/lovell/sharp/issues/2612) [@edsilv](https://github.com/edsilv) +* Ensure composite replicates the correct number of tiles for centred gravities. + [#2626](https://github.com/lovell/sharp/issues/2626) + ## v0.27 - *avif* Requires libvips v8.10.5 diff --git a/src/pipeline.cc b/src/pipeline.cc index 06280ba2..4837a939 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -562,9 +562,17 @@ class PipelineWorker : public Napi::AsyncWorker { // Use gravity in overlay if (compositeImage.width() <= baton->width) { across = static_cast(ceil(static_cast(image.width()) / compositeImage.width())); + // Ensure odd number of tiles across when gravity is centre, north or south + if (composite->gravity == 0 || composite->gravity == 1 || composite->gravity == 3) { + across |= 1; + } } if (compositeImage.height() <= baton->height) { down = static_cast(ceil(static_cast(image.height()) / compositeImage.height())); + // Ensure odd number of tiles down when gravity is centre, east or west + if (composite->gravity == 0 || composite->gravity == 2 || composite->gravity == 4) { + down |= 1; + } } if (across != 0 || down != 0) { int left; diff --git a/test/unit/composite.js b/test/unit/composite.js index 959ef4aa..069d807f 100644 --- a/test/unit/composite.js +++ b/test/unit/composite.js @@ -225,6 +225,24 @@ describe('composite', () => { }); }); + it('centre gravity should replicate correct number of tiles', async () => { + const red = { r: 255, g: 0, b: 0 }; + const [r, g, b] = await sharp({ + create: { + width: 40, height: 40, channels: 4, background: red + } + }) + .composite([{ + input: fixtures.inputPngWithTransparency16bit, + gravity: 'centre', + tile: true + }]) + .raw() + .toBuffer(); + + assert.deepStrictEqual({ r, g, b }, red); + }); + it('cutout via dest-in', done => { sharp(fixtures.inputJpg) .resize(300, 300)