diff --git a/index.js b/index.js index c8e5ad48..8cef1a59 100644 --- a/index.js +++ b/index.js @@ -92,6 +92,7 @@ var Sharp = function(input, options) { overlayFileIn: '', overlayBufferIn: null, overlayGravity: 0, + overlayTile: false, // output options formatOut: 'input', fileOut: '', @@ -354,6 +355,15 @@ Sharp.prototype.overlayWith = function(overlay, options) { throw new Error('Unsupported overlay ' + typeof overlay); } if (isObject(options)) { + if (typeof options.tile === 'undefined') { + this.options.overlayTile = false; + } + else if (isBoolean(options.tile)) { + this.options.overlayTile = options.tile; + } else { + throw new Error(' Invalid Value for tile ' + options.tile + 'Only Boolean Values allowed for overlay.tile.'); + } + if (isInteger(options.gravity) && inRange(options.gravity, 0, 8)) { this.options.overlayGravity = options.gravity; } else if (isString(options.gravity) && isInteger(module.exports.gravity[options.gravity])) { diff --git a/src/pipeline.cc b/src/pipeline.cc index 864555b8..d671bd62 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -664,6 +664,35 @@ class PipelineWorker : public AsyncWorker { if (overlayImageType == ImageType::UNKNOWN) { return Error(); } + // Check if overlay is tiled + if (baton->overlayTile) { + int overlayImageWidth = overlayImage.width(); + int overlayImageHeight = overlayImage.height(); + int across = 0; + int down = 0; + + // use gravity in ovelay + if(overlayImageWidth <= baton->width) { + across = static_cast(ceil(static_cast(baton->width) / overlayImageWidth)); + } + if(overlayImageHeight <= baton->height) { + down = static_cast(ceil(static_cast(baton->height) / overlayImageHeight)); + } + if(across != 0 || down != 0) { + int left; + int top; + overlayImage = overlayImage.replicate(across, down); + // the overlayGravity will now be used to CalculateCrop for extract_area + std::tie(left, top) = CalculateCrop( + overlayImage.width(), overlayImage.height(), baton->width, baton->height, baton->overlayGravity + ); + overlayImage = overlayImage.extract_area( + left, top, baton->width, baton->height + ); + } + // the overlayGravity was used for extract_area, therefore set it back to its default value of 0 + baton->overlayGravity = 0; + } // Ensure overlay is premultiplied sRGB overlayImage = overlayImage.colourspace(VIPS_INTERPRETATION_sRGB).premultiply(); // Composite images with given gravity @@ -1050,6 +1079,7 @@ NAN_METHOD(pipeline) { baton->overlayBufferIn = node::Buffer::Data(overlayBufferIn); } baton->overlayGravity = attrAs(options, "overlayGravity"); + baton->overlayTile = attrAs(options, "overlayTile"); // Resize options baton->withoutEnlargement = attrAs(options, "withoutEnlargement"); baton->crop = attrAs(options, "crop"); diff --git a/src/pipeline.h b/src/pipeline.h index 50674b15..f366af8d 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -33,6 +33,7 @@ struct PipelineBaton { char *overlayBufferIn; size_t overlayBufferInLength; int overlayGravity; + bool overlayTile; int topOffsetPre; int leftOffsetPre; int widthPre; @@ -97,6 +98,7 @@ struct PipelineBaton { bufferOutLength(0), overlayBufferInLength(0), overlayGravity(0), + overlayTile(false), topOffsetPre(-1), topOffsetPost(-1), channels(0), diff --git a/test/fixtures/expected/overlay-tile-gravity-center.jpg b/test/fixtures/expected/overlay-tile-gravity-center.jpg new file mode 100644 index 00000000..3f38623d Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-center.jpg differ diff --git a/test/fixtures/expected/overlay-tile-gravity-centre.jpg b/test/fixtures/expected/overlay-tile-gravity-centre.jpg new file mode 100644 index 00000000..3f38623d Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-centre.jpg differ diff --git a/test/fixtures/expected/overlay-tile-gravity-east.jpg b/test/fixtures/expected/overlay-tile-gravity-east.jpg new file mode 100644 index 00000000..af8d3b59 Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-east.jpg differ diff --git a/test/fixtures/expected/overlay-tile-gravity-north.jpg b/test/fixtures/expected/overlay-tile-gravity-north.jpg new file mode 100644 index 00000000..82d47bd2 Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-north.jpg differ diff --git a/test/fixtures/expected/overlay-tile-gravity-northeast.jpg b/test/fixtures/expected/overlay-tile-gravity-northeast.jpg new file mode 100644 index 00000000..6eff5f9c Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-northeast.jpg differ diff --git a/test/fixtures/expected/overlay-tile-gravity-northwest.jpg b/test/fixtures/expected/overlay-tile-gravity-northwest.jpg new file mode 100644 index 00000000..a0f6e9d0 Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-northwest.jpg differ diff --git a/test/fixtures/expected/overlay-tile-gravity-south.jpg b/test/fixtures/expected/overlay-tile-gravity-south.jpg new file mode 100644 index 00000000..b0393d65 Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-south.jpg differ diff --git a/test/fixtures/expected/overlay-tile-gravity-southeast.jpg b/test/fixtures/expected/overlay-tile-gravity-southeast.jpg new file mode 100644 index 00000000..21aa2b1e Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-southeast.jpg differ diff --git a/test/fixtures/expected/overlay-tile-gravity-southwest.jpg b/test/fixtures/expected/overlay-tile-gravity-southwest.jpg new file mode 100644 index 00000000..e71ff07a Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-southwest.jpg differ diff --git a/test/fixtures/expected/overlay-tile-gravity-west.jpg b/test/fixtures/expected/overlay-tile-gravity-west.jpg new file mode 100644 index 00000000..2db832ac Binary files /dev/null and b/test/fixtures/expected/overlay-tile-gravity-west.jpg differ diff --git a/test/fixtures/expected/overlay-tile-rotated90-gravity-northwest.jpg b/test/fixtures/expected/overlay-tile-rotated90-gravity-northwest.jpg new file mode 100644 index 00000000..ad0e18ad Binary files /dev/null and b/test/fixtures/expected/overlay-tile-rotated90-gravity-northwest.jpg differ diff --git a/test/fixtures/expected/overlay-tile-rotated90.jpg b/test/fixtures/expected/overlay-tile-rotated90.jpg new file mode 100644 index 00000000..9d9c2171 Binary files /dev/null and b/test/fixtures/expected/overlay-tile-rotated90.jpg differ diff --git a/test/unit/overlay.js b/test/unit/overlay.js index 36a6f2a2..dec3ff02 100644 --- a/test/unit/overlay.js +++ b/test/unit/overlay.js @@ -242,4 +242,65 @@ describe('Overlays', function() { }); }); + describe('Overlay with tile enabled and gravity', function() { + Object.keys(sharp.gravity).forEach(function(gravity) { + it(gravity, function(done) { + var expected = fixtures.expected('overlay-tile-gravity-' + gravity + '.jpg'); + sharp(fixtures.inputJpg) + .resize(80) + .overlayWith(fixtures.inputPngWithTransparency16bit, { + tile: true, + gravity: gravity + }) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + assert.strictEqual(80, info.width); + assert.strictEqual(65, info.height); + assert.strictEqual(3, info.channels); + fixtures.assertSimilar(expected, data, done); + }); + }); + }); + }); + + + it('With tile enabled and image rotated 90 degrees', function(done) { + var expected = fixtures.expected('overlay-tile-rotated90.jpg'); + sharp(fixtures.inputJpg) + .rotate(90) + .resize(80) + .overlayWith(fixtures.inputPngWithTransparency16bit, { + tile: true + }) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + assert.strictEqual(80, info.width); + assert.strictEqual(98, info.height); + assert.strictEqual(3, info.channels); + fixtures.assertSimilar(expected, data, done); + }); + }); + + + it('With tile enabled and image rotated 90 degrees and gravity northwest', function(done) { + var expected = fixtures.expected('overlay-tile-rotated90-gravity-northwest.jpg'); + sharp(fixtures.inputJpg) + .rotate(90) + .resize(80) + .overlayWith(fixtures.inputPngWithTransparency16bit, { + tile: true, + gravity: 'northwest' + }) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + assert.strictEqual(80, info.width); + assert.strictEqual(98, info.height); + assert.strictEqual(3, info.channels); + fixtures.assertSimilar(expected, data, done); + }); + }); + });