Search code examples
htmlsvgsvg-filters

Composite of two filtered SVG files


I am trying to implement a visual 'diff' strategy for printed circuit boards. The intent is to highlight 'new' additions in blue and old parts of the design (removed) in red. I have two b&w svg files each of which represent a particular revision of a single layer of the board. These are generated programatically. They look something like this png image of example svg- I cannot upload an .svg directly but there are a couple of examples here;

3c9fec_F_Cu.svg

75d8f4_F_Cu.svg

I have applied two separate feColorMatrix filters, one filter to version 1 & the other version 2 and have then placed both filtered images, positioned absolutely so they overlay each other, within a single <div>.

This works in Chrome but fails in other browsers (have tried both Safari and Firefox).

Result in Chrome (desired):

I have attempted to apply the feColorMatrix filter directly and merge the result files but that does not work at all in any browser.

My current successful code (for Chrome):

.gallery {
  border: 1px solid #ccc;
}

.gallery:hover {
  border: 1px solid #777;
}

.gallery img {
  width: 100%;
  height: auto;
}
<div class="gallery">
<svg width="1123px" height="794px" viewBox="50 0 600 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
                <defs>
                    <filter id="f1" x="0%" y="0%" width="100%" height="100%">
                        <feColorMatrix result="original" id="c1" type="matrix" values="1   0   0   0   0
                                                    0   1   0   1   0
                                                    0   0   1   1   0
                                                    0   0   0   1  0 " />
                    </filter>
                    <filter id="f2" x="0%" y="0%" width="100%" height="100%">
                        <feColorMatrix result="original" id="c2" type="matrix" values="1   0   0   1   0
                                            0   1   0   0   0
                                            0   0   1   0   0
                                            0   0   0   .5   0" />
                    </filter>

                </defs>
            <image x="0" y="0" width="100%" filter="url(#f1)" xlink:href="../../75d8f4/ThermocoupleLogger-F_Cu.svg" style="position:absolute;" />    
            <image x="0" y="0" width="100%" filter="url(#f2)" xlink:href="../../3c9fec/ThermocoupleLogger-F_Cu.svg" style="position:absolute;" />
            </svg>
    </div>

My alternative attempt with a feMerge:

<div class="gallery">
<svg width="1123px" height="794px" viewBox="50 0 600 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
                <defs>
                    <filter id="f1" x="0%" y="0%" width="100%" height="100%">
                    <feColorMatrix xlink:href="../../75d8f4/ThermocoupleLogger-F_Cu.svg" result="one" type="matrix" 
                                    values="1   0   0   0   0
                                            0   1   0   1   0
                                            0   0   1   1   0
                                            0   0   0   1  0 " />
                    </filter>

                    <filter id="f2" x="0%" y="0%" width="100%" height="100%">
                    <feColorMatrix xlink:href="../../3c9fec/ThermocoupleLogger-F_Cu.svg" result="two" type="matrix"
                                    values="1   0   0   1   0
                                            0   1   0   0   0
                                            0   0   1   0   0
                                            0   0   0   .5   0" />
                    </filter>

                    <filter id="composite">
                        <feMerge>
                        <feMergeNode in="one"/>
                        <feMergeNode in="two"/>
                        </feMerge>
                    </filter>

                </defs>

<g filter="url(#composite)"></g>

</svg>
</div>

I would value any advice that would allow me to achieve this with some degree of cross browser compatibility.


Solution

  • Your first version is mostly right - but add a height attribute directly to your image tag - SVG is fussy about dimensions & Chrome often accepts things it shouldn't. img and image tags are different elements, so that height:auto isn't applying to your image tags. Also - there is a limited number of CSS styles that apply to SVG sub-elements - when in doubt stick to SVG attributes. And remove the position: absolute - positioning in SVG is absolute by default.

    The second version won't work for numerous reasons. feColorMatrix doesn't take an xlink:href as an input - you have to import an external file into a filter using feImage first and add a result attribute to it. You can then reference the result in the inattribute of your feColorMatrix. Filters can only reference a result within the same filter - so referencing one and two from the id=composite filter won't work. Lastly, filtering an empty g tag won't work - it doesn't have any dimensions so the filter area is zero. So ... use this tweaked version of your first try.

    .gallery {
      border: 1px solid #ccc;
    }
    
    .gallery:hover {
      border: 1px solid #777;
    }
    
    .gallery img {
      width: 100%;
      height: auto;
    }
    <div class="gallery">
    <svg width="1123px" height="794px" viewBox="50 0 600 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
                    <defs>
                        <filter id="f1" x="0%" y="0%" width="100%" height="100%">
                            <feColorMatrix result="original" id="c1" type="matrix" values="1   0   0   0   0
                                                        0   1   0   1   0
                                                        0   0   1   1   0
                                                        0   0   0   1  0 " />
                        </filter>
                        <filter id="f2" x="0%" y="0%" width="100%" height="100%">
                            <feColorMatrix result="original" id="c2" type="matrix" values="1   0   0   1   0
                                                0   1   0   0   0
                                                0   0   1   0   0
                                                0   0   0   .5   0" />
                        </filter>
    
                    </defs>
                <image x="0" y="0" height="100%" width="100%" filter="url(#f1)" xlink:href="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/beacon.svg"  />    
                <image x="0" y="0" height="100%" width="100%" filter="url(#f2)" xlink:href="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/adobe.svg"  />
                </svg>
        </div>

    You can also do a more elegant version by using feComposite's xor operator. Mouse over to see the effect.

    svg :hover {
      filter: url(#diff);
    }
    <svg width="1600px" height="800px" viewBox="0 0 1600 800">
    <defs>
      <filter id="diff" x="20%" y="0%" width="80%" height="100%" >
        <feColorMatrix type="matrix" values="0 0 0 0 0  0 0 0 0 0   0 0 0 0 1   0 0 0 1 0" result="blue-original"/>
        <feImage x="-100" y="0" width="800" height="600" xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/32648/SVG-Test%201.svg">
        </feImage>
           <feColorMatrix type="matrix" values="0 0 0 0 1  0 0 0 0 0   0 0 0 0 0   0 0 0 1 0"/> 
        <feComposite operator="xor" in="blue-original"/>
      </filter>
      </defs>
    
    
    
    <image  xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/32648/SVG-test2.svg" x="-100" y="0" width="800" height="600" preserveAspectRatio="xMinYMin slice"/>
      
      <image  xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/32648/SVG-Test%201.svg" x="500" y="0" width="800" height="600" preserveAspectRatio="xMinYMin slice"/>
      
    </svg>