Skip to content

Commit

Permalink
Add ability to extend (pad) the edges of an image
Browse files Browse the repository at this point in the history
  • Loading branch information
lovell committed Mar 3, 2016
1 parent 86815bc commit f950294
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 1 deletion.
21 changes: 20 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
25 changes: 25 additions & 0 deletions src/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<double> 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;
Expand Down Expand Up @@ -991,6 +1012,10 @@ NAN_METHOD(pipeline) {
baton->rotateBeforePreExtract = attrAs<bool>(options, "rotateBeforePreExtract");
baton->flip = attrAs<bool>(options, "flip");
baton->flop = attrAs<bool>(options, "flop");
baton->extendTop = attrAs<int32_t>(options, "extendTop");
baton->extendBottom = attrAs<int32_t>(options, "extendBottom");
baton->extendLeft = attrAs<int32_t>(options, "extendLeft");
baton->extendRight = attrAs<int32_t>(options, "extendRight");
// Output options
baton->progressive = attrAs<bool>(options, "progressive");
baton->quality = attrAs<int32_t>(options, "quality");
Expand Down
8 changes: 8 additions & 0 deletions src/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand Down
Binary file added test/fixtures/expected/extend-equal.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/expected/extend-unequal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 52 additions & 0 deletions test/unit/extend.js
Original file line number Diff line number Diff line change
@@ -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});
});
});

});

0 comments on commit f950294

Please sign in to comment.