Is there a way to rotate feImage or feTile pattern in an SVG?

Viewed 510

In this example, I set up a filter that textures the given element with a checkboard pattern using SVG filter effects:

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">
  
  <defs>
    <filter id="texture" x="0" y="0" width="100%" height="100%">
      <feImage width="16" height="16" result="checkerboard-image"
               xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEXMzMzLy8v////+/v7l
9thZAAAAO0lEQVR4ASXIUQ3AIBAFsJKcACQxu4/kBCAJFUu2ftbUYeWYI8G51kqU3VSCm68l
hpyH/nuWHaQH2eoF1bMYGK3LF0IAAAAASUVORK5CYII="/>
      <feTile in="checkerboard-image" result="texture" />
      <feBlend in="SourceGraphic" in2="texture" mode="multiply" />
      <feTile/>
    </filter>
  </defs>

  <image xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png"
      x="0" y="0" width="100%" height="100%" style="filter:url(#texture);"/>
</svg>

Is there a way I can rotate the whole texture, so that I get e.g. a 45-degree rotated checkboard pattern applied on the same image?

3 Answers

I figured it out. You need to wrap the image in several nested <g> groups. From the innermost group:

  1. Rotate the image in the negative desired angle
  2. Apply the filter
  3. Rotate the image in the positive desired angle

The image will remain in the original rotation, but the effect will have been applied on the rotated image, thus making the effect itself rotated.

Here's the code itself:

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">
  
  <defs>
    <filter id="texture" x="0" y="0" width="100%" height="100%">
      <feImage width="16" height="16" result="texture-image"
               xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEXMzMzLy8v////+/v7l
9thZAAAAO0lEQVR4ASXIUQ3AIBAFsJKcACQxu4/kBCAJFUu2ftbUYeWYI8G51kqU3VSCm68l
hpyH/nuWHaQH2eoF1bMYGK3LF0IAAAAASUVORK5CYII="/>
      <feTile in="texture-image" result="texture" />
      <feBlend in="SourceGraphic" in2="texture" mode="multiply" />
      <feTile/>
    </filter>
  </defs>

  
  <g transform="rotate(30)">
    <g filter="url(#texture)" >
      <g transform="rotate(-30)">
        <image xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png" x="0" y="0" width="100%" height="100%"/>
      </g>
    </g>
  </g>
</svg>

Theoretically, you could do it with a feDisplacementMap, but generating the right displacement map is quite difficult, so practically speaking, you have to rotate the checkerboard outside the filter (by redoing the checkerboard image, or using a patternTransform). Here is an example that inverts the filter inputs - it pulls in the main image via a feImage, and makes the rotated checkerboard the SourceGraphic.

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">
  
  <defs>
    <pattern id="checker-pattern" x="0" y="0" width="16" height="16" patternTransform="rotate(45) translate(-4 4)" patternUnits="userSpaceOnUse">
      <image x="0" y="0" height="16" width="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEXMzMzLy8v////+/v7l9thZAAAAO0lEQVR4ASXIUQ3AIBAFsJKcACQxu4/kBCAJFUu2ftbUYeWYI8G51kqU3VSCm68l
hpyH/nuWHaQH2eoF1bMYGK3LF0IAAAAASUVORK5CYII="/>
    </pattern>
    
    
    <filter id="texture" x="0" y="0" width="100%" height="100%">
      <feImage x="0%" y="0%" width="100%" height="100%" result="original-image"
xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png"/>
      <feBlend in="SourceGraphic" mode="multiply" />
    </filter>
  </defs>
<g filter="url(#texture)">
  <rect x="0%" y="0%" width="100%" height="100%" fill="url(#checker-pattern)"/>
  </g>
</svg>

A different solution would be using an SVG pattern, like so:

<svg viewBox='0 0 200 200' width='200' height='200' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
  <defs>
    <pattern id='_texture' width='16' height='16' patternUnits='userSpaceOnUse'  patternTransform="rotate(45)">
      <g fill='rgba(0,0,0,.3)'>
        <rect width='8' height='8'/>
        <rect x='8' y='8' width='8' height='8'/>
      </g>
    </pattern>
  </defs> 
  
  
  <image xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png"
      x="0" y="0" width="100%" height="100%" />
  
  <rect width='100%' height='100%' fill='url(#_texture)'/>
</svg>

UPDATE

this time I'm using an SVG dataURI image instead instead of your dataURI. The filter is still there. I hope it helps.

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"> 
  <defs>
    <filter id="texture" x="0" y="0" width="100%" height="100%">
      <feImage width="11.31" height="11.31" result="checkerboard-image"
               xlink:href="data:image/svg+xml,%3Csvg viewBox='0 0 11.31 11.31' width='11.31' height='11.31' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cg fill='rgba(0,0,0,.3)' transform='rotate(45 5.655 5.655)'%3E%3Crect width='8' height='8' x='1.655' y='1.655'/%3E%3C/g%3E%3C/svg%3E"/>
      <feTile in="checkerboard-image" result="texture" />
      <feBlend in="SourceGraphic" in2="texture" mode="multiply" />
      <feTile/>
    </filter>
  </defs>

  <image xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png"
      x="0" y="0" width="100%" height="100%" style="filter:url(#texture);"/>
</svg>

Related