mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add coords to output when using attention based crop (#3470)
This commit is contained in:
parent
bdc50e1d6e
commit
6d404f4d2c
@ -61,6 +61,7 @@ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math
|
|||||||
* `info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
* `info` contains the output image `format`, `size` (bytes), `width`, `height`,
|
||||||
* `channels` and `premultiplied` (indicating if premultiplication was used).
|
* `channels` and `premultiplied` (indicating if premultiplication was used).
|
||||||
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
|
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
|
||||||
|
* When using attention as crop strategy also contains the center of the cropped region in the fields `attentionX` and `attentionY`.
|
||||||
* May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
|
* May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
|
||||||
* @returns {Promise<Object>} - when no callback is provided
|
* @returns {Promise<Object>} - when no callback is provided
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
|
@ -456,6 +456,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Gravity-based crop
|
// Gravity-based crop
|
||||||
int left;
|
int left;
|
||||||
int top;
|
int top;
|
||||||
|
|
||||||
std::tie(left, top) = sharp::CalculateCrop(
|
std::tie(left, top) = sharp::CalculateCrop(
|
||||||
inputWidth, inputHeight, baton->width, baton->height, baton->position);
|
inputWidth, inputHeight, baton->width, baton->height, baton->position);
|
||||||
int width = std::min(inputWidth, baton->width);
|
int width = std::min(inputWidth, baton->width);
|
||||||
@ -466,16 +467,25 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
left, top, width, height, nPages, &targetPageHeight)
|
left, top, width, height, nPages, &targetPageHeight)
|
||||||
: image.extract_area(left, top, width, height);
|
: image.extract_area(left, top, width, height);
|
||||||
} else {
|
} else {
|
||||||
|
int attention_x;
|
||||||
|
int attention_y;
|
||||||
|
|
||||||
// Attention-based or Entropy-based crop
|
// Attention-based or Entropy-based crop
|
||||||
MultiPageUnsupported(nPages, "Resize strategy");
|
MultiPageUnsupported(nPages, "Resize strategy");
|
||||||
image = image.tilecache(VImage::option()
|
image = image.tilecache(VImage::option()
|
||||||
->set("access", VIPS_ACCESS_RANDOM)
|
->set("access", VIPS_ACCESS_RANDOM)
|
||||||
->set("threaded", TRUE));
|
->set("threaded", TRUE));
|
||||||
|
|
||||||
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
||||||
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION));
|
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
||||||
|
->set("attention_x", &attention_x)
|
||||||
|
->set("attention_y", &attention_y));
|
||||||
baton->hasCropOffset = true;
|
baton->hasCropOffset = true;
|
||||||
baton->cropOffsetLeft = static_cast<int>(image.xoffset());
|
baton->cropOffsetLeft = static_cast<int>(image.xoffset());
|
||||||
baton->cropOffsetTop = static_cast<int>(image.yoffset());
|
baton->cropOffsetTop = static_cast<int>(image.yoffset());
|
||||||
|
baton->hasAttentionCenter = true;
|
||||||
|
baton->attentionX = static_cast<int>(attention_x * jpegShrinkOnLoad / scale);
|
||||||
|
baton->attentionY = static_cast<int>(attention_y * jpegShrinkOnLoad / scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1198,6 +1208,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
info.Set("cropOffsetLeft", static_cast<int32_t>(baton->cropOffsetLeft));
|
info.Set("cropOffsetLeft", static_cast<int32_t>(baton->cropOffsetLeft));
|
||||||
info.Set("cropOffsetTop", static_cast<int32_t>(baton->cropOffsetTop));
|
info.Set("cropOffsetTop", static_cast<int32_t>(baton->cropOffsetTop));
|
||||||
}
|
}
|
||||||
|
if (baton->hasAttentionCenter) {
|
||||||
|
info.Set("attentionX", static_cast<int32_t>(baton->attentionX));
|
||||||
|
info.Set("attentionY", static_cast<int32_t>(baton->attentionY));
|
||||||
|
}
|
||||||
if (baton->trimThreshold > 0.0) {
|
if (baton->trimThreshold > 0.0) {
|
||||||
info.Set("trimOffsetLeft", static_cast<int32_t>(baton->trimOffsetLeft));
|
info.Set("trimOffsetLeft", static_cast<int32_t>(baton->trimOffsetLeft));
|
||||||
info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
|
info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
|
||||||
|
@ -74,6 +74,9 @@ struct PipelineBaton {
|
|||||||
bool hasCropOffset;
|
bool hasCropOffset;
|
||||||
int cropOffsetLeft;
|
int cropOffsetLeft;
|
||||||
int cropOffsetTop;
|
int cropOffsetTop;
|
||||||
|
bool hasAttentionCenter;
|
||||||
|
int attentionX;
|
||||||
|
int attentionY;
|
||||||
bool premultiplied;
|
bool premultiplied;
|
||||||
bool tileCentre;
|
bool tileCentre;
|
||||||
bool fastShrinkOnLoad;
|
bool fastShrinkOnLoad;
|
||||||
@ -236,6 +239,9 @@ struct PipelineBaton {
|
|||||||
hasCropOffset(false),
|
hasCropOffset(false),
|
||||||
cropOffsetLeft(0),
|
cropOffsetLeft(0),
|
||||||
cropOffsetTop(0),
|
cropOffsetTop(0),
|
||||||
|
hasAttentionCenter(false),
|
||||||
|
attentionX(0),
|
||||||
|
attentionY(0),
|
||||||
premultiplied(false),
|
premultiplied(false),
|
||||||
tintA(128.0),
|
tintA(128.0),
|
||||||
tintB(128.0),
|
tintB(128.0),
|
||||||
|
BIN
test/fixtures/expected/crop-strategy.webp
vendored
Normal file
BIN
test/fixtures/expected/crop-strategy.webp
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
@ -376,6 +376,8 @@ describe('Resize fit=cover', function () {
|
|||||||
assert.strictEqual(320, info.height);
|
assert.strictEqual(320, info.height);
|
||||||
assert.strictEqual(-107, info.cropOffsetLeft);
|
assert.strictEqual(-107, info.cropOffsetLeft);
|
||||||
assert.strictEqual(0, info.cropOffsetTop);
|
assert.strictEqual(0, info.cropOffsetTop);
|
||||||
|
assert.strictEqual(588, info.attentionX);
|
||||||
|
assert.strictEqual(640, info.attentionY);
|
||||||
fixtures.assertSimilar(fixtures.expected('crop-strategy-attention.jpg'), data, done);
|
fixtures.assertSimilar(fixtures.expected('crop-strategy-attention.jpg'), data, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -394,10 +396,32 @@ describe('Resize fit=cover', function () {
|
|||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
assert.strictEqual(0, info.cropOffsetLeft);
|
assert.strictEqual(0, info.cropOffsetLeft);
|
||||||
assert.strictEqual(0, info.cropOffsetTop);
|
assert.strictEqual(0, info.cropOffsetTop);
|
||||||
|
assert.strictEqual(0, info.attentionX);
|
||||||
|
assert.strictEqual(0, info.attentionY);
|
||||||
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
|
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('WebP', function (done) {
|
||||||
|
sharp(fixtures.inputWebP)
|
||||||
|
.resize(320, 80, {
|
||||||
|
fit: 'cover',
|
||||||
|
position: sharp.strategy.attention
|
||||||
|
})
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('webp', info.format);
|
||||||
|
assert.strictEqual(3, info.channels);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(80, info.height);
|
||||||
|
assert.strictEqual(0, info.cropOffsetLeft);
|
||||||
|
assert.strictEqual(-161, info.cropOffsetTop);
|
||||||
|
assert.strictEqual(288, info.attentionX);
|
||||||
|
assert.strictEqual(745, info.attentionY);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('crop-strategy.webp'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('supports the strategy passed as a string', function (done) {
|
it('supports the strategy passed as a string', function (done) {
|
||||||
sharp(fixtures.inputPngWithTransparency)
|
sharp(fixtures.inputPngWithTransparency)
|
||||||
.resize(320, 80, {
|
.resize(320, 80, {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user