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>