Search code examples
svgstrokesvg-filters

Apply SVG filter to "fill" only


How do I apply an SVG filter on an SVG element, but not on its stroke?

Let's say I have this SVG filter (it puts the red component to 100%):

<filter id="testStroke">
<feComponentTransfer>
<feFuncR type="linear" slope="0" intercept="1"/>
</feComponentTransfer>
</filter>

If I apply this filter on that text node:

<text x="450" y="210" fill="black" stroke="blue" filter="url('#testStroke')">
Result
</text>

Then the filled part (originally black) turns red (because of the filter), and the blue stroke turns purple (same reason). I would like the stroke to stay blue (not filtered), but the fill to turn red (filtered).

I'm not looking for the "don't stroke the shape, apply the filter on it and create a clone of that shape to apply the stroke on".

Is there a way to apply the filter only on the filled part on a shape, and not on its stroke?


Solution

  • There is no configuration or attribute to select the fill directly, but you can use a "green-screen" technique to select out the stroke, filter the fill and then reapply the stroke. You have to know the colors of the stroke and fill ahead of time, which is a downside (because the pseudo inputs for doing this are not supported in Chrome/Safari (although they are in Firefox and IE10)). So here is a working example:

        <filter id="testStroke">
           <feComponentTransfer in="SourceGraphic" result="recolored">
               <feFuncR type="linear" slope="0" intercept="1"/>
           </feComponentTransfer>
    
     <!-- We're using the fact that the black fill has zero luminance to create a selection mask for the stroke -->
           <feColorMatrix in="SourceGraphic" type="luminanceToAlpha" result="lumMask"/>
            <feComponentTransfer in="lumMask" result="lumMaskCeil">
     <!-- a blue fill doesn't have a high luminance, so we're going to dial it way up using a gamma transform with a high exponent-->
               <feFuncA type="gamma" exponent=".01"/>
            </feComponentTransfer>
    
     <!-- this selects just the part of the input image that overlaps with our stroke mask-->
         <feComposite operator="in" in="SourceGraphic" in2="lumMaskCeil" result="stroke"/>
    
     <!-- and composite it over our recolored content from the original filter-->
    
      <feComposite operator="over" in="stroke" in2="recolored"/>    
    </filter>