mirror of
https://github.com/lovell/sharp.git
synced 2025-07-11 03:20:13 +02:00
Add ability to extend (pad) the edges of an image
This commit is contained in:
parent
86815bc9c4
commit
f950294f70
21
docs/api.md
21
docs/api.md
@ -280,7 +280,7 @@ sharp(input)
|
|||||||
|
|
||||||
#### background(rgba)
|
#### 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.
|
`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`.
|
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()
|
#### negate()
|
||||||
|
|
||||||
Produces the "negative" of the image. White => Black, Black => White, Blue => Yellow, etc.
|
Produces the "negative" of the image. White => Black, Black => White, Blue => Yellow, etc.
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
### v0.14 - "*needle*"
|
### 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.
|
* Improvements to overlayWith: differing sizes/formats, gravity, buffer input.
|
||||||
[#239](https://github.com/lovell/sharp/issues/239)
|
[#239](https://github.com/lovell/sharp/issues/239)
|
||||||
[@chrisriley](https://github.com/chrisriley)
|
[@chrisriley](https://github.com/chrisriley)
|
||||||
|
30
index.js
30
index.js
@ -69,6 +69,10 @@ var Sharp = function(input, options) {
|
|||||||
rotateBeforePreExtract: false,
|
rotateBeforePreExtract: false,
|
||||||
flip: false,
|
flip: false,
|
||||||
flop: false,
|
flop: false,
|
||||||
|
extendTop: 0,
|
||||||
|
extendBottom: 0,
|
||||||
|
extendLeft: 0,
|
||||||
|
extendRight: 0,
|
||||||
withoutEnlargement: false,
|
withoutEnlargement: false,
|
||||||
interpolator: 'bicubic',
|
interpolator: 'bicubic',
|
||||||
// operations
|
// operations
|
||||||
@ -650,6 +654,32 @@ Sharp.prototype.tile = function(size, overlap) {
|
|||||||
return this;
|
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) {
|
Sharp.prototype.resize = function(width, height) {
|
||||||
if (!width) {
|
if (!width) {
|
||||||
this.options.width = -1;
|
this.options.width = -1;
|
||||||
|
@ -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
|
// Threshold - must happen before blurring, due to the utility of blurring after thresholding
|
||||||
if (shouldThreshold) {
|
if (shouldThreshold) {
|
||||||
image = image.colourspace(VIPS_INTERPRETATION_B_W) >= baton->threshold;
|
image = image.colourspace(VIPS_INTERPRETATION_B_W) >= baton->threshold;
|
||||||
@ -991,6 +1012,10 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->rotateBeforePreExtract = attrAs<bool>(options, "rotateBeforePreExtract");
|
baton->rotateBeforePreExtract = attrAs<bool>(options, "rotateBeforePreExtract");
|
||||||
baton->flip = attrAs<bool>(options, "flip");
|
baton->flip = attrAs<bool>(options, "flip");
|
||||||
baton->flop = attrAs<bool>(options, "flop");
|
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
|
// Output options
|
||||||
baton->progressive = attrAs<bool>(options, "progressive");
|
baton->progressive = attrAs<bool>(options, "progressive");
|
||||||
baton->quality = attrAs<int32_t>(options, "quality");
|
baton->quality = attrAs<int32_t>(options, "quality");
|
||||||
|
@ -60,6 +60,10 @@ struct PipelineBaton {
|
|||||||
bool rotateBeforePreExtract;
|
bool rotateBeforePreExtract;
|
||||||
bool flip;
|
bool flip;
|
||||||
bool flop;
|
bool flop;
|
||||||
|
int extendTop;
|
||||||
|
int extendBottom;
|
||||||
|
int extendLeft;
|
||||||
|
int extendRight;
|
||||||
bool progressive;
|
bool progressive;
|
||||||
bool withoutEnlargement;
|
bool withoutEnlargement;
|
||||||
VipsAccess accessMethod;
|
VipsAccess accessMethod;
|
||||||
@ -106,6 +110,10 @@ struct PipelineBaton {
|
|||||||
angle(0),
|
angle(0),
|
||||||
flip(false),
|
flip(false),
|
||||||
flop(false),
|
flop(false),
|
||||||
|
extendTop(0),
|
||||||
|
extendBottom(0),
|
||||||
|
extendLeft(0),
|
||||||
|
extendRight(0),
|
||||||
progressive(false),
|
progressive(false),
|
||||||
withoutEnlargement(false),
|
withoutEnlargement(false),
|
||||||
quality(80),
|
quality(80),
|
||||||
|
BIN
test/fixtures/expected/extend-equal.jpg
vendored
Normal file
BIN
test/fixtures/expected/extend-equal.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
test/fixtures/expected/extend-unequal.png
vendored
Normal file
BIN
test/fixtures/expected/extend-unequal.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
52
test/unit/extend.js
Normal file
52
test/unit/extend.js
Normal file
@ -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});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user