Use bounding box of alpha+non-alpha for trim op #2166

This commit is contained in:
Lovell Fuller 2022-07-05 18:19:17 +01:00
parent e3cab7f10f
commit e0d3c6e05d
8 changed files with 44 additions and 14 deletions

View File

@ -236,7 +236,8 @@ Returns **Sharp**
## trim
Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
will contain `trimOffsetLeft` and `trimOffsetTop` properties.

View File

@ -8,6 +8,9 @@ Requires libvips v8.13.0
* Drop support for Node.js 12, now requires Node.js >= 14.15.0.
* Use combined bounding box of alpha and non-alpha channels for `trim` operation.
[#2166](https://github.com/lovell/sharp/issues/2166)
* Re-introduce support for greyscale ICC profiles (temporarily removed in 0.30.2).
[#3114](https://github.com/lovell/sharp/issues/3114)

File diff suppressed because one or more lines are too long

View File

@ -430,7 +430,8 @@ function extract (options) {
/**
* Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
* Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
*
* Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
*
* The `info` response Object, obtained from callback of `.toFile()` or `.toBuffer()`,
* will contain `trimOffsetLeft` and `trimOffsetTop` properties.

View File

@ -275,19 +275,31 @@ namespace sharp {
left = image.find_trim(&top, &width, &height, VImage::option()
->set("background", background(0, 0))
->set("threshold", threshold));
if (width == 0 || height == 0) {
if (HasAlpha(image)) {
// Search alpha channel
// Search alpha channel (A)
int leftA, topA, widthA, heightA;
VImage alpha = image[image.bands() - 1];
VImage backgroundAlpha = alpha.extract_area(0, 0, 1, 1);
left = alpha.find_trim(&top, &width, &height, VImage::option()
leftA = alpha.find_trim(&topA, &widthA, &heightA, VImage::option()
->set("background", backgroundAlpha(0, 0))
->set("threshold", threshold));
if (widthA > 0 && heightA > 0) {
if (width > 0 && height > 0) {
// Combined bounding box (B)
int const leftB = std::min(left, leftA);
int const topB = std::min(top, topA);
int const widthB = std::max(left + width, leftA + widthA) - leftB;
int const heightB = std::max(top + height, topA + heightA) - topB;
return image.extract_area(leftB, topB, widthB, heightB);
} else {
// Use alpha only
return image.extract_area(leftA, topA, widthA, heightA);
}
}
}
if (width == 0 || height == 0) {
throw VError("Unexpected error while trimming. Try to lower the tolerance");
}
}
return image.extract_area(left, top, width, height);
}

View File

@ -94,6 +94,7 @@ module.exports = {
inputPngSolidAlpha: getPath('with-alpha.png'), // https://github.com/lovell/sharp/issues/1599
inputPngP3: getPath('p3.png'), // https://github.com/lovell/sharp/issues/2862
inputPngPalette: getPath('swiss.png'), // https://github.com/randy408/libspng/issues/188
inputPngTrimIncludeAlpha: getPath('trim-mc.png'), // https://github.com/lovell/sharp/issues/2166
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp

BIN
test/fixtures/trim-mc.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -128,6 +128,18 @@ describe('Trim borders', function () {
)
);
it('Ensure trim uses bounding box of alpha and non-alpha channels', async () => {
const { info } = await sharp(fixtures.inputPngTrimIncludeAlpha)
.trim()
.toBuffer({ resolveWithObject: true });
const { width, height, trimOffsetTop, trimOffsetLeft } = info;
assert.strictEqual(width, 179);
assert.strictEqual(height, 123);
assert.strictEqual(trimOffsetTop, -44);
assert.strictEqual(trimOffsetLeft, -13);
});
describe('Invalid thresholds', function () {
[-1, 'fail', {}].forEach(function (threshold) {
it(JSON.stringify(threshold), function () {