SVG feComponentTransfer to colorize image - svg

I'm trying to colorize a white image using a filter but I'm getting an unexpected result
SVG Sample:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="373" height="400"
viewBox="0 0 373 400">
<defs>
<filter id="colorize">
<feComponentTransfer>
<feFuncR type="linear" slope="0.3333333"></feFuncR>
<feFuncG type="linear" slope="0"></feFuncG>
<feFuncB type="linear" slope="0.3333333"></feFuncB>
</feComponentTransfer>
</filter>
</defs>
<g filter="url(#colorize)">
<image x="0" y="0" xlink:href="https://i.imgur.com/Df8zD8O.png" width="373" height="400"></image>
</g>
<text font-weight="bold" fill="rgb(85,0,85)" x="0" y="100" font-size="100">Surfer</text>
</svg>
The expected result is that the image becomes the same color as the text
In this case #550055 or rgb(85,0,85)
I set the slopes on the filter to 0.3333333 for R and B based on the result of 85 / 255 but as you can see the result is not correct
Maybe I'm using the wrong method of calculation to attain the desired color
One thing to note is that colors having components approaching/equaling 255 give much better results
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="373" height="400"
viewBox="0 0 373 400">
<defs>
<filter id="colorize">
<feComponentTransfer>
<feFuncR type="linear" slope="255"></feFuncR>
<feFuncG type="linear" slope="0"></feFuncG>
<feFuncB type="linear" slope="255"></feFuncB>
</feComponentTransfer>
</filter>
</defs>
<g filter="url(#colorize)">
<image x="0" y="0" xlink:href="https://i.imgur.com/Df8zD8O.png" width="373" height="400"></image>
</g>
<text font-weight="bold" fill="rgb(255,0,255)" x="0" y="100" font-size="100">Surfer</text>
</svg>
I based my calculation on this formula
C' = slope * C + intercept
What am I doing wrong?

The default colour space for most SVG filters is linearRGB. You seem to be assuming it's sRGB though.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="373" height="400"
viewBox="0 0 373 400">
<defs>
<filter id="colorize" color-interpolation-filters="sRGB">
<feComponentTransfer>
<feFuncR type="linear" slope="0.3333333"></feFuncR>
<feFuncG type="linear" slope="0"></feFuncG>
<feFuncB type="linear" slope="0.3333333"></feFuncB>
</feComponentTransfer>
</filter>
</defs>
<g filter="url(#colorize)">
<image x="0" y="0" xlink:href="https://i.imgur.com/Df8zD8O.png" width="373" height="400"></image>
</g>
<text font-weight="bold" fill="rgb(85,0,85)" x="0" y="100" font-size="100">Surfer</text>
</svg>

You have to set the color space to sRGB - because that's the color space that the CSS rgb() color property uses. (The default color space for SVG filters is linearRGB).
Add color-interpolation-filters="sRGB" to your filter element and you'll get the result you expect.

Related

SVG filter to create waves from straight lines

I am try to reply the effect in the picture with SVG. Do you have any hint on how I can obtain that (other than manually drawing the paths?).
Basically I was thinking to draw or create a pattern of vertically straight lines and then apply an effect on them. The result doesn't have to be 100% equal to the one provided, but similar.
The best you can do without drawing the lines specifically is to distort them using a feDisplacementMap filter - but the results aren't even close to what you're hoping for because filters work in bitmaps.
<svg width="800px" height="600px" color-interpolation-filters="sRGB">
<defs>
<pattern id="diagonal" patternUnits="userSpaceOnUse" width="10" height="10">
<path d="M0 0 l 10 10"
stroke="black" stroke-width="1"/>
</pattern>
<filter id="distort">
<feTurbulence baseFrequency = "0.015" type="fractalNoise" />
<feDisplacementMap in='SourceGraphic' xChannelSelector="R" yChannelSelector="G" scale="60"/>
<feGaussianBlur stdDeviation="1"/>
<feComponentTransfer>
<feFuncA type="table" tableValues="0 .1 .5 1 1 1 1 1 1 1 1 1 1"/>
<feComponentTransfer>
</filter>
</defs>
<g filter="url(#distort)">
<rect x="10" y="10" width="300" height="300" fill="url(#diagonal)"/>
</g>
</svg>

SVG blur filter has hard borders

I have this code in which I want to add a feGaussianBlur to a <rect/> element:
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
<defs>
<filter id="f">
<feGaussianBlur in="SourceGraphic" stdDeviation="20"/>
</filter>
</defs>
<rect x="100" y="100" height="200" width="180" fill="green" filter="url(#f)"/>
</svg>
The output is rendered like this:
As you can see, that the sides are not softened. The sides have a hard border.
But, when I decrease the value of the stdDeviation, it works well. Here is the output if the value of stdDeviation is set to 10:
Why is it not working properly with a value greater than 10? And what can I do to achieve it?
Expand the filter region
x="-20%" y="-20%" width="150%" height="150%"
See Filter effects region
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
<defs>
<filter id="f" x="-20%" y="-20%" width="150%" height="150%">
<feGaussianBlur in="SourceGraphic" stdDeviation="20"/>
</filter>
</defs>
<rect x="100" y="100" height="200" width="180" fill="green" filter="url(#f)"/>
</svg>

Apply a texture to an image in SVG

I'm trying to apply a texture to an image
Original
Texture
Result (made with PHP GD)
But with SVG, the closest I got is this result
<svg preserveAspectRatio="none" width="500" height="500" viewBox="0 0 500 500">
<defs>
<filter id="texture">
<feImage href="https://i.imgur.com/pjWcnJs.jpg" result="texture-img"/>
<feBlend in="SourceGraphic" in2="texture-img" mode="multiply"/>
</filter>
</defs>
<g>
<g filter="url(#texture)">
<image x="0" y="0" href="https://i.imgur.com/oVEdsQt.png" opacity="1" width="500" height="500" />
</g>
</g>
</svg>
fiddle
Is there another way which won't texturize transparent pixels?
I found a solution which is to pass the texture through a composite filter which would crop it to the source image
<svg preserveAspectRatio="none" width="500" height="500" viewBox="0 0 500 500">
<defs>
<filter id="texture">
<feImage href="https://i.imgur.com/pjWcnJs.jpg" result="texture-img"/>
<feComposite in2="SourceGraphic" operator="in" in="texture-img" result="composite"/>
<feBlend in="SourceGraphic" in2="composite" mode="multiply"/>
</filter>
</defs>
<g>
<g filter="url(#texture)">
<image x="0" y="0" href="https://i.imgur.com/oVEdsQt.png" opacity="1" width="500" height="500" />
</g>
</g>
</svg>
To tile the texture, I used feTile like this
<svg preserveAspectRatio="none" width="500" height="500" viewBox="0 0 500 500">
<defs>
<filter id="texture" x="0" y="0" width="100%" height="100%">
<feImage href="https://i.imgur.com/gWH7NLm.jpg" result="texture-img" width="256" height="256"/>
<feTile in="texture-img" x="0" y="0" width="100%" height="100%" result="tile" ></feTile>
<feComposite in2="SourceGraphic" operator="in" in="tile" result="composite"/>
<feBlend in="SourceGraphic" in2="composite" mode="multiply"/>
</filter>
</defs>
<g>
<g filter="url(#texture)">
<image x="0" y="0" href="https://i.imgur.com/oVEdsQt.png" opacity="1" width="500" height="500" />
</g>
</g>
</svg>
I got the idea by checking how inkscape applies material textures

wrong rendering of blurred circle in svg

I want to draw a blurred circle in svg, but with some higher stdDeviation values, it will be cropped.
I tried several attributes like "edgeMode" on the "feGaussianBlur" tag or 'width="150%"' and 'height="150%"' on the "circle" and "filter" tag, but nothing helps.
Any ideas?
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="f1">
<feGaussianBlur in="SourceGraphic" stdDeviation="15"/>
</filter>
</defs>
<circle cx="100" cy="100" r="30" fill="#ff0000" filter="url(#f1)"/>
</svg>
Adjusting the filter's x, y, width and height is all that's required.
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="f1" x="-40%" y="-40%" width="180%" height="180%">
<feGaussianBlur in="SourceGraphic" stdDeviation="15"/>
</filter>
</defs>
<circle cx="100" cy="100" r="30" fill="#ff0000" filter="url(#f1)"/>
</svg>

Adding border to svg polygon by using filters

I am looking for a way to add a border to a polygon with SVG. I can't add a another polygon since it should work independently from polygon shape.
I tried using stroke on polygon but it also draws the line inside the polygon. Drawing only outside of the polygon was removed recently latest W3C specs.
So I fiddled with svg filters and it nearly works but I got to point that the border is a bit cut. Does anyone know how to remove this unwanted effect?
Fiddle
Code:
` <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<defs>
<style>
.cls-1{fill:#feb500;}
</style>
<filter id="solid-border" width="130%">
<feFlood flood-color="black" result="base"/>
<feMorphology result="bigger" in="SourceGraphic" operator="dilate" radius="3"/>
<feComposite result="drop" in="base" in2="bigger" operator="in"/>
<feMerge>
<feMergeNode in="drop"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
<feBlend in="SourceGraphic" in2="drop" mode="normal"/>
</filter>
</defs>
<g id="background" filter="url(#solid-border)">
<polygon class="cls-1" points="64 13 6.5 107 121.5 107 64 13"/>
</g>
</svg>`
You can create neater rounded corners by using the blur-then-threshold trick.
We are blurring a black triangle, then using a feComponentTransfer primitive to convert dark greys to black and the lighter greys to white.
You can adjust the "stroke" size by changing stdDeviation, or varying the number of 1s in the tableValues attribute.
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<defs>
<style>
.cls-1{fill:#feb500;}
</style>
<filter id="solid-border2" width="130%" color-interpolation-filters="sRGB">
<feFlood flood-color="black" result="base"/>
<feGaussianBlur result="blur" in="SourceAlpha" stdDeviation="3"/>
<feComponentTransfer in="blur" result="threshold">
<feFuncA type="discrete" tableValues="0 1 1 1 1 1 1 1 1 1 1 1 1 1"/>
</feComponentTransfer>
<feBlend in="SourceGraphic" in2="threshold" mode="normal"/>
</filter>
</defs>
<g id="background" filter="url(#solid-border2)">
<polygon class="cls-1" points="64 13 6.5 107 121.5 107"/>
</g>
</svg>
If you wanted, you could also apply this trick to your version of the filter. However, the corners get a bit funky.
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<defs>
<style>
.cls-1{fill:#feb500;}
</style>
<filter id="solid-border" width="130%">
<feFlood flood-color="black" result="base"/>
<feMorphology in="SourceAlpha" operator="dilate" radius="4"/>
<feGaussianBlur stdDeviation="2"/>
<feComponentTransfer>
<feFuncA type="discrete" tableValues="0 1 1 1 1 1"/>
</feComponentTransfer>
<feBlend in="SourceGraphic" in2="drop" mode="normal"/>
</filter>
</defs>
<g id="background" filter="url(#solid-border)">
<polygon class="cls-1" points="64 13 6.5 107 121.5 107"/>
</g>
</svg>
I'd do something like below. Declaring the xlink namespace in svg tag as without it, Safari doesn't support the use tag.
You're defining the shape after you use the use tag. The internal stroke in the use tag is covered by the original shape after. Though I'm unsure if this is using another polygon as you say can't use one.
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128 128">
<defs>
<style>
.cls-1{fill:#feb500;}
</style>
</defs>
<use xlink:href="#triangleShape" stroke="black" stroke-width="8"/>
<polygon id="triangleShape" class="cls-1" points="64 13 6.5 107 121.5 107 64 13"/>
</svg>

Resources