Skip to content

Commit 927b777

Browse files
AzureBytelovell
authored andcommitted
Add gravity support to embed feature (lovell#1038)
1 parent 1d7a0ea commit 927b777

28 files changed

Lines changed: 426 additions & 7 deletions

lib/constructor.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ const Sharp = function (input, options) {
136136
height: -1,
137137
canvas: 'crop',
138138
crop: 0,
139+
embed: 0,
139140
useExifOrientation: false,
140141
angle: 0,
141142
rotateBeforePreExtract: false,

lib/resize.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,26 @@ function crop (crop) {
179179
* // outputBuffer contains WebP image data of a 200 pixels wide and 300 pixels high
180180
* // containing a scaled version, embedded on a transparent canvas, of input.gif
181181
* });
182-
*
182+
* @param {String} [embed='centre'] - A member of `sharp.gravity` to embed to an edge/corner.
183183
* @returns {Sharp}
184+
* @throws {Error} Invalid parameters
184185
*/
185-
function embed () {
186+
function embed (embed) {
186187
this.options.canvas = 'embed';
188+
189+
if (!is.defined(embed)) {
190+
// Default
191+
this.options.embed = gravity.center;
192+
} else if (is.integer(embed) && is.inRange(embed, 0, 8)) {
193+
// Gravity (numeric)
194+
this.options.embed = embed;
195+
} else if (is.string(embed) && is.integer(gravity[embed])) {
196+
// Gravity (string)
197+
this.options.embed = gravity[embed];
198+
} else {
199+
throw is.invalidParameterError('embed', 'valid embed id/name', embed);
200+
}
201+
187202
return this;
188203
}
189204

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
"Nicolas Coden <nicolas@ncoden.fr>",
4141
"Matt Parrish <matt.r.parrish@gmail.com>",
4242
"Matthew McEachen <matthew+github@mceachen.org>",
43-
"Jarda Kotěšovec <jarda.kotesovec@gmail.com>"
43+
"Jarda Kotěšovec <jarda.kotesovec@gmail.com>",
44+
"Kenric D'Souza <kenric.dsouza@gmail.com>"
4445
],
4546
"scripts": {
4647
"clean": "rm -rf node_modules/ build/ vendor/ coverage/ test/fixtures/output.*",
@@ -66,6 +67,7 @@
6667
"resize",
6768
"thumbnail",
6869
"crop",
70+
"embed",
6971
"libvips",
7072
"vips"
7173
],

src/common.cc

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,62 @@ namespace sharp {
409409

410410
/*
411411
Calculate the (left, top) coordinates of the output image
412-
within the input image, applying the given gravity.
412+
within the input image, applying the given gravity during an embed.
413+
414+
@Azurebyte: We are basically swapping the inWidth and outWidth, inHeight and outHeight from the CalculateCrop function.
415+
*/
416+
std::tuple<int, int> CalculateEmbedPosition(int const inWidth, int const inHeight,
417+
int const outWidth, int const outHeight, int const gravity) {
418+
419+
int left = 0;
420+
int top = 0;
421+
switch (gravity) {
422+
case 1:
423+
// North
424+
left = (outWidth - inWidth) / 2;
425+
break;
426+
case 2:
427+
// East
428+
left = outWidth - inWidth;
429+
top = (outHeight - inHeight) / 2;
430+
break;
431+
case 3:
432+
// South
433+
left = (outWidth - inWidth) / 2;
434+
top = outHeight - inHeight;
435+
break;
436+
case 4:
437+
// West
438+
top = (outHeight - inHeight) / 2;
439+
break;
440+
case 5:
441+
// Northeast
442+
left = outWidth - inWidth;
443+
break;
444+
case 6:
445+
// Southeast
446+
left = outWidth - inWidth;
447+
top = outHeight - inHeight;
448+
break;
449+
case 7:
450+
// Southwest
451+
top = outHeight - inHeight;
452+
break;
453+
case 8:
454+
// Northwest
455+
// Which is the default is 0,0 so we do not assign anything here.
456+
break;
457+
default:
458+
// Centre
459+
left = (outWidth - inWidth) / 2;
460+
top = (outHeight - inHeight) / 2;
461+
}
462+
return std::make_tuple(left, top);
463+
}
464+
465+
/*
466+
Calculate the (left, top) coordinates of the output image
467+
within the input image, applying the given gravity during a crop.
413468
*/
414469
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
415470
int const outWidth, int const outHeight, int const gravity) {

src/common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,13 @@ namespace sharp {
206206
*/
207207
std::string VipsWarningPop();
208208

209+
/*
210+
Calculate the (left, top) coordinates of the output image
211+
within the input image, applying the given gravity during an embed.
212+
*/
213+
std::tuple<int, int> CalculateEmbedPosition(int const inWidth, int const inHeight,
214+
int const outWidth, int const outHeight, int const gravity);
215+
209216
/*
210217
Calculate the (left, top) coordinates of the output image
211218
within the input image, applying the given gravity.

src/pipeline.cc

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -446,12 +446,25 @@ class PipelineWorker : public Nan::AsyncWorker {
446446
image = image.bandjoin(
447447
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
448448
}
449+
449450
// Embed
450-
int left = static_cast<int>(round((baton->width - image.width()) / 2));
451-
int top = static_cast<int>(round((baton->height - image.height()) / 2));
452-
image = image.embed(left, top, baton->width, baton->height, VImage::option()
451+
452+
// Calculate where to position the embeded image if gravity specified, else center.
453+
int left;
454+
int top;
455+
456+
left = static_cast<int>(round((baton->width - image.width()) / 2));
457+
top = static_cast<int>(round((baton->height - image.height()) / 2));
458+
459+
int width = std::max(image.width(), baton->width);
460+
int height = std::max(image.height(), baton->height);
461+
std::tie(left, top) = sharp::CalculateEmbedPosition(
462+
image.width(), image.height(), baton->width, baton->height, baton->embed);
463+
464+
image = image.embed(left, top, width, height, VImage::option()
453465
->set("extend", VIPS_EXTEND_BACKGROUND)
454466
->set("background", background));
467+
455468
} else if (baton->canvas != Canvas::IGNORE_ASPECT) {
456469
// Crop/max/min
457470
if (baton->crop < 9) {
@@ -1141,6 +1154,7 @@ NAN_METHOD(pipeline) {
11411154
// Resize options
11421155
baton->withoutEnlargement = AttrTo<bool>(options, "withoutEnlargement");
11431156
baton->crop = AttrTo<int32_t>(options, "crop");
1157+
baton->embed = AttrTo<int32_t>(options, "embed");
11441158
baton->kernel = AttrAsStr(options, "kernel");
11451159
baton->fastShrinkOnLoad = AttrTo<bool>(options, "fastShrinkOnLoad");
11461160
// Join Channel Options

src/pipeline.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ struct PipelineBaton {
6262
int channels;
6363
Canvas canvas;
6464
int crop;
65+
int embed;
6566
bool hasCropOffset;
6667
int cropOffsetLeft;
6768
int cropOffsetTop;
@@ -145,6 +146,7 @@ struct PipelineBaton {
145146
channels(0),
146147
canvas(Canvas::CROP),
147148
crop(0),
149+
embed(0),
148150
hasCropOffset(false),
149151
cropOffsetLeft(0),
150152
cropOffsetTop(0),

test/fixtures/embedgravitybird.png

476 KB
Loading
78.4 KB
Loading
78.4 KB
Loading

0 commit comments

Comments
 (0)