Hi,
I've just discovered sharp. I'm eternally grateful!
I'm trying to overlay many images onto a single image in order to create an image map.
I've gotten this far...
sharp({
create: {
width: 512,
height: 512,
channels: 4,
background: {
r: 255,
g: 255,
b: 255,
alpha: 255
}
}
}).png()
.overlayWith(grassImagePath, {
top: 0,
left: 0
})
.pipe(response)
...but na茂vely trying to overlay successive images...
sharp({
create: {
width: 512,
height: 512,
channels: 4,
background: {
r: 255,
g: 255,
b: 255,
alpha: 255
}
}
}).png()
.overlayWith(grassImagePath, {
top: 0,
left: 0
})
.overlayWith(concreteImagePath, {
top: 0,
left: 0
})
.pipe(response)
...does not seem to work.
I've found a way around this by using buffers:
sharp({
create: {
width: 512,
height: 512,
channels: 4,
background: {
r: 255,
g: 255,
b: 255,
alpha: 255
}
}
}).png()
.toBuffer()
.then(function(buffer) {
sharp(buffer)
.overlayWith(grassImagePath, {
top: 0,
left: 0
})
.toBuffer()
.then(function(buffer) {
sharp(buffer)
.overlayWith(concreteImagePath, {
top: 256,
left: 0
})
.pipe(response)
})
});
I can remove the pyramid with some asynchronous utility function, after all this will be working on an array of images, but I still feel that using buffers like this is wasteful.
So my question is: is there a way to apply successive overlays without resorting to this kind of trickery?
Hello, did you see #728 ?
Nope. I'll read it now, thank you.
Your comment on https://github.com/lovell/sharp/issues/405 seems to do things the same way I have done. Namely returning a buffer via a promise then constructing a fresh sharp instance with it.
I'm happy with this. I have (effectively):
const necessary = require('necessary');
const { asynchronousUtilities } = necessary,
{ whilst } = asynchronousUtilities;
function respond(response) {
createImageMap(function(buffer) {
const context = {
paths: paths,
buffer: buffer
};
whilst(overlayImageCallback, function() {
const { buffer } = context;
sharp(buffer).pipe(response);
}, context);
});
}
function createImageMap(callback) {
const width = 512,
height = 512,
channels = 4,
background = { r: 255, g: 255, b: 255, alpha: 255 },
options = {
width: width,
height: height,
channels: channels,
background: background
};
sharp({
create: options ///
})
.png()
.toBuffer()
.then(callback);
}
function overlayImageCallback(next, done, context, index) {
const { buffer, paths } = context,
pathsLength = paths.length,
lastIndex = pathsLength - 1;
if (index > lastIndex) {
done();
return;
}
const top = 0,
left = index * 256, /// nonsense obviously
options = {
top: top,
left: left
},
path = paths[index];
sharp(buffer)
.overlayWith(path, options)
.toBuffer()
.then(function(buffer) {
context.buffer = buffer; ///
next();
});
}
Once I've created the loop it's pretty tidy. I have to update the context with the fresh buffer every time (near the bottom) but apart from that it's fine.
Thanks for the help. Feel free to close!
I'll close it myself if that's okay since I'm here. Thanks again.
Until #405 is complete it makes for cleaner code to do the following and use await instead of a nasty promise chain. Then when the feature is completed you can just kill off those two lines.
sharpImage.overlayWith(.....);
var tempBuffer = await compressedImage.toBuffer({ resolveWithObject: true });
sharpImage = sharp(tempBuffer.data);
Most helpful comment
Your comment on https://github.com/lovell/sharp/issues/405 seems to do things the same way I have done. Namely returning a buffer via a promise then constructing a fresh
sharpinstance with it.I'm happy with this. I have (effectively):
Once I've created the loop it's pretty tidy. I have to update the context with the fresh buffer every time (near the bottom) but apart from that it's fine.
Thanks for the help. Feel free to close!