diff --git a/docs/api-resize.md b/docs/api-resize.md index a9a76323..6c76a6f6 100644 --- a/docs/api-resize.md +++ b/docs/api-resize.md @@ -196,6 +196,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. + The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties. ### Parameters diff --git a/docs/changelog.md b/docs/changelog.md index 886b4936..a0803a72 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,9 @@ Requires libvips v8.8.1. #### v0.23.3 - TBD +* Ensure `trim` operation supports images contained in the alpha channel. + [#1597](https://github.com/lovell/sharp/issues/1597) + * Ensure tile `overlap` option works as expected. [#1921](https://github.com/lovell/sharp/pull/1921) [@rustyguts](https://github.com/rustyguts) diff --git a/lib/resize.js b/lib/resize.js index 93f0ca15..0aafd5af 100644 --- a/lib/resize.js +++ b/lib/resize.js @@ -367,7 +367,10 @@ 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. + * * The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties. + * * @param {Number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero. * @returns {Sharp} * @throws {Error} Invalid parameters diff --git a/src/operations.cc b/src/operations.cc index 9f722971..a38d3162 100644 --- a/src/operations.cc +++ b/src/operations.cc @@ -266,12 +266,22 @@ namespace sharp { if (HasAlpha(background)) { background = background.flatten(); } - int top, width, height; - int const left = image.find_trim(&top, &width, &height, VImage::option() + int left, top, width, height; + left = image.find_trim(&top, &width, &height, VImage::option() ->set("background", background(0, 0)) ->set("threshold", threshold)); if (width == 0 || height == 0) { - throw VError("Unexpected error while trimming. Try to lower the tolerance"); + if (HasAlpha(image)) { + // Search alpha channel + VImage alpha = image[image.bands() - 1]; + VImage backgroundAlpha = alpha.extract_area(0, 0, 1, 1); + left = alpha.find_trim(&top, &width, &height, VImage::option() + ->set("background", backgroundAlpha(0, 0)) + ->set("threshold", threshold)); + } + if (width == 0 || height == 0) { + throw VError("Unexpected error while trimming. Try to lower the tolerance"); + } } return image.extract_area(left, top, width, height); } diff --git a/test/fixtures/image-in-alpha.png b/test/fixtures/image-in-alpha.png new file mode 100644 index 00000000..f58ca10c Binary files /dev/null and b/test/fixtures/image-in-alpha.png differ diff --git a/test/fixtures/index.js b/test/fixtures/index.js index 7a7dadb8..1fdd6338 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -87,6 +87,7 @@ module.exports = { inputPngTruncated: getPath('truncated.png'), // gm convert 2569067123_aca715a2ee_o.jpg -resize 320x240 saw.png ; head -c 10000 saw.png > truncated.png inputPngEmbed: getPath('embedgravitybird.png'), // Released to sharp under a CC BY 4.0 inputPngRGBWithAlpha: getPath('2569067123_aca715a2ee_o.png'), // http://www.flickr.com/photos/grizdave/2569067123/ (same as inputJpg) + inputPngImageInAlpha: getPath('image-in-alpha.png'), // https://github.com/lovell/sharp/issues/1597 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 diff --git a/test/unit/trim.js b/test/unit/trim.js index ef6c6f27..118876b9 100644 --- a/test/unit/trim.js +++ b/test/unit/trim.js @@ -41,6 +41,21 @@ describe('Trim borders', function () { }); }); + it('single colour PNG where alpha channel provides the image', () => + sharp(fixtures.inputPngImageInAlpha) + .trim() + .toBuffer({ resolveWithObject: true }) + .then(({ data, info }) => { + assert.strictEqual(true, data.length > 0); + assert.strictEqual('png', info.format); + assert.strictEqual(916, info.width); + assert.strictEqual(137, info.height); + assert.strictEqual(4, info.channels); + assert.strictEqual(-6, info.trimOffsetLeft); + assert.strictEqual(-20, info.trimOffsetTop); + }) + ); + it('16-bit PNG with alpha channel', function (done) { sharp(fixtures.inputPngWithTransparency16bit) .resize(32, 32)