Is it even possible to add a shadow to an image?
I have been trying to add a shadow to an image but I doesn't seem possible.
I couldn't get the simple shadow running because when you create a shadow with SVG, for example:
<svg width="200" height="200">
<defs>
<filter id="shadow">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
</defs>
<circle cx="50%" cy="50%" r="80" style="filter:url(#shadow);"/>
</svg>
and overlay that with overlayWith(Buffer.from(<ABOVE SVG>), { cutout: true }) it will simply ignore the filter (shadow) and cut it out like i wasn't there.
The idea I have as a workaround is make an SVG that is the same shape and size, position it behind the image and add a blur to the SVG.
<svg width="200" height="200">
<defs>
<filter id="f1" x="0" y="0">
<feGaussianBlur in="SourceGraphic" stdDeviation="15" />
</filter>
</defs>
<rect width="90" height="90" fill="yellow" filter="url(#f1)" />
</svg>
Here is an HTML example of how that would look (JSFiddle):
<svg width="200" height="200">
<filter id="blurMe">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
</filter>
<circle cx="60" cy="60" r="50" fill="black" filter="url(#blurMe)" id="shadow" />
<circle cx="60" cy="60" r="52" fill="dodgerblue" id="image" />
</svg>
The problem with this idea is I don't know how to stack layers in Sharp.
Thank you in advance.
Hello, feDropShadow is unsupported by librsvg - it's only at W3C draft status - https://drafts.fxtf.org/filter-effects/
See #728 for multiple composition, some of the linked issues provide possible workarounds for now.
Hope this helped, please feel free to re-open with more details if not.
Came across this issue while trying to do something similar, feDropShadow is still unsupported but I managed to achieve what I wanted to do by blurring a rectangle (an svg the same size as the input image, with a margin) to act as the shadow and then using composites. Hope this helps anyone looking for the same thing!
const OUTPUT_BACKGROUND = '#bada55'
const OUTPUT_WIDTH = 1200
const OUTPUT_HEIGHT = 630
const SHADOW_MARGIN = 40
const SHADOW_BLUR = 15
const SHADOW_OFFSET = 6
const SHADOW_OPACITY = 0.3
const stream = await sharp(inputPath)
const { width, height } = await stream.metadata()
const shadow = await sharp(
Buffer.from(`
<svg
width="${width + SHADOW_MARGIN * 2}"
height="${height + SHADOW_MARGIN * 2}"
>
<rect
width="${width}"
height="${height}"
x="${SHADOW_MARGIN}"
y="${SHADOW_MARGIN + SHADOW_OFFSET}"
fill="rgba(0, 0, 0, ${SHADOW_OPACITY})"
/>
</svg>`)
)
.blur(SHADOW_BLUR)
.toBuffer()
const image = await stream
.resize({
height,
width,
})
.toBuffer()
await sharp({
create: {
width: OUTPUT_WIDTH,
height: OUTPUT_HEIGHT,
channels: 3,
background: OUTPUT_BACKGROUND,
},
})
.composite([
{ input: shadow, blend: 'multiply' },
{ input: image, blend: 'over' },
])
.jpeg()
.toFile(outputPath)
Wow, thanks @papandreou! This is exactly what I was looking for!
Most helpful comment
Came across this issue while trying to do something similar,
feDropShadowis still unsupported but I managed to achieve what I wanted to do by blurring a rectangle (an svg the same size as the input image, with a margin) to act as the shadow and then using composites. Hope this helps anyone looking for the same thing!