sharp icon indicating copy to clipboard operation
sharp copied to clipboard

Add settings for symmetric .trim()

Open Zirafnik opened this issue 10 months ago • 1 comments

Feature request

Currently the .trim() method trims each border as much as possible. If the image is asymmetric then the trimOffsets on each axis might differ (e.g. trimOffsetLeft !== trimOffsetRight or trimOffsetTop !== trimOffsetBottom).

In my use case, I wish to keep the image positioning the same when I center the new image over a larger canvas. If the image was asymmetric (and therefore so was the trim) it will be offset by the difference in the trimOffsets.

I wish for the option for symmetric trimming, which would trim the chosen axis only by the lesser of both sides' lengths.

Current workaround is to first calculate the trimOffsetRight and trimOffsetBottom which are not provided on the info object (see #4086). Then calculate the x-axis and y-axis difference in offsets. After getting these difference numbers, you have to re-add the "missing" border to the correct side, which was trimmed in excess.

This needs to happen before resizing, which forces you to save to buffer first, and then again read that new buffer into a new sharp instance for further processing.

// Calculate offsets
const data = await image.metadata();
const buffer1 = await image
    .trim()
    .toBuffer({ resolveWithObject: true }); // we get new data after .trim()

const info = buffer1.info;

const trimOffset = {
    left: Math.abs(info.trimOffsetLeft),
    right: data.width - info.width - Math.abs(info.trimOffsetLeft),
    top: Math.abs(info.trimOffsetTop),
    bottom: data.height - info.height - Math.abs(info.trimOffsetTop),
};

// Adjust for offset differences due to differently thick borders, so the final picture is centered
const xAxisDifference = trimOffset.left - trimOffset.right;
const yAxisDifference = trimOffset.top - trimOffset.bottom;

const adjustment = {
    left: xAxisDifference > 0 ? xAxisDifference : 0,
    right: xAxisDifference < 0 ? Math.abs(xAxisDifference) : 0,
    top: yAxisDifference > 0 ? yAxisDifference : 0,
    bottom: yAxisDifference < 0 ? Math.abs(yAxisDifference) : 0,
};

// Applying border adjustments so the picture is centered
// If we wish to extend before resizing, we need to save it to buffer first
const buffer2 = await sharp(buffer1.data)
    .extend({
         left: adjustment.left,
         right: adjustment.right,
         top: adjustment.top,
         bottom: adjustment.bottom,
         background: 'white',
     })
    .toBuffer();

// Other processing
const filePromise = sharp(buffer2)
        .resize({...})
        .extend({...})
        .composite([...])
        .sharpen()
        .toFile(filename);

Ideally, I propose 3 additional settings for .trim(), which would allow symmetric trimming:

  • symmetric: boolean: symmetric trim on both x-axis & y-axis
  • xSymmetric: boolean: symmetric trim on x-axis
  • ySymmetric: boolean: symmetric trim on y-axis

As proposed, it should trim the same amount on both sides of the axis, equal to the lesser of the two available sides.

See also: #4086

Zirafnik avatar Apr 26 '24 19:04 Zirafnik

This looks highly specific to a particular scenario and feels like it belongs in a consumer of sharp rather than sharp itself.

lovell avatar Apr 26 '24 22:04 lovell

I believe what we need is a trim, which does not re-center the image as the current trim does.

That is a quite common use case for graphics batch processing i.e. optimising website or video game sprites. You dont want the center of the image to shift in any way (anchor 0.5). You just want to trim the borders, but symmetrically so the center will not move. With the current trim i would have to re-center hundreds of images manually after trim, which is not feasible.

The api could look as:

trim({ center: true }) or trim({ symmetric: true })

fyyyyy avatar Aug 11 '24 18:08 fyyyyy

A similar function is here: cropSymmetric

https://www.npmjs.com/package/@jimp/plugin-crop#autocrop

fyyyyy avatar Aug 11 '24 18:08 fyyyyy

related https://github.com/lovell/sharp/issues/3696

fyyyyy avatar Aug 11 '24 18:08 fyyyyy