Creating a vignette with SVG filters? - svg

I would like to create vignette on an image using SVG filters. What is the best way to approach this? I already tried creating a feFlood with a gradient as flood-color but that doesn't work. Right now I am using a png generated in illustrator but I would like to keep it all in svg.
To illustrate what I am aiming for, this is the original:
And this is what is should be:
UPDATE:
I am using svg.js with the svg.filter.js plugin to generate the code dynamically. This is what I tried:
// create svg canvas
var draw = SVG('canvas').size(400,400)
// define gradient
var gradient = draw.gradient('radial', function(stop) {
stop.at({ offset: 0, opacity: 0 })
stop.at({ offset: 1 })
})
gradient.radius('80%')
// create image
var image = draw.image('http://distilleryimage11.ak.instagram.com/89ac2e90d9b111e297bf22000a1f9263_7.jpg').size(400,400)
// add filter
image.filter(function(add) {
add.blend(add.source, add.flood(gradient), 'multiply')
})
This is the generated code:
<svg id="SvgjsSvg1000" xmlns="http://www.w3.org/2000/svg" version="1.1" width="400" height="400" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:relative;overflow:hidden;left:0px;top:0px;">
<image id="SvgjsImage1005" xlink:href="http://distilleryimage11.ak.instagram.com/89ac2e90d9b111e297bf22000a1f9263_7.jpg" width="400" height="400" filter="url( #SvgjsFilter1006)"></image>
<defs id="SvgjsDefs1001">
<radialGradient id="SvgjsRadialGradient1002" r="80%">
<stop id="SvgjsStop1003" stop-opacity="0" offset="0"></stop>
<stop id="SvgjsStop1004" offset="1"></stop>
</radialGradient>
<filter id="SvgjsFilter1006">
<feFlood id="SvgjsFeFlood1007" in="SourceGraphic" result="SvgjsFeFlood1007Out" flood-color="url(#SvgjsRadialGradient1002)"></feFlood>
<feBlend id="SvgjsFeBlend1008" in="SourceGraphic" result="SvgjsFeBlend1008Out" in2="SvgjsFeFlood1007Out" mode="multiply"></feBlend>
</filter>
</defs>
</svg>
The result is a completely black image. It seems that the feFlood element does not accept gradients as fill because it works with a color.
Here is a fiddle with the example code: http://jsfiddle.net/wout/VmUu6/

This worked for me without spotlighting, I user a radial gradient with multiple stop points you can adjust the color, stop points and opacity level to get the effect you want. play with the r(radius to widen/shorten the effect and the fx,fy to position. Gradients tend to be more efficient then using some of the filters that require heavy maths processing
<radialGradient id="grad"
fx="50%" fy="50%" r="55%"
spreadMethod="pad">
<stop offset="30%" stop-color="#222222" stop-opacity="0"/>
<stop offset="40%" stop-color="#222222" stop-opacity="0.2"/>
<stop offset="50%" stop-color="#222222" stop-opacity="0.4"/>
<stop offset="70%" stop-color="#222222" stop-opacity="0.6" />
<stop offset="100%" stop-color="#222222" stop-opacity="1" />
</radialGradient>
Apply to the rectangle positioned above an image
<image id="background" x="0" y="0" width="800px" height="530px" preserveAspectRatio="true"
xlink:href="http://i1-qa.adis.ws/i/Client_23/ss_collection_reddress?w=800"/>
<rect filter="url(#blur)" style="fill:url(#grad)" x="0" y="0" width="800px" height="530px"/>
vignette example

I have some vignetteing and selecting compositing methods in my slide share preso on filters. Page 50 onwards for techniques. Here is the most relevant piece of filter code. It's not a real vignette - that requires a much longer filter and selective image combination, but it's easier to understand.
<filter>
<feFlood id="flood-5" result="blackfield-6" x="0%" y="0%" width="100%" height="100%" result="blackfield-6" flood-color="#000000" flood-opacity="1"/>
<feSpecularLighting id="specular-5" result="Spotlight-6" lighting-color="#FFFFFF" surfaceScale="1" specularConstant="1" specularExponent="120">
<fePointLight id="pointlight-5" x="100" y="100" z="854"/>
</feSpecularLighting>
<feBlend id="svg-7" result="A-6" in="blackfield-6" in2="Spotlight-6" mode="lighten"/>
<feBlend id="blend-5" result="B-6" in="A-6" in2="SourceGraphic" mode="multiply"/>
</filter>

Related

How do I contain feGaussianBlur within a circle?

I'm trying to contain a feGaussianBlur within a circle in order make the circle's gradient smoother, but the blur expands beyond the circle.
I'm also using another gradient with a feGaussianBlur behind the top circle that will expand beyond it, so I can't use a mask over the top circle to cover up the color bleeding beyond it, else the blur of the circle below it will also be covered.
In this example I add a gradient to two circles and a rectangle.
The rectangle is made into a circle using a clip-path.
The second circle and the rectangle has the filter applied.
The filter has the feComposite/in to keep the filter inside the circle as suggested by Michael Mullany: <feComposite operator="in" in2="SourceGraphic"/>
As I see it the the one without filter looks more smooth. So, I don't know if it makes sense -- maybe in your use case?
<svg width="500" viewBox="0 0 300 110" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="4" />
<feComposite operator="in" in2="SourceGraphic"/>
</filter>
<linearGradient id="gradient">
<stop offset="0" stop-color="blue" />
<stop offset="30%" stop-color="blue" />
<stop offset="70%" stop-color="lightblue" />
<stop offset="100%" stop-color="lightblue" />
</linearGradient>
<clipPath id="cp1">
<circle cx="50" cy="50" r="45" />
</clipPath>
</defs>
<circle cx="50" cy="50" r="45" fill="url(#gradient)" />
<text font-size="10" x="50" y="105" text-anchor="middle">circle no filter</text>
<circle cx="150" cy="50" r="45" fill="url(#gradient)" filter="url(#blur)" />
<text font-size="10" x="150" y="105" text-anchor="middle">circle filter</text>
<rect transform="translate(200 0)" width="100" height="100" fill="url(#gradient)" clip-path="url(#cp1)" filter="url(#blur)"/>
<text font-size="10" x="250" y="105" text-anchor="middle">rect filter</text>
</svg>
To contain a blur to the area of the original graphic, you add a feComposite/in to the end of your filter.
<feComposite operator="in" in2="SourceGraphic"/>

Apply glow to SVG element with a gradient

I have an SVG element that's based on a circle and a mask to create a ring.
The code is as follows:
<svg width="100%" height="100%" viewBox="0 0 300 300" version="1.1">
<defs>
<linearGradient x1="97.3756325%" y1="100%" x2="0%" y2="100%" id="gradient">
<stop stop-color="#FF00AC" offset="0%"></stop>
<stop stop-color="#5D00C4" offset="100%"></stop>
</linearGradient>
<mask id="circle-mask">
<circle cx="150" cy="150" r="145" fill="white"/>
<circle cx="150" cy="150" r="140" fill="black"/>
</mask>
</defs>
<circle cx="150" cy="150" r="145" mask="url(#circle-mask)" fill="url(#gradient)"/>
</svg>
The ring got a gradient as fill color. Now I want to apply a glow effect on the ring that uses the colors of the gradient. Any ideas on how to do that?
What is a "glow"? I don't think there is a canonical definition, so I'm going with one that I have seen used before: a backdrop shadow that is colored and whose opacity values are exagerated. To define those effects, refer to the SVG <filter> spec, its support is pretty good across browsers.
I understand you need the mask for more complex situations than this one. The important thing here is the order in which different effects are applied: clip paths and masks are processed after filter effects:
First the element is styled under absence of filter effects, masking, clipping and opacity. Then the element and its descendants are drawn on a temporary canvas. In a last step the following effects are applied to the element in order: filter effects, clipping, masking and opacity.
Therefore you need to wrap the masked element in a <g> and apply the glow filter there.
Vary stdDeviation to stretch or shrink the shadow, and vary slope to change its opacity. If you set the slope to a value > 2, you will no longer get a clear border between the ring and its shadow.
<svg width="100%" height="100%" viewBox="0 0 300 300" version="1.1">
<defs>
<linearGradient x1="97.3756325%" y1="100%" x2="0%" y2="100%" id="gradient">
<stop stop-color="#FF00AC" offset="0%"></stop>
<stop stop-color="#5D00C4" offset="100%"></stop>
</linearGradient>
<mask id="circle-mask">
<circle cx="150" cy="150" r="145" fill="white"/>
<circle cx="150" cy="150" r="140" fill="black"/>
</mask>
<filter id="glow">
<feGaussianBlur stdDeviation="5"/>
<feComponentTransfer>
<feFuncA type="linear" slope="2" />
</feComponentTransfer>
<feBlend in2="SourceGraphic" />
</filter>
</defs>
<g filter="url(#glow)">
<circle cx="150" cy="150" r="145" mask="url(#circle-mask)" fill="url(#gradient)"/>
</g>
</svg>

White linear gradient defined in svg def , does't display white gradient. why ?

I was just going though the MDN documention HERE for clipping and masking in SVG and came across the following SVG::-
<svg width="200" height="200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="Gradient">
<stop offset="0" stop-color="white" stop-opacity="0" />
<stop offset="1" stop-color="white" stop-opacity="1" />
</linearGradient>
<mask id="Mask">
<rect x="0" y="0" width="200" height="200" fill="url(#Gradient)" />
</mask>
</defs>
<rect x="0" y="0" width="200" height="200" fill="green" />
<rect x="0" y="0" width="200" height="200" fill="red" mask="url(#Mask)" />
</svg>
I see the following two stop colors defined::-
<stop offset="0" stop-color="white" stop-opacity="0" />
<stop offset="1" stop-color="white" stop-opacity="1" />
But when the SVG displays i see no white color, why is this ? looking at the above linear gradient, i would expect a white gradient instead of a red one, i am not quite understanding why there is no white gradient.
NOTE ::- this is a why question, not a how-to-do-this-question
The gradient is not used to color the rectangle, but to define a mask. The <mask> is an offscreen image that is never shown, but only used to compute the element it is applied to.
In this case, the mask consists of a black (transparent) background, onto which a white gradient is added, so that it runs from black transparent to white opaque. When this mask is applied to the red rectangle, black is transformed into opacity="0", and white is transformed into opacity="1". So on the left, the lower green rectangle shines through, but on the right it doesn't and you see the red.

How to achieve a Progressive Blur using SVG by combining a filter with a mask?

I'm trying to achieve a linear blur effect like the one on the image, but using just svg, no css!!!
Notice how the top of the image is completely blurred, but the bottom isn't.
In SVG Blur effect can be achieved using feGaussianBlur. The gradient can be used with linearGradient.
How can these two be combined?
While it's possible to do this entirely in a filter without using double images, the solution can be buggy because of how both Firefox and Chrome handle ops on low opacities. So this is an alternative & straightforward way to do it using doubled images. Note that you have to clip the image edges for a clean image because feGaussianBlur creates edge fades.
<svg width="800px" height="600px">
<defs>
<linearGradient id="progFade" x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="0%" stop-color="black"/>
<stop offset="60%" stop-color="white"/>
</linearGradient>
<mask id="progFadeMask" >
<rect x="0%" y="0%" width="100%" height="100%" fill="url(#progFade)" />
<mask>
<filter id="blurme" x="0%" y="0%" width="100%" height="100%">
<feGaussianBlur stdDeviation="15" result="blurry"/>
</filter>
<clipPath id="outerclip">
<rect x="20" y="20" width="460" height="380" fill="black">
</clipPath>
</defs>
<g clip-path="url(#outerclip)">
<image x="0" y="0" filter="url(#blurme)" xlink:href="http://cps-static.rovicorp.com/3/JPG_400/MI0003/890/MI0003890640.jpg" width="494" height="400"/>
<image x="0" y="0" mask="url(#progFadeMask)" xlink:href="http://cps-static.rovicorp.com/3/JPG_400/MI0003/890/MI0003890640.jpg" width="494" height="400"/>
</g>
</svg>
Enjoy Progressively Blurred Chaka Khan

Elliptical gradient in SVG

Is there a way to make an elliptical gradient in SVG?
I tried the suggested code below, but it just displays a red ellipse, not a gradient:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20001102//EN"
"http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">
<svg width="100%" height="100%"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events" >
<defs>
<radialGradient id="gradientDefinition"
gradientUnits="userSpaceOnUse">
<stop stop-color="yellow" offset="0%" />
<stop stop-color="red" offset="100%" />
</radialGradient>
</defs>
<ellipse cx="250" cy="150" rx="200" ry="100" stroke="white"
stroke-width="1" stroke-dasharray="1 1 1 1"
style="fill:url(#gradientDefinition)" />
</svg>
I want an elliptical gradient, not a circular gradient inside an ellipse.
To summarize the findings from earlier, it appears that removing the gradientUnits="userSpaceOnUse" attribute and value pair from the <radialGradient> tag allows for the radial gradient to become (or at least appear to become) elliptical in shape. Also, adding stop-opacity attributes and values to each <stop> tag allows for the elliptical gradient effect to be more easily seen (at least for demonstration purposes.)
Here is the code with the above changes made:
<defs>
<radialGradient id="gradientDefinition" >
<stop stop-color="yellow" offset="0%" stop-opacity="0" />
<stop stop-color="red" offset="100%" stop-opacity="1" />
</radialGradient>
</defs>
To show that this code appears to work:
see: elliptical radialGradient vs circular radialGradient
There is also a tutorial online that appears to provide similar behavior for a similar elliptical gradient approach and the results from that tutorial can be found in this jsFiddle.
Note: If this approach does not work for your purposes, there may be some other, better approach (possibly having to do with gradient transformations, or something similar...)

Resources