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:

And the output should be

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.
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.