【问题标题】:How to disable gradient dithering in browsers?如何在浏览器中禁用渐变抖动?
【发布时间】: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。)

注意事项:

更新

按照 Michael 的建议,我在应用过滤器的其余部分之前尝试使用模糊。通过将&lt;feGaussianBlur stdDeviation="1" in="SourceGraphic" result="blurred-img" /&gt; 添加到过滤器并使用blurred-img 而不是SourceGraphic。但正如您从下面的屏幕截图中看到的那样,它不会禁用抖动或处理“有问题”的像素。

问题在于,由于计算,它们可能具有非常小的正值,但太小以至于抖动开始而不是假设不透明度为 0。

模糊为 1 像素

仅供参考

所以虽然和问题没什么关系,但我还是稍微解释一下用例吧。

简而言之,此滤镜将用于根据色调值影响原始图像的特定颜色范围。

为了做到这一点,上面的这个过滤器制作了一个蒙版,可以“选择”一定范围的颜色。然后可以通过实际效果将结果粘贴到原始图像上。例如,图像中的所有红色调都会进行色调旋转。

【问题讨论】:

  • 您需要在此处在您的问题中发布minimal reproducible example,并在任何其他网站上发布not a link
  • 在 Chrome 93/Win10(64 位)上 - 我无法重现您在正常渐变中显示的问题。在我的机器上,它产生了预期的结果。 (非常浅的渐变确实显示了问题)
  • 由于过滤器代码有点长,所以使用了JS Fiddle。但我会编辑问题。

标签: google-chrome svg filter gradient dithering


【解决方案1】:

撤消抖动的最简单方法是向源图形添加一个小模糊,并使用该结果而不是 SourceGraphic 作为输入。

【讨论】:

  • 不幸的是,添加一个小的模糊并不能消除抖动 (see here)。它只是使某个抖动的区域更宽,更“块状”。抖动的原因可能是由于浮点数和浏览器或图形引擎试图解释不透明度的“小于 1”值并通过使用抖动来修复它。
  • stdDeviation=3 摆脱了 Chrome/Win64 上的抖动? YMMV 显然
  • 我没有尝试过3,但是您应用滤镜的图像会变得模糊。我想保持图像的原始清晰度。 (此滤镜是一种准备,可以根据色调对某些颜色范围应用效果。因此,它可以准确地标记 r/g/b 而不会抖动或模糊图像,这一点很重要。)
  • 我想你可能想用一个完整的例子更全面地解释用例,这样人们就可以弄清楚如何解决你的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-05
相关资源
最近更新 更多