New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Easier way to composite 3+ layers #405

MPSinclair opened this Issue Apr 10, 2016 · 5 comments


3 participants

MPSinclair commented Apr 10, 2016

Sorry about digging up the old thread. After looking at that I went poking around the tests where I found "Composite three transparent PNGs into one".

What I was hoping to do was be able to chain overlayWith together like this:
etc. as necessary.

From what I can tell though, I need to nest them in callbacks like in the test?

Essentially what I am dealing with is a dynamic number of image layers that can be composited into a single image. Each layer file has the same dimension as well as an alpha channel.

The layer stack goes something like this:
• color layer
• accent layer (0-n)
• shading
• highlight
• lineart

There can be other layers as well, but that's the most basic version.

Is there a way to achieve what I'm looking for without the callback hell?


This comment has been minimized.


lovell commented Apr 10, 2016

Hello, given all the images are the same dimension, I'd probably use a Promises and raw pixel data approach for this, something like the following (untested):

const options = {
  raw: {
    width: 200,
    height: 100,
    channels: 4

const base = sharp('color.png').raw().toBuffer();

const composite = [
].reduce( function(input, overlay) {
  return input.then( function(data) {
    return sharp(data, options).overlayWith(overlay).raw().toBuffer();
}, base);

composite.then(function(data) {
  // data contains the multi-composited image as raw pixel data

@lovell lovell added the question label Apr 10, 2016


This comment has been minimized.

MPSinclair commented Apr 11, 2016

I love the way this is setup. It definitely looks like what I'm trying to achieve!

Right now I'm getting:
throw new Error('Unsupported input ' + typeof input);

I've been messing about with what you wrote above, trying to piece it all together. I'm fairly new to the Node environment so I spent some time this evening reading over handling the filesystem / buffered data / etc.

I tried removing the toBuffer() from the lines "const base = ..." and "return sharp(input, opt...)", thinking maybe I could just pass back the raw data since it didn't like the Promise it got back from toBuffer(). It didn't like the input format for that either, though.

I appreciate you taking the time to assist!

These are sample images I'm trying (the only difference being the width and height set to 400 from your script above):



This comment has been minimized.


lovell commented Apr 11, 2016

I've updated the code sample:

-  return sharp(input, options).overlayWith(overlay).raw().toBuffer();
+  return input.then( function(data) {
+    return sharp(data, options).overlayWith(overlay).raw().toBuffer();
+  });

This comment has been minimized.

MPSinclair commented Apr 11, 2016

Perfect, very much appreciated!


This comment has been minimized.

yyman001 commented Mar 20, 2018

i try it,down all source to demo,use this your code run fail.

(node:3832) UnhandledPromiseRejectionWarning: Error: Overlay image must have same dimensions or smaller
(node:3832) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:3832) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Repository owner locked and limited conversation to collaborators Mar 20, 2018

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.