New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[filter-effects] Browsers don't match spec for feDisplacementMap #113
Comments
@AmeliaBR I didn't look at the spec text yet. For me it seems that Firefox and WebKit behave the same while Chromium doesn't displace anything. I have no comparison to Edge. |
Screenshots of the CodePen on Windows 10, for comparison. As explained in the pen description:
|
So WebKit, Chrome and Firefox come to the same result. Here the examples of Note that Adobe Illustrator has some clipping and position issues with nested Here the pure SVG code that I used. @Tavmjong could you add the results of Inkscape please? <svg width="600" height="600" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
* {
stroke-width: 10;
}
.ref {stroke: tomato; fill: none;}
.base-position {stroke: darkRed; }
.test {stroke: forestGreen; fill: none;}
</style>
<svg viewBox="0 0 100 100" width="100" height="100">
<defs>
<path id="p" d="M30,70L70,30" />
<g id="refs" class="ref">
<use xlink:href="#p" transform="translate(-20,-20)"/>
<use xlink:href="#p" transform="translate(-10,-10)"/>
<use xlink:href="#p" class="base-position"/>
<use xlink:href="#p" transform="translate(10,10)"/>
<use xlink:href="#p" transform="translate(20,20)"/>
</g>
</defs>
<filter id="solid-white" filterUnits="userSpaceOnUse">
<feFlood flood-color="#fff"
flood-opacity="1" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="40" in="SourceGraphic" />
</filter>
<use xlink:href="#refs" />
<use xlink:href="#p" class="test"
filter="url(#solid-white)"/>
</svg>
<svg viewBox="0 0 100 100" x="100" width="100" height="100">
<filter id="half-white" filterUnits="userSpaceOnUse">
<feFlood flood-color="#fff"
flood-opacity="0.5" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="40" in="SourceGraphic" />
</filter>
<use xlink:href="#refs" />
<use xlink:href="#p" class="test"
filter="url(#half-white)"/>
</svg>
<svg viewBox="0 0 100 100" x="200" width="100" height="100">
<filter id="transparent-white" filterUnits="userSpaceOnUse">
<feFlood flood-color="#fff"
flood-opacity="0" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="40" in="SourceGraphic" />
</filter>
<use xlink:href="#refs" />
<use xlink:href="#p" class="test"
filter="url(#transparent-white)"/>
</svg>
<svg viewBox="0 0 100 100" transform="translate(0,100)" y="100" width="100" height="100">
<filter id="solid-gray" filterUnits="userSpaceOnUse">
<feFlood flood-color="#888"
flood-opacity="1" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="40" in="SourceGraphic" />
</filter>
<use xlink:href="#refs" />
<use xlink:href="#p" class="test"
filter="url(#solid-gray)"/>
</svg>
<svg viewBox="0 0 100 100" x="100" y="100" width="100" height="100">
<filter id="half-gray" filterUnits="userSpaceOnUse">
<feFlood flood-color="#888"
flood-opacity="0.5" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="40" in="SourceGraphic" />
</filter>
<use xlink:href="#refs" />
<use xlink:href="#p" class="test"
filter="url(#half-gray)"/>
</svg>
<svg viewBox="0 0 100 100" x="200" y="100" width="100" height="100">
<filter id="transparent-gray" filterUnits="userSpaceOnUse">
<feFlood flood-color="#888"
flood-opacity="0" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="40" in="SourceGraphic" />
</filter>
<use xlink:href="#refs" />
<use xlink:href="#p" class="test"
filter="url(#transparent-gray)"/>
</svg>
<svg viewBox="0 0 100 100" y="200" width="100" height="100">
<filter id="solid-black" filterUnits="userSpaceOnUse">
<feFlood flood-color="#000"
flood-opacity="1" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="40" in="SourceGraphic" />
</filter>
<use xlink:href="#refs" />
<use xlink:href="#p" class="test"
filter="url(#solid-black)"/>
</svg>
<svg viewBox="0 0 100 100" x="100" y="200" width="100" height="100">
<filter id="half-black" filterUnits="userSpaceOnUse">
<feFlood flood-color="#000"
flood-opacity="0.5" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="40" in="SourceGraphic" />
</filter>
<use xlink:href="#refs" />
<use xlink:href="#p" class="test"
filter="url(#half-black)"/>
</svg>
<svg viewBox="0 0 100 100" x="200" y="200" width="100" height="100">
<filter id="transparent-black" filterUnits="userSpaceOnUse">
<feFlood flood-color="#000"
flood-opacity="0" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="40" in="SourceGraphic" />
</filter>
<use xlink:href="#refs" />
<use xlink:href="#p" class="test"
filter="url(#transparent-black)"/>
</svg>
</svg>
|
It looks like Inkscape (at least, the stable 0.92.1 that I have) doesn't support To make the test work, I took Dirk's version and replaced every instance of |
@AmeliaBR To summarize your confusion with the spec text: You are asking if implementations should use premultiplied colors or non-premultiplied colors when selecting the channels R, G or B? The introduction (https://drafts.fxtf.org/filter-effects/#FilterPrimitivesOverviewIntro) states that all primitives operate with premultiplied colors unless stated differently.
So no scaling by alpha from in2, the displacement map, for R, G or B channel. If implementations do so, then they are in mistake. Quite frankly, I personally would not think that we should change the specification text here. Using premultiplied colors from the displacement map is logically incorrect. What we should do is writing tests (which you did) and report issues to browser vendors. Edit: Ergo, your expectations are correct that gray should not displace the pixel of |
@AmeliaBR Note: colors with 0 alpha is a special case. In your example, That is not what the implementations see. If we assume premultiplied colors, then all colors with opacity of 0 result in a color value of 0. There is no way to compute the original unpremuliplied color anymore. So your observation that for 0 alpha all implementations are operating on transparent black makes sense unless filter-effects would require to preserve the unpremultiplied color values of the input image which it currently doesn't. It just operates on un-premultiplied color values which means an implementation needs to convert premultiplied color values back to unpremultiplied color values. Edit: This also explains the difference from Photoshop to browser implementations which does preserve un-premultiplied colors as much as possible here. |
Adding @tabatkins @svgeesus for their input: What do you think about specifying that all color values default to 0 when then alpha channel is 0 for filter primitive inputs? This allows implementations to store intermediate results in either premultiplied or un-premultiplied colors and still have consistent output regardless of the implementation details. |
That is consistent with other problems I've discovered with feTurbulence output. |
So, basically there are 2 distinct issues here:
|
@smfr Since you did a lot of changes/fixes with regard for |
@AmeliaBR wrote:
Loosing color precision is a known issue with premultiplied alpha; at the limit, an alpha of zero looses all precision and the original color is completely gone. This is unavoidable when only the premultiplied value is stored. It is avoidable and somewhat tragic, if the unpremultiplied values (separate alpha) are stored, but then "for consistency" an alpha of 0/255 results in the color channels being knocked out to black (while at an alpha of 1/255, the color channels are left as-is. IIRC this is what Photoshop does, and it bugs me). |
The premultiplied alpha question above has been covered by other commenters, but no-one has voiced any thoughts on the colorspace issue which I think is more interesting. A few observations:
Amelia, you asked whether the spec should change to match current behaviour, or whether there was anything that could be clarified. I can think of 3 options.
On balance I'd go for the last option. feDisplacement is unique in using a color channel for something other than color, and applying any sort of gamma curve to this input makes no sense. So far I haven't thought of a case where this will do the wrong thing. The other benefit is it means that the behaviour of all browsers when the feDisplacementMap input is an image continues to work. The only change required is to remove any color conversion on the input loaded from "in2" - if the input is an image this has no effect, and if the input is not, well that's the case that isn't working now. Hope that makes sense. |
To briefly follow myself up: I was digging through the "filters-displace-01-f.html" referenced above, and the PNG images used as input have a gAMA block, which alters the ColorSpace from sRGB. If this were respected it would change the position of the "bump" in the displacement map. It's not respected - an embedded ColorSpace in an image used for displacement is ignored by all current implementations. Further indication that option 3, ignoring the colorspace completely, is the right thing to do. |
Yes. The data in the color channels for the in2 image is not treated as a color. It is treated as a 0 to 255 displacement amount. Thus alpha is ignored gamma and chromaticity and ICC profiles are ignored, because the raw image data is not being interpreted as a color. The spec already states this regarding alpha, but could state the rest more clearly. |
The feDisplacement map description states "The color-interpolation-filters property only applies to the in2 source image ...". To me, that implies that the intention is that "in2" is color corrected, to sRGB or linearRGB depending on the value of that attribute. |
Huh. That seems wrong to me, and also doesn't match implementations, and should be corrected. I wonder if that text was always there or arrived more recently. |
I just want to chime in with a related issue I had with the current browsers. I attempted to populate an feImage to use as the displacement source, with a URL encoded copy of a local canvas buffer. I had the expectation that 128/256 - 0.5 = 0... but it wasn't. the mid-point was closer to 187.5; could be useful for future standards to make feImage and/or feDisplacementMap input source-generation bullet-proof - preferably too with the ability of feDisplacementMap to have a high-byte selector too i.e. |
@MeirionHughes Kind of late, but you're just missing |
The spec for
feDisplacementMap
says:If I'm reading that correctly, I would expect that, when
xChannelSelector
andyChannelSelector
are R,G,or B:If the input map is partially transparent, I'm not quite sure what should happen next. It depends on whether "premultiplied" only applies to scaling the color channel range, or whether it also includes scaling by alpha. If we do not scale the color channels by the alpha, then the alpha value should not have any effect unless the value of
xChannelSelector
oryChannelSelector
isA
. This is what I would expect as an author.If you do scale the color channels by alpha, which I think is a more literal reading of the spec, then I would expect:
None of the browsers tested (Chrome 56, Firefox 53, and Edge 14) lead to any of these sets of expected results.
Solid black and white input are treated as expected in all browsers: displacement by half the scale factor, in opposing directions. But a 50% gray input in all browsers does displace the image, halfway the distance of the displacement for a solid black input.
Chrome and Firefox treat a 50% transparent input the same as a solid input of the same color, but treat a completely transparent input the same as solid black. MS Edge linearly scales the amount of displacement by the alpha of the input color.
CodePen test case
The text was updated successfully, but these errors were encountered: