sharp icon indicating copy to clipboard operation
sharp copied to clipboard

Enhancement: add support to extend feature for width and height

Open michalkvasnicak opened this issue 7 years ago • 9 comments

Hello,

is there a way to pad an image if it's smaller than required dimensions?

For example:

desired dimensions actual dimensions expected result
1000x1000 200x200 1000x1000 image with 200x200 centered inside
300x300 350x350 downscaled to 300x300

Original original

Desired output large

michalkvasnicak avatar Dec 19 '17 17:12 michalkvasnicak

I ended up with something like this:

async function resize(width, height) {
  const image = sharp(imgBuffer).resize(width, height).withoutEnlargement();

  const { info, data } = await image.jpeg({ quality: 100 }).toBuffer({ resolveWithObject: true });

  if (info.width < width || info.height < height) {
    const bg = sharp({
      create: {
        height,
        width,
        background: 'white',
      },
    });

    bg.overlayWith(data);

    return bg.jpeg({ quality: 100 }).toBuffer();
  }

  return data;
}

But could this be achieved with the Sharp's api?

michalkvasnicak avatar Dec 19 '17 18:12 michalkvasnicak

Hello, did you see the extend operation?

lovell avatar Dec 19 '17 18:12 lovell

@lovell yeah I saw it, but in order to use extend I need to know dimensions of image after resize so I can calculate params for extend. So at the end I will have to calculate the exact pixels to add to the sides.

Maybe something like extend with desired width and height could be great. So my question stays the same, could this be achieved with the sharp's api ? :)

Maybe something like this:

image.resize(width, height).withoutEnlargement().extend({ width, height })

michalkvasnicak avatar Dec 19 '17 18:12 michalkvasnicak

Providing target dimensions to extend is not currently supported but would make a good addition, thanks for the suggestion. PRs always welcome.

For now two pipelines are required to achieve what you want. You could use raw pixel data to avoid the compression/decompression round-trip in the middle.

lovell avatar Dec 19 '17 19:12 lovell

@lovell Thanks for suggesting raw but when I replaced jpeg({ quality: 100 }) in the middle with raw() I got the error Input buffer contains unsupported image format.

Here is a failing test code I used:

import sharp from 'sharp';
import { resolve as resolvePath } from 'path';
import { readFileSync } from 'fs';

describe('test', () => {
  const width = 1000;
  const height = 1000;

  it('works with buffer from an image file', async () => {
    const img = readFileSync(resolvePath(__dirname, '../../__tests__/fixture.jpeg'));

    const buf = await sharp(img)
      .resize(width, height)
      .withoutEnlargement()
      .raw()
      .toBuffer();

    await sharp({ create: { width, height, background: 'white', channels: 3 } })
      .overlayWith(buf)
      .jpeg({ quality: 100 })
      .toBuffer();
  });

  it('works with an output from another sharp instance', async () => {
    const buf = await sharp({ create: { width, height, background: 'white', channels: 4 } })
      .raw()
      .toBuffer();

    await sharp({ create: { width, height, background: 'white', channels: 4 } })
      .overlayWith(buf)
      .jpeg({ quality: 100 })
      .toBuffer();
  });
});

When I replace raw with jpeg in test, it passes just fine.

michalkvasnicak avatar Dec 19 '17 19:12 michalkvasnicak

Raw pixel data Buffer objects need their dimensions and channel count described, something like:

-      .overlayWith(buf)
+      .overlayWith(buf, { raw: { width, height, channels: 3 }})

https://sharp.pixelplumbing.com/en/stable/api-composite/#overlaywith

lovell avatar Dec 20 '17 10:12 lovell

@lovell oh sorry I completely oversaw this, thank you very much. Great library :)

michalkvasnicak avatar Dec 20 '17 11:12 michalkvasnicak

Any word on this enhancement being merged in?

schematis avatar Dec 11 '19 21:12 schematis

@schematis Happy to accept a PR if you're able.

lovell avatar Dec 12 '19 10:12 lovell