<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    
<head>
<style>

.protanopia {
    -webkit-filter: url(#protanopia);
    -moz-filter: url(#protanopia);
    -ms-filter: url(#protanopia);
    -o-filter: url(#protanopia);
    filter: url(#protanopia);
}

.deuteranopia {
    -webkit-filter: url(#deuteranopia);
    -moz-filter: url(#deuteranopia);
    -ms-filter: url(#deuteranopia);
    -o-filter: url(#deuteranopia);
    filter: url(#deuteranopia);
}

.tritanopia {
    -webkit-filter: url(#tritanopia);
    -moz-filter: url(#tritanopia);
    -ms-filter: url(#tritanopia);
    -o-filter: url(#tritanopia);
    filter: url(#tritanopia);
}
</style>
</head>

<body>

<!-------------------------------------------------------------------
    DaltonLens SVG filters to simulate color vision deficiencies 
    
    https://daltonlens.org/opensource-cvd-simulation/ for a
    discussion of the various methods.     

    The various matrices were generated from DaltonLens-Python.

    It is very important for these filters to get applied in     
    linearRGB, which is supposed to be the default, but never
    hurts to specify it explicitly.
-------------------------------------------------------------------->
<svg style='height: 0; width: 0; padding: 0; margin: 0; line-height: 0;'>
    <!-- Single matrix approximation of Viénot, Brettel & Mollon 1999 -->
    <filter id="protanopia" color-interpolation-filters="linearRGB">
        <feColorMatrix type="matrix" in="SourceGraphic" values="
            0.10889,0.89111,-0.00000,0,0
            0.10889,0.89111,0.00000,0,0
            0.00447,-0.00447,1.00000,0,0
            0,0,0,1,0"
        />
    </filter>

    <!-- Single matrix approximation of Viénot, Brettel & Mollon 1999 -->
    <filter id="deuteranopia" color-interpolation-filters="linearRGB">
        <feColorMatrix type="matrix" in="SourceGraphic" values="
            0.29031,0.70969,-0.00000,0,0
            0.29031,0.70969,-0.00000,0,0
            -0.02197,0.02197,1.00000,0,0
            0,0,0,1,0"
        />
    </filter>

    <!-- 
        Brettel, Viénot & Mollon 1997 algorithms with two projection planes.

        This is the only approach I know that is supposed to be reasonably
        accurate for tritanopia, the single matrix approaches are NOT accurate.
    -->
    <filter id="tritanopia" color-interpolation-filters="linearRGB">
        <!-- 
            Projection 1, with a special alpha that encodes the separation plane.
            If dot(rgb, n) > 0, then use projection 1, otherwise use projection 2.
            This is encoded in alpha by:
                - Applying a 1.0 factor on the source alpha so that 0 input alpha remains 0
                - Subtracting 0.2 so that negative values become < 0.8 and position values >= 0.8
                - It is important to normalize the factors to keep a good numerical accuracy
                  and to keep a large alpha threshold since the RGB values are then stored
                  premultiplied by alpha.
                - This assumes that negative values get clipped to 0, and positive
                  values clipped to 1, without overflowing, etc. Which seems to be the case
                  on all browsers.
          -->
        <feColorMatrix type="matrix" in="SourceGraphic" result="ProjectionOnPlane1" values="
            1.01354, 0.14268, -0.15622, 0, 0
            -0.01181, 0.87561, 0.13619, 0, 0
            0.07707, 0.81208, 0.11085, 0, 0
            7.92482, -5.66475, -2.26007, 1, -0.2"
        />
        <!-- 
            Binarize alpha. 5 values means the last chunk will start at 0.8.
            All the values below 0.8 will become 0 (correspond to the dot
            product with the separation plane being negative) and above will become 1
        -->        
        <feComponentTransfer in="ProjectionOnPlane1" result="ProjectionOnPlane1">
            <feFuncA type="discrete" tableValues="0 0 0 0 1"/>
        </feComponentTransfer>

        <feColorMatrix type="matrix" in="SourceGraphic" result="ProjectionOnPlane2" values="
            0.93337, 0.19999, -0.13336, 0, 0
            0.05809, 0.82565, 0.11626, 0, 0
            -0.37923, 1.13825, 0.24098, 0, 0
            0,0,0,1,0"
        />

        <!-- Uncomment the debug black matrix to see which pixels go to which plane -->
        <!-- <feColorMatrix type="matrix" in="SourceGraphic" result="ProjectionOnPlane2" values="0,0,0,0,0 0,0,0,0,0 0,0,0,0,0 0,0,0,1,0"/> -->

        <!-- Blend the two projections, picking one or the other depending on alpha. -->
        <feBlend in="ProjectionOnPlane1" in2="ProjectionOnPlane2" mode="normal"/>
    </filter>
</svg>
<!------------------------------------------------------------------>


<h2>Normal</h2>
<div class="normal">
<img src="https://daltonlens.org/images/rgbspan.png">
</div>

<h2>Protanopia (Viénot, Brettel & Mollon 1999)</h2>
<div class="protanopia">
<img src="https://daltonlens.org/images/rgbspan.png">
</div>

<h2>Deuteranopia (Viénot, Brettel & Mollon 1999)</h2>
<div class="deuteranopia">
<img src="https://daltonlens.org/images/rgbspan.png">
</div>

<h2>Tritanopia (Brettel, Viénot & Mollon 1997)</h2>
<div class="tritanopia">
<img src="https://daltonlens.org/images/rgbspan.png">
</div>

</body>
</html>