Closed ilicmarko closed 5 years ago
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!
@ilicmarko @urbanisierung @lovell According to https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/529
Now you can filter="drop-shadow(<color> <dx> <dy> < stdDeviation>)"
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400">
<rect x="100" y="100" width="200" height="200" fill="green" filter="drop-shadow(#ff0000 1px 4px 6px)"/>
</svg>
Hello! @wchaws @lovell I'm trying to add shadow to an SVG image (black shadow). I'm doing this:
const imageData = await sharp(inputBuffer);
const { width, height } = await imageData.metadata();
const maxWidthHeight = Math.min(width, height);
const widthHeight = maxWidthHeight;
const imageRadio = widthHeight / 2.3;
const roundedCorners = Buffer.from(
`<svg width="100%" height="100%">
<circle
filter="drop-shadow(0 0 5px black)"
fill="#black"
style="padding: 10px;"
cx="${imageRadio}" cy="${imageRadio}" r="${imageRadio}"/>
</svg>`,
);
// Rounded
await sharp(inputBuffer)
.composite([{
input: roundedCorners,
blend: 'dest-in',
}])
.toFile(imagePath)
and this is the result:
I'm adding the shadow but it is not black. Why?
Thanks, guys!
Armando
@armandoarmando Actually, you're adding shadow to your circle mask not adding shadow to compositing result. That's why you can see blurry edge. I think you can add another circle with shadow in the background.
@wchaws I'm trying to make it like this https://github.com/lovell/sharp/issues/1490#issuecomment-611779237 but the shadow is cut... I think I'm very close, but I can't figure out what is the problem... any idea?
const OUTPUT_WIDTH = 630
const OUTPUT_HEIGHT = 630
const SHADOW_MARGIN = 5
const SHADOW_BLUR = 5
const SHADOW_OFFSET = 6
const SHADOW_OPACITY = 0.5
const stream = await sharp(inputBuffer)
const { width, height } = await stream.metadata();
const radioShadowImage = (width + SHADOW_MARGIN * 2) / 2;
const radioImage = width / 2;
const shadow = await sharp(
Buffer.from(`
<svg
width="${width + SHADOW_MARGIN}"
height="${height + SHADOW_MARGIN}"
>
<circle
fill="rgba(0, 0, 0, ${SHADOW_OPACITY})"
cx="${radioShadowImage}" cy="${radioShadowImage}" r="${radioShadowImage}"
/>
</svg>`)
)
.blur(SHADOW_BLUR)
.toBuffer();
const roundedCorners = Buffer.from(
`<svg width="550" height="550">
<circle
cx="${radioImage}" cy="${radioImage}" r="${radioImage}"/>
</svg>`,
);
const image = await sharp(inputBuffer)
.composite([
{
input: roundedCorners,
blend: 'dest-in',
},
])
.toBuffer();
await sharp({
create: {
width: OUTPUT_WIDTH,
height: OUTPUT_HEIGHT,
channels: 4,
background: { r: 255, g: 255, b: 255, alpha: 0 },
},
})
.composite([
{
input: shadow,
blend: 'over',
gravity: 'centre',
},
{
input: image,
blend: 'over',
gravity: 'centre',
},
])
.toFile('image.png');
@armandoarmando To give the shadow some room, I believe you should reduce the size of the circle a little bit.
drop-shadow
is super slow
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:
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.
Here is an HTML example of how that would look (JSFiddle):
The problem with this idea is I don't know how to stack layers in Sharp.
Thank you in advance.