【发布时间】:2021-11-17 20:55:21
【问题描述】:
情况/问题
在制作 SVG 滤镜时,我偶然发现了一些奇怪的东西。仅将原始 CSS 渐变转换为 R、G 或 B 颜色(无论哪个通道具有最高值)时,渐变中会出现抖动。
问题
虽然这通常对视觉性能有好处,但现在它会扰乱我的 SVG <filter> 中的计算/屏蔽。
有没有办法禁用这种抖动?还是为了防止它发生?
示例
* { margin: 0; padding: 0; box-sizing: border-box; font-family: sans-serif; }
.wheel {
display: block;
width: 100%;
height: 50px;
/* background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); */
/* background: linear-gradient(to right, #f88, #ff8, #8f8, #8ff, #88f, #f8f, #f88); */
/* background: linear-gradient(to right, #f44, #ff4, #4f4, #4ff, #44f, #f4f, #f44); */
background: linear-gradient(to right, #ffe0e0, #ffffe0, #e0ffe0, #e0ffff, #e0e0ff, #ffe0ff, #ffe0e0);
}
.very-light {
background: linear-gradient(to right, #fffafa, #fffffa, #fafffa, #faffff, #fafaff, #fffaff, #fffafa);
}
.filter {
filter: url(#max-channel);
/* Does not change gradient dithering behaviour */
/*transform: translateZ(0);*/
}
.expected {
background: linear-gradient(
to right,
#f00 0%,
#f00 calc((1/6) * 100%),
#0f0 calc((1/6) * 100%),
#0f0 calc((3/6) * 100%),
#00f calc((3/6) * 100%),
#00f calc((5/6) * 100%),
#f00 calc((5/6) * 100%),
#f00 100%
);
}
<div class="wheel">Original <code>linear-gradient()</code></div>
<div class="wheel filter"><code>linear-gradient()</code> with SVG filter applied</div>
<div class="wheel filter very-light">Even worse on very light gradient..!</div>
<div class="wheel expected">Expected result</div>
<svg
version="1.1" xmlns="http://www.w3.org/2000/svg"
width="0" height="0"
>
<filter id="max-channel" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB">
<feFlood flood-color="#ff0000" result="red" />
<feFlood flood-color="#00ff00" result="green" />
<feFlood flood-color="#0000ff" result="blue" />
<!-- Separate channels -->
<feComponentTransfer in="SourceGraphic" result="r-channel">
<feFuncR type="table" tableValues="0 1"></feFuncR>
<feFuncG type="table" tableValues="0 0"></feFuncG>
<feFuncB type="table" tableValues="0 0"></feFuncB>
</feComponentTransfer>
<feComponentTransfer in="SourceGraphic" result="g-channel">
<feFuncR type="table" tableValues="0 0"></feFuncR>
<feFuncG type="table" tableValues="0 1"></feFuncG>
<feFuncB type="table" tableValues="0 0"></feFuncB>
</feComponentTransfer>
<feComponentTransfer in="SourceGraphic" result="b-channel">
<feFuncR type="table" tableValues="0 0"></feFuncR>
<feFuncG type="table" tableValues="0 0"></feFuncG>
<feFuncB type="table" tableValues="0 1"></feFuncB>
</feComponentTransfer>
<!-- Red is max mask -->
<feBlend mode="lighten" in="SourceGraphic" in2="red" result="r-lighten" />
<feBlend mode="difference" in="SourceGraphic" in2="r-lighten" result="r-lighten-diff" />
<feColorMatrix type="matrix" in="r-lighten-diff" result="r-max-mask"
values="0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 -255 0 0 0 1"
/>
<!-- Green is max mask -->
<feBlend mode="lighten" in="SourceGraphic" in2="green" result="g-lighten" />
<feBlend mode="difference" in="SourceGraphic" in2="g-lighten" result="g-lighten-diff" />
<feColorMatrix type="matrix" in="g-lighten-diff" result="g-max-beforemask"
values="0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 -255 0 0 1"
/>
<feComposite operator="out" in="g-max-beforemask" in2="r-max-mask" result="g-max-mask" />
<!-- Blue is max mask -->
<feBlend mode="lighten" in="SourceGraphic" in2="blue" result="b-lighten" />
<feBlend mode="difference" in="SourceGraphic" in2="b-lighten" result="b-lighten-diff" />
<feColorMatrix type="matrix" in="b-lighten-diff" result="b-max-beforemask"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 -255 0 1"
/>
<feComposite operator="out" in="b-max-beforemask" in2="r-max-mask" result="b-max-beforemask2" />
<feComposite operator="out" in="b-max-beforemask2" in2="g-max-mask" result="b-max-mask" />
<feMerge>
<!-- <feMergeNode in="SourceGraphic" /> -->
<!-- <feMergeNode in="r-channel" /> -->
<!-- <feMergeNode in="r-lighten-diff" /> -->
<feMergeNode in="b-max-mask" />
<feMergeNode in="g-max-mask" />
<feMergeNode in="r-max-mask" />
</feMerge>
</filter>
</svg>
截图
这里有两个色轮条。底部应用了 SVG 过滤器。过滤器应该在红色是(共享)最高通道的颜色上显示全红色,然后是绿色,然后是蓝色。理想情况下,它将显示 4 个块,如果添加它们,它们的宽度几乎相等。 但是 如您所见,抖动将其弄乱了,因此块是抖动区域。对于非常浅的颜色渐变,情况会更糟。
平台:Windows 10 - 浏览器:Chrome 版本 93.0.4577.82(64 位)(但也适用于 Edge。)
注意事项:
- 我尝试将
transform: translateZ(0)添加到元素以强制它进行硬件加速,但没有任何区别。 - 我找到了thread on adding dithering to Chromium browsers,但不确定是否发生了这种情况。
更新
按照 Michael 的建议,我在应用过滤器的其余部分之前尝试使用模糊。通过将<feGaussianBlur stdDeviation="1" in="SourceGraphic" result="blurred-img" /> 添加到过滤器并使用blurred-img 而不是SourceGraphic。但正如您从下面的屏幕截图中看到的那样,它不会禁用抖动或处理“有问题”的像素。
问题在于,由于计算,它们可能具有非常小的正值,但太小以至于抖动开始而不是假设不透明度为 0。
模糊为 1 像素
仅供参考
所以虽然和问题没什么关系,但我还是稍微解释一下用例吧。
简而言之,此滤镜将用于根据色调值影响原始图像的特定颜色范围。
为了做到这一点,上面的这个过滤器制作了一个蒙版,可以“选择”一定范围的颜色。然后可以通过实际效果将结果粘贴到原始图像上。例如,图像中的所有红色调都会进行色调旋转。
【问题讨论】:
-
您需要在此处在您的问题中发布minimal reproducible example,并在任何其他网站上发布not a link。
-
在 Chrome 93/Win10(64 位)上 - 我无法重现您在正常渐变中显示的问题。在我的机器上,它产生了预期的结果。 (非常浅的渐变确实显示了问题)
-
由于过滤器代码有点长,所以使用了JS Fiddle。但我会编辑问题。
标签: google-chrome svg filter gradient dithering