Sharp: Duplicate image and mirror

Created on 27 Mar 2019  路  11Comments  路  Source: lovell/sharp

I have a use case where my input image should cover 1/4 of the image to be generated.
The image is the top left corner, and should be flipped and rotated to top right, bottom right, and bottom left.
What I have is:
oneCorner

And the output should be
fourCorners

Is this something that can be achieved using sharp?
And if, I'd appreciate an example of how to.
I took a quick look at the docs, but didn't find exactly what I was searching for.

cookbook question

All 11 comments

Hi, did you see the flip and flop operations?

Yes, thank you, thats what I found aswell and thought that I'd probably need it. Putting it into a more general context, what is missing for me is: How to compose my input image into a new image where I duplicate that layer multiple times and apply operations on each of these copies. Thanks for your help!

Try an approach like the following (untested):

const { width, height, channels } = await sharp(topLeft).metadata();
const topRight = await sharp(topLeft).flip().toBuffer();
const bottomLeft = await sharp(topLeft).flop().toBuffer();
const bottomRight = await sharp(topLeft).flip().flop().toBuffer();

const result = await sharp({
  create: {
    width: width * 2,
    height: height * 2,
    channels,
    background: 'black'
  }
})
  .composite([{
    input: topLeft,
    gravity: 'northwest'
  }, {
    input topRight,
    gravity: 'northeast'
  }, {
    input: bottomLeft,
    gravity: 'southwest'
  }, {
    input: bottomRight,
    gravity: 'southeast'
  }])
  .toBuffer();

Great, exactly what i was searching for!
Will take this as a base, way easier for me to work upon that now.
Thank you

I guess I will need to use v0.22.0 as this is the new composite array input introduced there.

Does composite work with stream based input?
I`m struggling to get this to work.

const topLeft = await sharp();

// const { width, height, channels } = topLeft.metadata();
// Does not work, is undefined. However I dont rely on that information.

const topRight = await topLeft.flip();
const bottomLeft = await topLeft.flop();
const bottomRight = await topLeft.flip().flop();

return sharp({
create: {
    width: 400,
    height: 400,
    channels: 4,
    //background: { r: 255, g: 0, b: 0, alpha: 1 }
    background: "red"
}
})
.composite([{
    input: topLeft.toBuffer(),
    gravity: "northwest",
}, {
    input: topRight.toBuffer(),
    gravity: "northeast",
}, {
    input: bottomLeft.toBuffer(),
    gravity: "southwest",
}, {
    input: bottomRight.toBuffer(),
    gravity: "southeast",
}])
.toFormat(format)
.on('error', err => { throw new Error("error-sharp-resize-internal", err); });

This throws Error: Unsupported input object
I also tried leaving out toBuffer() on the input elements, it then throws:

Error: Expected width, height and channels for raw pixel input

Thefore I added this object to each image, right under gravity:

raw: {
    width: 200,
    height: 200,
    channels: 4
}

And I am back at the original error, Error: Unsupported input object

The three images topRight, bottomLeft and bottomRight are created properly.
When I return one of them instead, the image is stored properly, eg.:

return bottomRight
.resize({width: size, height: size, fit: sharp.fit[fit], withoutEnlargement: true})
.toFormat(format)
.on('error', err => { throw new ReactionError("error-sharp-resize-internal", err); });

works.

The composite() operation does not support Stream-based input. This is the documented behaviour.

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

images[].input (Buffer | String)? Buffer containing image data or String containing the path to an image file.

Thanks for your quick feedback. Would there be any workaround to achieve this? Is this a libvips limitation or just not implemented in sharp? I don't really have an idea about the complexity of this.

Or , would it possible to read my image into buffer during the stream? and to write out the result? Appareantly this is not what toBuffer does with streams

Perhaps use a module like get-stream to convert from Stream to Buffer.

Tried with both get-stream and concat-stream, without success.

I am working with a transform stream, but can only mutate each chunk, and have to run a callback.
As sharp`s compose feature appareantly does only work on Buffers my idea was to get everything into one Buffer and return the image from that, but didn't get that to work yet.

I am a bit lost with this.
From an inexperiened point of view, what I don't get is that I am able to resize directly on a stream and convert to different file formats with sharp, but can't achieve something like described in the intial post.

I could solve this without external dependencies.
And while this probably destroys most of the advantages of using a stream, it works great for my case.

I implemented a custom transform stream, where each transform pushes the chunk into an array and returns an empty string in the callback.

Then in the _flush function of that stream I run Buffer.concat on that error to have a Buffer that I can use with sharp.

Not sure why I have to use _flush as I'm running on Node 8 and expected _final to do this, but that did not return any data, and using _flush works.

Was this page helpful?
0 / 5 - 0 ratings