svg

How to use tableValues in feComponentTransfer type "table"


I am learning about SVG. So far it has been great but now I am stuck on the color transfer using table values. Here is an example:

<feFuncR type="table" tableValues="1 0 0 0"></feFuncR>

This function changed a solid bar of red color to black. I want to know how the tableValues attribute works so that I can figure out the color changes myself. I couldn't find any article that explained it in detail.


Solution

  • How feComponentTransfer works is described in the SVG specification.

    How type="table" works is that you supply a list of N values describing N-1 interpolation regions. These become the output values (V0 to V3) at various interpolation points.

    Your table is "1 0 0 0". That's 4 values specifying the start and end interpolation values of 3 interpolation regions.

    Input values 0 -> 0.33 are mapped to output values V0 -> V1 (1 -> 0).
    Input values 0.33 -> 0.66 are mapped to output values V1 -> V2 (0 -> 0).
    Input values 0.66 -> 1.00 are mapped to output values V2 -> V3 (0 -> 0).

    So an input value of 1 for Red, which is what you said you were using, will be mapped to 0. That's why your red element turned black.

    An input value of 0 would be mapped to 1. An input value of 0.33 would be mapped to 0. An input value of 0.2 would be mapped to:

    Vk + (C - k/n)*n * (Vk+1 - Vk)
    1  + (0.2 - (0/3)) * 3 * (0 - 1) = 0.4
    

    Where Vk is the first table value (1), and Vk+1 is the second table value (0). That's because 0.2 is in the first region (between 0 and 0.33).

    Here's a graph to show how input maps to output.

    <svg width="500" height="300"
         font-family="sans-serif" font-size="16" text-anchor="middle">
      <g fill="none" stroke="lightgray" stroke-width="2">
        <line x1="100" y1="40" x2="100" y2="160"/>
        <line x1="90" y1="150" x2="410" y2="150"/>
        <line x1="90" y1="50" x2="100" y2="50"/>
        <line x1="200" y1="150" x2="200" y2="160"/>
        <line x1="300" y1="150" x2="300" y2="160"/>
        <line x1="400" y1="150" x2="400" y2="160"/>
      </g>
      <g fill="lightgray">
        <text x="50" y="100">Output</text>
        <text x="250" y="195">Input</text>
        <text x="80" y="55">1</text>
        <text x="80" y="155">0</text>
        <text x="100" y="175">0</text>
        <text x="200" y="175">0.33</text>
        <text x="300" y="175">0.66</text>
        <text x="400" y="175">1</text>
        <text x="50" y="245">Input</text>
        <text x="50" y="275">Output</text>
      </g>
      <g fill="black">
        <text x="100" y="30">1</text>
        <text x="200" y="30">0</text>
        <text x="300" y="30">0</text>
        <text x="400" y="30">0</text>
      </g>
      <polyline fill="none" stroke="black" stroke-width="4"
                points="100,50, 200,150, 300,150, 400,150"/>
    
      <!-- filter a graduated rectangle to show ouput values -->
      <rect x="100" y="230" width="300" height="20" fill="url(#red-gradient"/>
      <rect x="100" y="260" width="300" height="20" fill="url(#red-gradient"
             filter="url(#example)"/>
      
      <linearGradient id="red-gradient">
        <stop offset="0" stop-color="rgb(0,0,0)"/>
        <stop offset="1" stop-color="rgb(255,0,0)"/>
      </linearGradient>
    
      <filter id="example" color-interpolation-filters="sRGB">
        <feComponentTransfer>
          <feFuncR type="table" tableValues="1 0 0 0"/>
        </feComponentTransfer>
      </filter>
    </svg>