google-chrome - How to disable gradient dithering in browsers?
问题描述
Situation/Problem
When making a SVG filter, I stumbled upon something strange. When converting the original CSS gradient to R, G or B color only (whichever channel has the highest value), I get dithering in the gradient.
Question
While this usually is a good thing for visual performance, it now messes up the calculations/masking in my SVG <filter>
.
Is there a way to disable this dithering? Or to prevent it from occurring?
Example
* { 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>
Screenshot
Here you have two color wheel bars. The bottom one has the SVG filter applied to it. The filter should show full red on colors where red is the (shared) highest channel, then green, then blue. Ideally it would show 4 blocks that are, if added, have almost equal width. But as you can see, the dithering messes this up, and thus the blocks are dithered areas. For very light color gradients it's even worse.
Platform: Windows 10 - Browser: Chrome version 93.0.4577.82 (64-bit) (But happens in Edge as well.)
Notes:
- I tried adding
transform: translateZ(0)
to the element to force it to hardware acceleration, but it makes no difference. - I found a thread on adding dithering to Chromium browsers, but not sure if this has happened.
Update
As suggested by Michael, I tried using a blur before applying the rest of the filter. By adding <feGaussianBlur stdDeviation="1" in="SourceGraphic" result="blurred-img" />
to the filter and using blurred-img
instead of SourceGraphic
. But as you can see from the screenshot below, it does not disable the dithering or deals with the 'troubled' pixels.
The trouble there is that they probably have a very small positive value because of calculations, but so small that dithering kicks in instead of assuming 0 opacity.
With blur of 1 px
FYI
So although it does not really have something to do with the question, I will explain the use case a bit.
In short, this filter will be used to effect a specific range of colors of the original image based on their hue value.
In order to do that, this filter above makes a mask that will ‘select’ a certain range of colors. The result can then be put through the actual effect and then pasted over the original image. For example all red tones in an image get hue rotated.
解决方案
撤消抖动的最简单方法是向源图形添加一个小模糊,并使用该结果而不是 SourceGraphic 作为输入。
推荐阅读
- c# - 如何在文本框中输入 10 个字符后自动添加空格
- php - 显示图像而不是变量的值
- javascript - 如何在 Angular 中设置大型动态文本字符串的样式?
- kubernetes - K8s Pod 生命周期钩子
- c# - 映射到现有对象 ef core 后的 Automapper 无法跟踪更改
- c++ - 如何修复“未定义的引用 SetDIBitsToDevice”,甚至已经链接库“gdi32.lib”?
- node.js - 我的 mern 应用程序无法在 heroku 上运行。我该如何解决这个错误?
- javascript - 如何在 AJAX jQuery 中一个一个地获取元素?
- css - Angular 包的水平滚动(带箭头)
- android - gnustl (libstdc++) 到 llvm (libc++) 错误