From 02676140e8c0c86471548a98c49389876abb9ff2 Mon Sep 17 00:00:00 2001 From: Manan Jadhav Date: Sun, 20 Dec 2020 17:26:11 +0000 Subject: [PATCH] Allow for negative top/left offsets in composite overlays A top or left offset value of -1 will no longer mean that the value is not set, but will now be an actual offset of -1. INT_MIN for left & top will mean that the values are not set. Co-authored-by: Christian Flintrup --- docs/changelog.md | 4 ++ lib/composite.js | 17 +++++---- package.json | 4 +- src/common.cc | 12 +----- src/pipeline.cc | 5 ++- src/pipeline.h | 6 ++- .../overlay-negative-offset-with-gravity.jpg | Bin 0 -> 24425 bytes test/unit/composite.js | 36 ++++++++++++++++-- 8 files changed, 59 insertions(+), 25 deletions(-) create mode 100644 test/fixtures/expected/overlay-negative-offset-with-gravity.jpg diff --git a/docs/changelog.md b/docs/changelog.md index 0eb87f9a..d8e94791 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -10,6 +10,10 @@ Requires libvips v8.10.5 * Remove experimental status from `heif` output, defaults are now AVIF-centric. +* Allow negative top/left offsets for composite operation. + [#2391](https://github.com/lovell/sharp/pull/2391) + [@CurosMJ](https://github.com/CurosMJ) + * Ensure all platforms use fontconfig for font rendering. [#2399](https://github.com/lovell/sharp/issues/2399) diff --git a/lib/composite.js b/lib/composite.js index 8f45f690..88ab83cb 100644 --- a/lib/composite.js +++ b/lib/composite.js @@ -105,8 +105,9 @@ function composite (images) { input: this._createInputDescriptor(image.input, inputOptions, { allowStream: false }), blend: 'over', tile: false, - left: -1, - top: -1, + left: 0, + top: 0, + hasOffset: false, gravity: 0, premultiplied: false }; @@ -125,21 +126,23 @@ function composite (images) { } } if (is.defined(image.left)) { - if (is.integer(image.left) && image.left >= 0) { + if (is.integer(image.left)) { composite.left = image.left; } else { - throw is.invalidParameterError('left', 'positive integer', image.left); + throw is.invalidParameterError('left', 'integer', image.left); } } if (is.defined(image.top)) { - if (is.integer(image.top) && image.top >= 0) { + if (is.integer(image.top)) { composite.top = image.top; } else { - throw is.invalidParameterError('top', 'positive integer', image.top); + throw is.invalidParameterError('top', 'integer', image.top); } } - if (composite.left !== composite.top && Math.min(composite.left, composite.top) === -1) { + if (is.defined(image.top) !== is.defined(image.left)) { throw new Error('Expected both left and top to be set'); + } else { + composite.hasOffset = is.integer(image.top) && is.integer(image.left); } if (is.defined(image.gravity)) { if (is.integer(image.gravity) && is.inRange(image.gravity, 0, 8)) { diff --git a/package.json b/package.json index f213f683..6c3d3bda 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,9 @@ "Roman Malieiev ", "Tomas Szabo ", "Robert O'Rourke ", - "Guillermo Alfonso Varela ChouciƱo " + "Guillermo Alfonso Varela ChouciƱo ", + "Christian Flintrup ", + "Manan Jadhav " ], "scripts": { "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)", diff --git a/src/common.cc b/src/common.cc index 5da9d400..9091a257 100644 --- a/src/common.cc +++ b/src/common.cc @@ -658,26 +658,18 @@ namespace sharp { int top = 0; // assign only if valid - if (x >= 0 && x < (inWidth - outWidth)) { + if (x < (inWidth - outWidth)) { left = x; } else if (x >= (inWidth - outWidth)) { left = inWidth - outWidth; } - if (y >= 0 && y < (inHeight - outHeight)) { + if (y < (inHeight - outHeight)) { top = y; } else if (y >= (inHeight - outHeight)) { top = inHeight - outHeight; } - // the resulting left and top could have been outside the image after calculation from bottom/right edges - if (left < 0) { - left = 0; - } - if (top < 0) { - top = 0; - } - return std::make_tuple(left, top); } diff --git a/src/pipeline.cc b/src/pipeline.cc index 55af804c..fdb4856b 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -570,7 +570,7 @@ class PipelineWorker : public Napi::AsyncWorker { int left; int top; compositeImage = compositeImage.replicate(across, down); - if (composite->left >= 0 && composite->top >= 0) { + if (composite->hasOffset) { std::tie(left, top) = sharp::CalculateCrop( compositeImage.width(), compositeImage.height(), image.width(), image.height(), composite->left, composite->top); @@ -592,7 +592,7 @@ class PipelineWorker : public Napi::AsyncWorker { // Calculate position int left; int top; - if (composite->left >= 0 && composite->top >= 0) { + if (composite->hasOffset) { // Composite image at given offsets std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(), compositeImage.width(), compositeImage.height(), composite->left, composite->top); @@ -1253,6 +1253,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) { composite->gravity = sharp::AttrAsUint32(compositeObject, "gravity"); composite->left = sharp::AttrAsInt32(compositeObject, "left"); composite->top = sharp::AttrAsInt32(compositeObject, "top"); + composite->hasOffset = sharp::AttrAsBool(compositeObject, "hasOffset"); composite->tile = sharp::AttrAsBool(compositeObject, "tile"); composite->premultiplied = sharp::AttrAsBool(compositeObject, "premultiplied"); baton->composite.push_back(composite); diff --git a/src/pipeline.h b/src/pipeline.h index 1ef5b05e..135552df 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -40,6 +40,7 @@ struct Composite { int gravity; int left; int top; + bool hasOffset; bool tile; bool premultiplied; @@ -47,8 +48,9 @@ struct Composite { input(nullptr), mode(VIPS_BLEND_MODE_OVER), gravity(0), - left(-1), - top(-1), + left(0), + top(0), + hasOffset(false), tile(false), premultiplied(false) {} }; diff --git a/test/fixtures/expected/overlay-negative-offset-with-gravity.jpg b/test/fixtures/expected/overlay-negative-offset-with-gravity.jpg new file mode 100644 index 0000000000000000000000000000000000000000..06e585e2b65a02b46eef15d08a10c4684027fe2d GIT binary patch literal 24425 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 SDpV7mg#MM4l^hXDO#j*Q&m>a- literal 0 HcmV?d00001 diff --git a/test/unit/composite.js b/test/unit/composite.js index d7e50e56..959ef4aa 100644 --- a/test/unit/composite.js +++ b/test/unit/composite.js @@ -172,6 +172,24 @@ describe('composite', () => { }); }); + it('negative offset and gravity', done => { + sharp(fixtures.inputJpg) + .resize(400) + .composite([{ + input: fixtures.inputPngWithTransparency16bit, + left: -10, + top: -10, + gravity: 4 + }]) + .toBuffer((err, data, info) => { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + assert.strictEqual(3, info.channels); + fixtures.assertSimilar( + fixtures.expected('overlay-negative-offset-with-gravity.jpg'), data, done); + }); + }); + it('offset, gravity and tile', done => { sharp(fixtures.inputJpg) .resize(80) @@ -333,13 +351,25 @@ describe('composite', () => { it('invalid left', () => { assert.throws(() => { sharp().composite([{ input: 'test', left: 0.5 }]); - }, /Expected positive integer for left but received 0.5 of type number/); + }, /Expected integer for left but received 0.5 of type number/); + assert.throws(() => { + sharp().composite([{ input: 'test', left: 'invalid' }]); + }, /Expected integer for left but received invalid of type string/); + assert.throws(() => { + sharp().composite([{ input: 'test', left: 'invalid', top: 10 }]); + }, /Expected integer for left but received invalid of type string/); }); it('invalid top', () => { assert.throws(() => { - sharp().composite([{ input: 'test', top: -1 }]); - }, /Expected positive integer for top but received -1 of type number/); + sharp().composite([{ input: 'test', top: 0.5 }]); + }, /Expected integer for top but received 0.5 of type number/); + assert.throws(() => { + sharp().composite([{ input: 'test', top: 'invalid' }]); + }, /Expected integer for top but received invalid of type string/); + assert.throws(() => { + sharp().composite([{ input: 'test', top: 'invalid', left: 10 }]); + }, /Expected integer for top but received invalid of type string/); }); it('left but no top', () => {