From f950294f705638c5a9e72839fd30f1a3e9227567 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Thu, 3 Mar 2016 09:18:11 +0000 Subject: [PATCH] Add ability to extend (pad) the edges of an image --- docs/api.md | 21 ++++++++- docs/changelog.md | 4 ++ index.js | 30 +++++++++++++ src/pipeline.cc | 25 +++++++++++ src/pipeline.h | 8 ++++ test/fixtures/expected/extend-equal.jpg | Bin 0 -> 4195 bytes test/fixtures/expected/extend-unequal.png | Bin 0 -> 18100 bytes test/unit/extend.js | 52 ++++++++++++++++++++++ 8 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/expected/extend-equal.jpg create mode 100644 test/fixtures/expected/extend-unequal.png create mode 100644 test/unit/extend.js diff --git a/docs/api.md b/docs/api.md index 47ccf865..b8641e6f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -280,7 +280,7 @@ sharp(input) #### background(rgba) -Set the background for the `embed` and `flatten` operations. +Set the background for the `embed`, `flatten` and `extend` operations. `rgba` is parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha. @@ -292,6 +292,25 @@ The default background is `{r: 0, g: 0, b: 0, a: 1}`, black without transparency Merge alpha transparency channel, if any, with `background`. +#### extend(extension) + +Extends/pads the edges of the image with `background`, where `extension` is one of: + +* a Number representing the pixel count to add to each edge, or +* an Object containing `top`, `left`, `bottom` and `right` attributes, each a Number of pixels to add to that edge. + +This operation will always occur after resizing and extraction, if any. + +```javascript +// Resize to 140 pixels wide, then add 10 transparent pixels +// to the top, left and right edges and 20 to the bottom edge +sharp(input) + .resize(140) + .background({r: 0, g: 0, b: 0, a: 0}) + .extend({top: 10, bottom: 20, left: 10, right: 10}) + ... +``` + #### negate() Produces the "negative" of the image. White => Black, Black => White, Blue => Yellow, etc. diff --git a/docs/changelog.md b/docs/changelog.md index b8678b41..60b8cac9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,10 @@ ### v0.14 - "*needle*" +* Add ability to extend (pad) the edges of an image. + [#128](https://github.com/lovell/sharp/issues/128) + [@blowsie](https://github.com/blowsie) + * Improvements to overlayWith: differing sizes/formats, gravity, buffer input. [#239](https://github.com/lovell/sharp/issues/239) [@chrisriley](https://github.com/chrisriley) diff --git a/index.js b/index.js index 39a3aa60..265ad690 100644 --- a/index.js +++ b/index.js @@ -69,6 +69,10 @@ var Sharp = function(input, options) { rotateBeforePreExtract: false, flip: false, flop: false, + extendTop: 0, + extendBottom: 0, + extendLeft: 0, + extendRight: 0, withoutEnlargement: false, interpolator: 'bicubic', // operations @@ -650,6 +654,32 @@ Sharp.prototype.tile = function(size, overlap) { return this; }; +/* + Extend edges +*/ +Sharp.prototype.extend = function(extend) { + if (isInteger(extend) && extend > 0) { + this.options.extendTop = extend; + this.options.extendBottom = extend; + this.options.extendLeft = extend; + this.options.extendRight = extend; + } else if ( + isObject(extend) && + isInteger(extend.top) && extend.top >= 0 && + isInteger(extend.bottom) && extend.bottom >= 0 && + isInteger(extend.left) && extend.left >= 0 && + isInteger(extend.right) && extend.right >= 0 + ) { + this.options.extendTop = extend.top; + this.options.extendBottom = extend.bottom; + this.options.extendLeft = extend.left; + this.options.extendRight = extend.right; + } else { + throw new Error('Invalid edge extension ' + extend); + } + return this; +}; + Sharp.prototype.resize = function(width, height) { if (!width) { this.options.width = -1; diff --git a/src/pipeline.cc b/src/pipeline.cc index df5770e6..f153559f 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -522,6 +522,27 @@ class PipelineWorker : public AsyncWorker { ); } + // Extend edges + if (baton->extendTop > 0 || baton->extendBottom > 0 || baton->extendLeft > 0 || baton->extendRight > 0) { + // Scale up 8-bit values to match 16-bit input image + const double multiplier = (image.interpretation() == VIPS_INTERPRETATION_RGB16) ? 256.0 : 1.0; + // Create background colour + std::vector background { + baton->background[0] * multiplier, + baton->background[1] * multiplier, + baton->background[2] * multiplier + }; + // Add alpha channel to background colour + if (HasAlpha(image)) { + background.push_back(baton->background[3] * multiplier); + } + // Embed + baton->width = image.width() + baton->extendLeft + baton->extendRight; + baton->height = image.height() + baton->extendTop + baton->extendBottom; + image = image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height, + VImage::option()->set("extend", VIPS_EXTEND_BACKGROUND)->set("background", background)); + } + // Threshold - must happen before blurring, due to the utility of blurring after thresholding if (shouldThreshold) { image = image.colourspace(VIPS_INTERPRETATION_B_W) >= baton->threshold; @@ -991,6 +1012,10 @@ NAN_METHOD(pipeline) { baton->rotateBeforePreExtract = attrAs(options, "rotateBeforePreExtract"); baton->flip = attrAs(options, "flip"); baton->flop = attrAs(options, "flop"); + baton->extendTop = attrAs(options, "extendTop"); + baton->extendBottom = attrAs(options, "extendBottom"); + baton->extendLeft = attrAs(options, "extendLeft"); + baton->extendRight = attrAs(options, "extendRight"); // Output options baton->progressive = attrAs(options, "progressive"); baton->quality = attrAs(options, "quality"); diff --git a/src/pipeline.h b/src/pipeline.h index a5778a29..ae024614 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -60,6 +60,10 @@ struct PipelineBaton { bool rotateBeforePreExtract; bool flip; bool flop; + int extendTop; + int extendBottom; + int extendLeft; + int extendRight; bool progressive; bool withoutEnlargement; VipsAccess accessMethod; @@ -106,6 +110,10 @@ struct PipelineBaton { angle(0), flip(false), flop(false), + extendTop(0), + extendBottom(0), + extendLeft(0), + extendRight(0), progressive(false), withoutEnlargement(false), quality(80), diff --git a/test/fixtures/expected/extend-equal.jpg b/test/fixtures/expected/extend-equal.jpg new file mode 100644 index 0000000000000000000000000000000000000000..800f32e968c91cc2cd9d639cadc0bffa6b39715d GIT binary patch literal 4195 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 literal 0 HcmV?d00001 diff --git a/test/fixtures/expected/extend-unequal.png b/test/fixtures/expected/extend-unequal.png new file mode 100644 index 0000000000000000000000000000000000000000..1454f01644032dd339e5ad9b72cc5ddf80affeb1 GIT binary patch literal 18100 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 QRsaA107*qoM6N<$f_ha5p#T5? literal 0 HcmV?d00001 diff --git a/test/unit/extend.js b/test/unit/extend.js new file mode 100644 index 00000000..2232512d --- /dev/null +++ b/test/unit/extend.js @@ -0,0 +1,52 @@ +'use strict'; + +var assert = require('assert'); + +var sharp = require('../../index'); +var fixtures = require('../fixtures'); + +describe('Extend', function () { + + it('extend all sides equally with RGB', function(done) { + sharp(fixtures.inputJpg) + .resize(120) + .background({r: 255, g: 0, b: 0}) + .extend(10) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(140, info.width); + assert.strictEqual(118, info.height); + fixtures.assertSimilar(fixtures.expected('extend-equal.jpg'), data, done); + }); + }); + + it('extend sides unequally with RGBA', function(done) { + sharp(fixtures.inputPngWithTransparency16bit) + .resize(120) + .background({r: 0, g: 0, b: 0, a: 0}) + .extend({top: 50, bottom: 0, left: 10, right: 35}) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(165, info.width); + assert.strictEqual(170, info.height); + fixtures.assertSimilar(fixtures.expected('extend-unequal.png'), data, done); + }); + }); + + it('missing parameter fails', function() { + assert.throws(function() { + sharp().extend(); + }); + }); + it('negative fails', function() { + assert.throws(function() { + sharp().extend(-1); + }); + }); + it('partial object fails', function() { + assert.throws(function() { + sharp().extend({top: 1}); + }); + }); + +});