3

SVG filters rasterize their source image, which means filtered text won't be anti-aliased, resulting in jagged edges.

Is there a workaround for this? Perhaps another filter can be used to emulate anti-aliasing, or maybe I can somehow anti-alias the text before the filter is applied?

Relevant Filter:

<filter id="f">
  <feGaussianBlur in="SourceGraphic" stdDeviation="0" result="blur" />
  <feColorMatrix
    in="blur"
    mode="matrix"
    values="1 0 0 0 0
    0 1 0 0 0
    1 0 1 0 0
    0 0 0 15 -8"
    result="goo"
  />
  <feComposite in="SourceGraphic" in2="goo" operator="atop" />
</filter>
Michael Mullany
  • 30,283
  • 6
  • 81
  • 105
aleclarson
  • 18,087
  • 14
  • 64
  • 91
  • Your assumption is not correct. Text elements provided as input to a filter will be anti-aliased before they're rasterized, and the filter preserves the anti-aliasing unless you're doing something to the alpha channel. What specific use case are you running into - please provide a minimal verifiable test case (aka code) – Michael Mullany Feb 27 '21 at 23:45
  • https://codesandbox.io/s/hardcore-water-yny8u – You will see jagged edges on the top ``. It's more noticeable on lower PPI screens. – aleclarson Feb 28 '21 at 16:24

2 Answers2

1

Text elements provided as input to a filter will be anti-aliased before they're rasterized, and the filter preserves the anti-aliasing unless you're doing something to the alpha channel.

In this case, you ARE doing something to the alpha channel. That feColorMatrix filter is multiplying the alpha channel by 15 and then subtracting 8. This has the effect of converting any pixels that have less than 8/15th opacity to transparent - which strips most of the anti-aliasing.

If you want to smooth out the edges a little, you can add another feGaussianBlur with a 0.5 or 1 unit stdDeviation to the end. There are other methods to add back the anti-aliasing but it really depends on what you're trying to do. Since the current filter doesn't do anything - I assume the intention is to animate something?

(Also - that feColorMatrix should have type='matrix' not mode='matrix' - mode is only used in feBlend.)

Update: may as well add the various techniques to smooth the edges.

  1. Original
  2. Original with the crispy filter
  3. #2 with a small blur tagged on at the end
  4. #2 with a small blur tagged on but clipped to a one pixel perimeter
  5. #2 with a small blur, but the low opacity part of the blur clipped to zero

<svg width="1000px" height="1000px" >
  <filter id="f1">
  <feGaussianBlur in="SourceGraphic" stdDeviation="0" result="blur" />
  <feColorMatrix
    in="blur"
    mode="matrix"
    values="1 0 0 0 0
    0 1 0 0 0
    1 0 1 0 0
    0 0 0 15 -8"
    result="goo"
  />
  <feComposite in="SourceGraphic" in2="goo" operator="atop" />
</filter>
  
<filter id="f2">
  <feGaussianBlur in="SourceGraphic" stdDeviation="0" result="blur" />
  <feColorMatrix
    in="blur"
    mode="matrix"
    values="1 0 0 0 0
    0 1 0 0 0
    1 0 1 0 0
    0 0 0 15 -8"
    result="goo"
  />
  <feComposite in="SourceGraphic" in2="goo" operator="atop" />
    <feGaussianBlur stdDeviation="1"/>
</filter>
  
  <filter id="f3">
  <feGaussianBlur in="SourceGraphic" stdDeviation="0" result="blur" />
  <feColorMatrix
    in="blur"
    mode="matrix"
    values="1 0 0 0 0
    0 1 0 0 0
    1 0 1 0 0
    0 0 0 15 -8"
    result="goo"
  />
  <feComposite in="SourceGraphic" in2="goo" operator="atop" result="pre-final"/>
  <feMorphology operator="dilate" radius="1" result="base"/>
   <feGaussianBlur in="pre-final" stdDeviation="1" />
  <feComposite in2="base" operator="in" />
</filter>
  
  
<filter id="f4">
  <feGaussianBlur in="SourceGraphic" stdDeviation="0" result="blur" />
  <feColorMatrix
    in="blur"
    mode="matrix"
    values="1 0 0 0 0
    0 1 0 0 0
    1 0 1 0 0
    0 0 0 15 -8"
    result="goo"
  />
  <feComposite in="SourceGraphic" in2="goo" operator="atop" result="pre-final"/>
  <feGaussianBlur stdDeviation="1"/>
  <feComponentTransfer>
    <feFuncA type="table" tableValues="0 0 .7 .8 .9 1"/>
  </feComponentTransfer>
</filter>
  
  
  <text x="50" y="150" style="font-size: 160; font-weight: bold;">FILTERED </text>
    <text filter="url(#f1)" x="50" y="300" style="font-size: 160; font-weight: bold;">FILTERED </text>
      <text filter="url(#f2)" x="50" y="450" style="font-size: 160; font-weight: bold;">FILTERED </text>
        <text filter="url(#f3)" x="50" y="600" style="font-size: 160; font-weight: bold;">FILTERED </text>
         <text filter="url(#f4)" x="50" y="750" style="font-size: 160; font-weight: bold;">FILTERED </text> 

  </svg>
Michael Mullany
  • 30,283
  • 6
  • 81
  • 105
1

Is there a workaround for this? Perhaps another filter can be used to emulate anti-aliasing, or maybe I can somehow anti-alias the text before the filter is applied?

This answer went into detail on the issue.

As a compromise solution, you can try:

  1. Choose less contrasting colors for the color of letters and background
  2. Apply the attribute shape-rendering =" crispEdges" to the font
  3. Choose a font that renders sharper edges

For example, your chosen font font-family =" cursive " looks like this:

enter image description here

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"     xmlns:xlink="http://www.w3.org/1999/xlink"
         width="400" height="600" viewBox="0 0 400 400" >  

<text  x="35" y="150" font-size="100px" fill="black" font-family="cursive"> HELLO </text>

</svg>

font-family = "Monotype Corsiva" looks better

enter image description here

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"     xmlns:xlink="http://www.w3.org/1999/xlink"
         width="400" height="200" viewBox="0 0 400 200" >  

<rect width="100%" height="100%" fill="silver" />
<text  x="35" y="120" font-size="100px" fill="#444444" shape-rendering="crispEdges" font-family="Monotype Corsiva"  > HELLO </text>

</svg>  

Applying an SVG filter to smooth jagged edges

  • For the filter feGaussianBlur the parameters are selected (the last row of the matrix) 0 0 0 29 -1
  • For the filter feComposite operator="atop"

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"     xmlns:xlink="http://www.w3.org/1999/xlink"
         width="400" height="200" viewBox="0 0 400 200" >  
<defs>
  <filter id="f">
  <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" >
        
    </feGaussianBlur>   
            <feColorMatrix in="blur" type="matrix"
                values="
                  1 0 0 0 0
                  0 1 0 0 0
                  0 0 1 0 0
                  0 0 0 29 -1"
                  result="goo" />
            <feComposite in="SourceGraphic" in2="goo" operator="atop"/>
        </filter>
</defs> 
<rect width="100%" height="100%" fill="silver" />
<text filter="url(#f)"  x="35" y="120" font-size="100px" fill="#444444" shape-rendering="crispEdges" font-family="Monotype Corsiva"  > HELLO </text>

</svg>  

Filter animation

I'm animating the stdDeviation of the feGaussianBlur

As I understood from @aleclarson's comment you want to animate the filter attribute

<animate attributeName="stdDeviation" begin="0s" dur="8s"
  repeatCount="indefinite" values="1;6;12;12;6;1;1" />

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"     xmlns:xlink="http://www.w3.org/1999/xlink"
         width="400" height="200" viewBox="0 0 400 200" >  
<defs>
  <filter id="f">
  <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" >
   <animate attributeName="stdDeviation" begin="0s" dur="8s" repeatCount="indefinite" values="1;6;12;12;6;1;1" />
    </feGaussianBlur>       
            <feColorMatrix in="blur" type="matrix"
                values="
                  1 0 0 0 0
                  0 1 0 0 0
                  0 0 1 0 0
                  0 0 0 29 -1"
                  result="goo" />
            <feComposite in="SourceGraphic" in2="goo" operator="atop"/>
        </filter>
</defs> 

<text filter="url(#f)"  x="35" y="120" font-size="100px" fill="#111111" shape-rendering="crispEdges" font-family="Monotype Corsiva"  > HELLO </text>

</svg>  

Variant with value and operator = "xor"

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"     xmlns:xlink="http://www.w3.org/1999/xlink"
         width="400" height="200" viewBox="0 0 400 200" >  
<defs>
  <filter id="f">
  <feGaussianBlur in="SourceGraphic" stdDeviation="1" result="blur" >
   <animate attributeName="stdDeviation" begin="0s" dur="8s" repeatCount="indefinite" values="1;6;12;12;6;1;1" />
    </feGaussianBlur>       
            <feColorMatrix in="blur" type="matrix"
                values="
                  1 0 0 0 0
                  0 1 0 0 0
                  0 0 1 0 0
                  0 0 0 29 -1"
                  result="goo" />
            <feComposite in="SourceGraphic" in2="goo" operator="xor"/>
        </filter>
</defs> 

<text filter="url(#f)"  x="35" y="120" font-size="100px" fill="#111111" shape-rendering="crispEdges" font-family="Monotype Corsiva"  > HELLO </text>

</svg>  
Alexandr_TT
  • 13,635
  • 3
  • 27
  • 54