为什么CSS滤镜hue-rotate会产生奇怪的效果?

11
我试图使用CSS滤镜模拟Photoshop的“颜色叠加”效果,但是在这个过程中发现CSS滤镜像癫痫一样不稳定。

考虑颜色#FF0000。如果我们将它的色相旋转120度,应该得到#00FF00,而将其旋转240度,应该得到#0000FF。这是理性的范畴。现在让我们进入CSS滤镜:

body { font: bold 99px Arial }
span { color: #F00; }
.daltonics-wont-notice {
    -webkit-filter: hue-rotate(120deg);
    filter: hue-rotate(120deg);
}
.precision-is-overrated {
    -webkit-filter: hue-rotate(240deg);
    filter: hue-rotate(240deg);
}
<span class="red"></span>
<span class="daltonics-wont-notice"></span>
<span class="precision-is-overrated"></span>

“应该是 #00FF00 的颜色变成了#007100,而应该是 #0000FF 的颜色变成了#0132FF。通过使用hue-rotate,色相、饱和度和亮度已经被设置为跨浏览器的无意义级别。

我需要赶上克苏鲁并弄清楚他编码的逻辑,这样我就可以绕过它。

这是一个与HSV或HSL无关的奇怪颜色空间吗?是否可能将HSV、HSL或RGB坐标转换为这个异想天开的维度?它有一个名字吗?一个标准吗?一个信奉者吗?”


1
可能是为什么色调旋转+180度和-180度不会产生原始颜色?的重复问题。 - Michael Mullany
3个回答

21

我还是不敢相信这是跨浏览器的。我的意思是,我一直在搜索颜色空间,但找不到任何一个“色调”定义有意义的地方。它们完全凭空编造了一个巨大、多刺的坚硬固体,像镀锌的愚蠢。

无论如何,我已经阅读了说明,经过仔细研究魔法咒语后,我制作了一个 JavaScript 版本的可怕破碎的 hue-rotate 算法,该算法当前正在受浏览器折磨。

这里是JSFiddle 版本,这里是代码片段:

function calculate() {
    // Get the RGB and angle to work with.
    var color = document.getElementById('color').value;
    if (! /^[0-9A-F]{6}$/i.test(color)) return alert('Bad color!');
    var angle = document.getElementById('angle').value;
    if (! /^-?[0-9]+$/i.test(angle)) return alert('Bad angle!');
    var r = parseInt(color.substr(0, 2), 16);
    var g = parseInt(color.substr(2, 2), 16);
    var b = parseInt(color.substr(4, 2), 16);
    var angle = (parseInt(angle) % 360 + 360) % 360;
    
    // Hold your breath because what follows isn't flowers.
    
    var matrix = [ // Just remember this is the identity matrix for
        1, 0, 0,   // Reds
        0, 1, 0,   // Greens
        0, 0, 1    // Blues
    ];
    
    // Luminance coefficients.
    var lumR = 0.2126;
    var lumG = 0.7152;
    var lumB = 0.0722;
    
    // Hue rotate coefficients.
    var hueRotateR = 0.143;
    var hueRotateG = 0.140;
    var hueRotateB = 0.283;
    
    var cos = Math.cos(angle * Math.PI / 180);
    var sin = Math.sin(angle * Math.PI / 180);
    
    matrix[0] = lumR + (1 - lumR) * cos - lumR * sin;
    matrix[1] = lumG - lumG * cos - lumG * sin;
    matrix[2] = lumB - lumB * cos + (1 - lumB) * sin;
    
    matrix[3] = lumR - lumR * cos + hueRotateR * sin;
    matrix[4] = lumG + (1 - lumG) * cos + hueRotateG * sin;
    matrix[5] = lumB - lumB * cos - hueRotateB * sin;
    
    matrix[6] = lumR - lumR * cos - (1 - lumR) * sin;
    matrix[7] = lumG - lumG * cos + lumG * sin;
    matrix[8] = lumB + (1 - lumB) * cos + lumB * sin;
    
    function clamp(num) {
        return Math.round(Math.max(0, Math.min(255, num)));
    }
    
    var R = clamp(matrix[0] * r + matrix[1] * g + matrix[2] * b);
    var G = clamp(matrix[3] * r + matrix[4] * g + matrix[5] * b);
    var B = clamp(matrix[6] * r + matrix[7] * g + matrix[8] * b);
    
    // Output the result
    var result = 'The original color, rgb(' + [r,g,b] + '), '
               + 'when rotated by ' + angle + ' degrees '
               + 'by the devil\'s logic, gives you '
               + 'rgb(' + [R,G,B] + '). If I got it right.';
    document.getElementById('result').innerText = result;
}
// Listen for Enter key press.
['color', 'angle'].forEach(function(i) {
    document.getElementById(i).onkeypress = function(event) {
        var e = event || window.event, c = e.which || e.keyCode;
        if (c == '13') return calculate();
    }
});
body {
    font: 14px sans-serif;
    padding: 6px 8px;
}

input {
    width: 64px;
}
<p>
    This algorithm emulates the wierd, nonsensical and completely 
    idiotic <code>hue-rotate</code> CSS filter. I wanted to know
    how it worked, because it is out of touch with any definition
    of "hue" I've ever seen; the results it produces are stupid
    and I believe it was coded under extreme influence of meth,
    alcohol and caffeine, by a scientologist listening to Death Metal.
</p>
<span>#</span>
<input type="text" id="color" placeholder="RRGGBB">
<input type="text" id="angle" placeholder="degrees">
<button onclick="calculate()">Calculate</button>
<p id="result"></p>

请注意,他们可能会发现算法是由一个想要恶作剧所有人的实习生在4月1日编写的。他们甚至可能会找到用于选择系数的骰子。
无论如何,了解所采用的随机逻辑有助于我绕过它,这就是为什么我这样做的原因。希望有人能够发现它,也许有一天我们就会有修复版本的hue-rotate等与浏览器一起发布。
额外奉上,以防对任何人有帮助:这就是如何计算Sepia的方法:
var newPixel = {
    newRed:   oldRed * 0.393 + oldGreen * 0.769 + oldBlue * 0.189,
    newGreen: oldRed * 0.349 + oldGreen * 0.686 + oldBlue * 0.168,
    newBlue:  oldRed * 0.272 + oldGreen * 0.534 + oldBlue * 0.131,
};

2
我发现不仅CSS,而且其他实现都使用了相同的算法。
这里有一个合理的解释

旋转120.0度将完全映射红色到绿色,绿色到蓝色,蓝色到红色。然而,此变换存在一个问题,即输入颜色的亮度未被保留。

因此,该算法进行色调旋转时,同时保持亮度,而不仅仅是旋转色调通道。

1
这对于图像来说是有意义的。我完全可以理解为什么你想在图像中使用它。但是,如果你想得到像互补颜色这样的东西,那就不是你通常期望的方式,我认为。我不记得6年前我为什么会非常生气,但它让我疯狂 :D - Camilo Martin
如果实际的色调旋转仍然是一个选项,那么这将是有意义的。我不明白为什么它不是。 - Rapti

0

这是一种保持亮度的色调旋转。它应该像这样工作。如果在将图像转换为单色之前对其进行色调旋转,则应具有相同的感知亮度。 #00FF00和#0000FF则不会。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接