Home > Enterprise >  Why isn't this feDisplacementMap filter working?
Why isn't this feDisplacementMap filter working?

Time:12-25

I'm trying to build an image within an SVG <defs> element for use in the in2 attribute of a displacement map filter. However, the displacement map filter isn't warping the element it's applied to.

The displacement map filter is being applied to a grid pattern so that I can see how the displacement works:

<svg viewBox="0 0 400 400" width="400" height="400">
  <defs>
    <pattern id="grid" viewBox="0,0,10,10" width="2.5%" height="2.5%">
      <rect x="0" y="0" width="1" height="10"/>
      <rect x="0" y="0" width="10" height="1"/>
    </pattern>
  </defs>
  <rect x="0" y="0" width="400" height="400" fill="url(#grid)"/>
</svg>

The image I'm intending to use as the in2 attribute of the displacement map is complex. When I just reference it via an <feImage> element within the filter, and apply that filter to the grid pattern, I see the image I've built, as expected:

<svg viewBox="0 0 400 400" width="400" height="400">
  <defs>
    <linearGradient id="gradientRed">
      <stop offset="0%" stop-color="rgba(255,0,0,0)"/>
      <stop offset="100%" stop-color="rgba(255,0,0,1)"/>
    </linearGradient>
    <linearGradient id="gradientGreen" gradientTransform="rotate(90)">
      <stop offset="0%" stop-color="rgba(0,255,0,0)"/>
      <stop offset="100%" stop-color="rgba(0,255,0,1)"/>
    </linearGradient>
    <rect id="rectRed" width="400" height="400" fill="url(#gradientRed)"/>
    <rect id="rectGreen" width="400" height="400" fill="url(#gradientGreen)"/>
    <rect id="rectBlack" width="400" height="400" fill="black"/>
    <filter id="filterIdentity">
      <feImage result="imageRed" href="#rectRed" x="0" y="0" width="400" height="400"/>
      <feImage result="imageGreen" href="#rectGreen" x="0" y="0" width="400" height="400"/>
      <feImage result="imageBlack" href="#rectBlack" x="0" y="0" width="400" height="400"/>
      <feBlend mode="screen" in="imageRed" in2="imageGreen" result="imageRedGreen"/>
      <feBlend mode="screen" in="imageRedGreen" in2="imageBlack"/>
    </filter>
    <mask id="maskCircle">
      <rect width="400" height="400" fill="black"/>
      <circle cx="200" cy="200" r="200" fill="white"/>
    </mask>
    <g id="shapeIdentity">
      <rect width="400" height="400" fill="black"/>
      <rect width="400" height="400" filter="url(#filterIdentity)" mask="url(#maskCircle)"/>
    </g>
    <filter id="filterDisplace">
      <feImage result="imageDisplace" href="#shapeIdentity" x="0" y="0" width="400" height="400"/>
    </filter>
    <pattern id="grid" viewBox="0,0,10,10" width="2.5%" height="2.5%">
      <rect x="0" y="0" width="1" height="10"/>
      <rect x="0" y="0" width="10" height="1"/>
    </pattern>
  </defs>
  <rect x="0" y="0" width="400" height="400" fill="url(#grid)" filter="url(#filterDisplace)"/>
</svg>

However when I use that same <feImage> element as the in2 attribute of a <feDisplacementMap> element, the filter does not warp the grid pattern it's applied to.

<svg viewBox="0 0 400 400" width="400" height="400">
  <defs>
    <linearGradient id="gradientRed">
      <stop offset="0%" stop-color="rgba(255,0,0,0)"/>
      <stop offset="100%" stop-color="rgba(255,0,0,1)"/>
    </linearGradient>
    <linearGradient id="gradientGreen" gradientTransform="rotate(90)">
      <stop offset="0%" stop-color="rgba(0,255,0,0)"/>
      <stop offset="100%" stop-color="rgba(0,255,0,1)"/>
    </linearGradient>
    <rect id="rectRed" width="400" height="400" fill="url(#gradientRed)"/>
    <rect id="rectGreen" width="400" height="400" fill="url(#gradientGreen)"/>
    <rect id="rectBlack" width="400" height="400" fill="black"/>
    <filter id="filterIdentity">
      <feImage result="imageRed" href="#rectRed" x="0" y="0" width="400" height="400"/>
      <feImage result="imageGreen" href="#rectGreen" x="0" y="0" width="400" height="400"/>
      <feImage result="imageBlack" href="#rectBlack" x="0" y="0" width="400" height="400"/>
      <feBlend mode="screen" in="imageRed" in2="imageGreen" result="imageRedGreen"/>
      <feBlend mode="screen" in="imageRedGreen" in2="imageBlack"/>
    </filter>
    <mask id="maskCircle">
      <rect width="400" height="400" fill="black"/>
      <circle cx="200" cy="200" r="200" fill="white"/>
    </mask>
    <g id="shapeIdentity">
      <rect width="400" height="400" fill="black"/>
      <rect width="400" height="400" filter="url(#filterIdentity)" mask="url(#maskCircle)"/>
    </g>
    <filter id="filterDisplace">
      <feImage result="imageDisplace" href="#shapeIdentity" x="0" y="0" width="400" height="400"/>
      <feDisplacementMap in="SourceGraphic" in2="imageDisplace" scale="20" yChannelSelector="R" xChannelSelector="G"/>
    </filter>
    <pattern id="grid" viewBox="0,0,10,10" width="2.5%" height="2.5%">
      <rect x="0" y="0" width="1" height="10"/>
      <rect x="0" y="0" width="10" height="1"/>
    </pattern>
  </defs>
  <rect x="0" y="0" width="400" height="400" fill="url(#grid)" filter="url(#filterDisplace)"/>
</svg>

What's going on here?

CodePudding user response:

Well you're tripping over a bug in Chrome. Here is a version that works in Chrome by inlining your displacementMap image as a separate SVG fragment within a data URI fed to an feImage. Please note that the syntax here is incorrect - but correct syntax {xlink:href = url(...)} does not work. (Note - this doesn't work in Firefox because Firefox doesn't accept fragment references for feImage.)

<svg viewBox="0 0 400 400" width="400" height="400">
  <defs>
    <filter id="filterDisplace">
      <feImage result="imageDisplace" xlink:href="data:image/svg xml," x="0" y="0" width="400" height="400"/>
<feDisplacementMap in="SourceGraphic" in2="imageDisplace" scale="20" yChannelSelector="R" xChannelSelector="G"/>
    </filter>
    
    <pattern id="grid" viewBox="0,0,10,10" width="2.5%" height="2.5%">
      <rect x="0" y="0" width="1" height="10"/>
      <rect x="0" y="0" width="10" height="1"/>
    </pattern>
  </defs>
  <g filter="url(#filterDisplace)">
  <rect x="0" y="0" width="400" height="400" fill="url(#grid)" />
  </g>
</svg>

  • Related