I see how to use feComponentTransfer to adjust RGBA values using a table
and tableValues
.
But is there a way to adjust the R, G, or B values using alpha values as input (for example)?
Why? Say I composite many translucent shapes, resulting in different opacities where these shapes overlap. I would like to use these opacity values to adjust the RGB using a lookup table based on the opacity of each pixel.
For example, in this image the ellipse, squares, and text are drawn with alpha 0.1. Overlapping regions have higher alpha values.
How would I map every pixel in this image with alpha 0.1 to rgba(255,0,0,1)
and everywhere with alpha 0.2 to rgba(0,255,0,1)
(and interpolate the replacement colors between those two alpha values on the anti-aliased edges)? More colors would be assigned to all other alpha values up to 1.
Easy to make such a lookup table.
But how to apply to image with a svg filter?
You can achieve it, but it is a multistep process, and you'll have to see about performance and maintainability yourself.
My example code divides the opacity values into four equally-spaced ranges: 0.2...0.4...0.6...0.8...1. The range 0...0.2 is mapped to transparency.
For each of the four colored ranges you need to combine three filter primitives:
SourceAlpha
as input, and apply <feComponentTransfer>
to the alpha channel with type discrete
such that only one range is mapped to opacity="1"
<feFlood>
in the assigned color<feComposite>
and operator="in"
, so that only the mapped values are coloredThen, merge all the different monochromatic parts together with <feMerge>
.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="200">
<linearGradient id="sourceGradient" x1="0" x2="1" y1="0" y2="0">
<stop style="stop-color:#000000;stop-opacity:0" offset="0" />
<stop style="stop-color:#000000;stop-opacity:1" offset="1" />
</linearGradient>
<filter id="filter">
<feComponentTransfer in="SourceAlpha" result="step1">
<feFuncA type="discrete" tableValues="0 1 0 0 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(255,0,0)" />
<feComposite operator="in" in2="step1" result="color1" />
<feComponentTransfer in="SourceAlpha" result="step2">
<feFuncA type="discrete" tableValues="0 0 1 0 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(255,255,0)" />
<feComposite operator="in" in2="step2" result="color2" />
<feComponentTransfer in="SourceAlpha" result="step3">
<feFuncA type="discrete" tableValues="0 0 0 1 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(0,255,0)" />
<feComposite operator="in" in2="step3" result="color3" />
<feComponentTransfer in="SourceAlpha" result="step4">
<feFuncA type="discrete" tableValues="0 0 0 0 1"/>
</feComponentTransfer>
<feFlood flood-color="rgb(0,0,255)" />
<feComposite operator="in" in2="step4" result="color4" />
<feMerge>
<feMergeNode in="color1" />
<feMergeNode in="color2" />
<feMergeNode in="color3" />
<feMergeNode in="color4" />
</feMerge>
</filter>
<rect fill="url(#sourceGradient)" x="50" y="25" width="300" height="50" id="rect" />
<use xlink:href="#rect" y="100" filter="url(#filter)" />
</svg>
interpolate the replacement colors between those two alpha values on the anti-aliased edges
I am not sure I understand that, but type="table"
for component transfer results in a continuous scale. The result after merge has opacities below 1 in most places, so you would need some final steps to ramp that up again.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="200">
<linearGradient id="sourceGradient" x1="0" x2="1" y1="0" y2="0">
<stop style="stop-color:#000000;stop-opacity:0" offset="0" />
<stop style="stop-color:#000000;stop-opacity:1" offset="1" />
</linearGradient>
<filter id="filter" x="0" y="0" width="1" height="1">
<feComponentTransfer in="SourceAlpha" result="step1">
<feFuncA type="table" tableValues="0 1 0 0 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(255,0,0)" />
<feComposite operator="in" in2="step1" result="color1" />
<feComponentTransfer in="SourceAlpha" result="step2">
<feFuncA type="table" tableValues="0 0 1 0 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(255,255,0)" />
<feComposite operator="in" in2="step2" result="color2" />
<feComponentTransfer in="SourceAlpha" result="step3">
<feFuncA type="table" tableValues="0 0 0 1 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(0,255,0)" />
<feComposite operator="in" in2="step3" result="color3" />
<feComponentTransfer in="SourceAlpha" result="step4">
<feFuncA type="table" tableValues="0 0 0 0 1"/>
</feComponentTransfer>
<feFlood flood-color="rgb(0,0,255)" />
<feComposite operator="in" in2="step4" result="color4" />
<feMerge result="merged">
<feMergeNode in="color1" />
<feMergeNode in="color2" />
<feMergeNode in="color3" />
<feMergeNode in="color4" />
</feMerge>
<feComponentTransfer in="SourceAlpha" result="capped">
<feFuncA type="discrete" tableValues="0 1 1 1 1"/>
</feComponentTransfer>
<feComposite operator="atop" in="merged" in2="capped" />
</filter>
<rect fill="url(#sourceGradient)" x="50" y="25" width="300" height="50" id="rect" />
<use xlink:href="#rect" y="100" filter="url(#filter)" />
</svg>