14

通过阅读 HSL/HSV 颜色理论,我得到的印象是色调分量是一个循环属性,每 360 度重复一次,并且可以独立于饱和度和亮度/值进行更改。如果我错了,请纠正我,但这些陈述在逻辑上遵循前面的定义:

  1. 将色调旋转 360 度产生相同的颜色
  2. 将色调旋转 180 度两次产生原始颜色
  3. 将色调旋转 180 度,然后旋转 -180 度会产生原始颜色

但是,只有选项 1 是正确的。将色调旋转 4 次 +90 度会产生一种与原始颜色相差甚远的颜色。

此外,使用 -webkit-filter 和 SVG 的

<filter><feColorMatrix in="SourceGraphic" type="hueRotate" values="..." /></filter>

不要为相同的旋转产生相同的结果。另一方面,SVG 过滤器产生的颜色在浏览器中是一致的。

是否有任何色调旋转的“隐藏”属性使操作不具有关联性?


可以在这里找到 webkit 过滤器和 SVG 的示例:http: //jsfiddle.net/maros_urbanec/ARsjb/5/

4

3 回答 3

44

在 CSS 和 SVG 过滤器中,没有转换为 HSV 或 HSL - hueRotation 简写使用 RGB 空间中的线性矩阵近似来执行色调旋转。对于小旋转和高度饱和的颜色,这不能很好地保存饱和度或亮度 - 正如您所看到的。

真正的色调旋转,首先将输入的 RGB 颜色转换为 HSL,调整 H,然后再转换回 RGB。过滤器不这样做。而且这种转换不能用线性矩阵精确地近似,所以当色调被准确地改变时(大部分),饱和度和亮度到处都是。这些效果是非线性的,因此将较小的操作添加在一起会产生不同的颜色,而不是进行一项大操作。

(SVG 和 CSS 过滤器中的 Huerotation 之间的差异可能是由于使用了不同的颜色空间(sRGB 与线性RGB) - 这些应该是相同的。)

更新:我有足够的兴趣去进行手动比较。如您所见,滤镜在 0 到 180 度范围内旋转纯色的色调非常糟糕。此图像比较了通过手动插入 hsl 颜色完成的手动色调旋转(外环)与基础颜色上的滤镜色调旋转(内环)

显式 HSL 色调旋转与 CSS 滤镜色调旋转

但是,如您所见,它们在像 hsl(0,50%,75%) 这样的纯度较低的颜色上做得更好。 中间 HSL 的色调旋转

如果你想玩的话,codepen 链接:http: //codepen.io/mullany/pen/fwHrd

于 2013-10-11T19:04:35.513 回答
9

迈克尔的回答很棒,我希望我以前看过;但是因为我不仅需要了解他们非常奇怪,而且还需要了解以何种方式(我想解决他们的逻辑,所以我需要数学),我已经hue-rotate用 Javascript 编写了一个实现(主要来自阅读 Firefox 的源代码代码),它模拟了hue-rotateWebkit/Blink/Gecko 使用的。

同样,这里的重点只是了解它会产生什么结果。

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>

该片段取自此答案

于 2015-04-12T03:19:44.127 回答
2

tl; dr将颜色从浮点数(在过滤器内)转换为字节(在其他任何地方)时出错。

所以它比这更复杂一点,规范为色调旋转矩阵提供了一个很好的公式,例如 180 度的矩阵是(不包括 alpha 和移位):

-0.5747  1.4304   0.1444
 0.4252  0.4304   0.1444
 0.4252  1.4304  -0.8556

请注意,如果将其自身相乘,您将得到(保留四位小数):

 0.9999  0.0001   0.0000
 0.0000  1.0      0.0
 0.0000  0.0000   1.0

这非常接近单位矩阵或零变换。

这将是完美的,除了浏览器在每个过滤器之间转换回 RGB。看看当我们对鲜红色进行色调旋转时会发生什么:

-0.5747  1.4304   0.1444     1     -0.5747
 0.4252  0.4304   0.1444  *  0  =   0.4252
 0.4252  1.4304  -0.8556     0      0.4252

我们得到了一种不可能用 RGB 表示的颜色,其值从 0 到 255。所以0 0.4235 0.4235在 RGB 转换过程中它会被绑定并舍入,当它再次旋转时,我们最终会得到暗色不饱和红色,0.6667 0.2431 0.2431而不是明亮的纯红色。开始。

于 2017-10-02T20:46:27.380 回答