Sharp: Easier way to composite 3+ layers

Created on 10 Apr 2016  Â·  5Comments  Â·  Source: lovell/sharp

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:
sharp(layer1)
.overlayWith(layer2)
.overlayWith(layer3)
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?

cookbook question

Most helpful comment

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 = [
  'accent0.png',
  'accent1.png',
  'accentn.png',
  'shading.png',
  'highlight.png',
  'lineart.png'
].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
});

All 5 comments

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 = [
  'accent0.png',
  'accent1.png',
  'accentn.png',
  'shading.png',
  'highlight.png',
  'lineart.png'
].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
});

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):

accent0.png
accent0
accent1.png
accent1
color.png
color
highlight.png
highlight
lineart.png
lineart
shading.png
shading

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();
+  });

Perfect, very much appreciated!

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.
Was this page helpful?
0 / 5 - 0 ratings

Related issues

emmtte picture emmtte  Â·  3Comments

jaekunchoi picture jaekunchoi  Â·  3Comments

janaz picture janaz  Â·  3Comments

iq-dot picture iq-dot  Â·  3Comments

vermin1337 picture vermin1337  Â·  3Comments