Search code examples
svgtexturessvg-filters

Apply a texture to an image in SVG


I'm trying to apply a texture to an image

Original

enter image description here

Texture

enter image description here

Result (made with PHP GD)

enter image description here

But with SVG, the closest I got is this result

<svg preserveAspectRatio="none" width="500" height="500" viewBox="0 0 500 500">
    <defs>
    
      <filter id="texture">
        <feImage href="https://i.imgur.com/pjWcnJs.jpg" result="texture-img"/>
        <feBlend in="SourceGraphic" in2="texture-img" mode="multiply"/>
      </filter>
    
    </defs>
    
    <g>
        <g filter="url(#texture)">
            <image x="0" y="0" href="https://i.imgur.com/oVEdsQt.png" opacity="1" width="500" height="500" />
        </g>
    </g>
    
</svg>

fiddle

Is there another way which won't texturize transparent pixels?


Solution

  • I found a solution which is to pass the texture through a composite filter which would crop it to the source image

    <svg preserveAspectRatio="none" width="500" height="500" viewBox="0 0 500 500">
        <defs>
        
          <filter id="texture">
            <feImage href="https://i.imgur.com/pjWcnJs.jpg" result="texture-img"/>
            <feComposite in2="SourceGraphic" operator="in" in="texture-img" result="composite"/>
            <feBlend in="SourceGraphic" in2="composite" mode="multiply"/>
          </filter>
        
        </defs>
        
        <g>
            <g filter="url(#texture)">
                <image x="0" y="0" href="https://i.imgur.com/oVEdsQt.png" opacity="1" width="500" height="500" />
            </g>
        </g>
        
    </svg>

    To tile the texture, I used feTile like this

    <svg preserveAspectRatio="none" width="500" height="500" viewBox="0 0 500 500">
        <defs>
        
          <filter id="texture" x="0" y="0" width="100%" height="100%">
            <feImage href="https://i.imgur.com/gWH7NLm.jpg" result="texture-img" width="256" height="256"/>
            <feTile in="texture-img" x="0" y="0" width="100%" height="100%" result="tile" ></feTile>
            <feComposite in2="SourceGraphic" operator="in" in="tile" result="composite"/>
            <feBlend in="SourceGraphic" in2="composite" mode="multiply"/>
          </filter>
        
        </defs>
        
        <g>
            <g filter="url(#texture)">
                <image x="0" y="0" href="https://i.imgur.com/oVEdsQt.png" opacity="1" width="500" height="500" />
            </g>
        </g>
        
    </svg>

    I got the idea by checking how inkscape applies material textures