Why is there no hue of rotation at + 180deg and -180deg, which gives the original color?

Reading the HSL / HSV color theory, it seems that the hue component is a cyclic attribute that repeats every 360 degrees and can be changed regardless of saturation and brightness / value. Correct me if I am wrong, but these statements logically follow the previous definition:

  • 360 degree rotating hue gives the same color
  • 180 degree rotating hue gives original color twice
  • 180 degrees rotating hue followed by -180 degrees gives the original color

However, only option 1 is correct. A rotating hue 4 times by +90 degrees gives a color that is not even removed from the original.

Also using the -webkit filter and SVG

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

do not give the same result for the same rotation. On the other hand, colors created by SVG filters are consistent across browsers.

Is there any β€œhidden” hue rotation property that makes the operation non-associative?




Examples of webkit and SVG filters can be found here: http://jsfiddle.net/maros_urbanec/ARsjb/5/

+8
colors svg graphics hsl svg-filters
04 Oct '13 at 18:05
source share
3 answers

CSS and SVG filters do not convert to HSV or HSL - hueRotation abbreviations use the linear matrix approximation in RGB space to perform hue rotation. This does not very well maintain saturation or brightness at low revs and saturated colors - as you can see.

True hue rotation, first converts the input RGB color to HSL, adjusts H, and then converts back to RGB. Filters do not do this. And this transformation cannot be accurately approximated by a linear matrix, therefore, although the hue changes exactly (mainly), saturation and brightness pass everywhere. These effects are non-linear, so adding smaller ops together results in different colors and does one big operation.

(The difference between huerotation in SVG and CSS filters can be due to the use of different color spaces (sRGB vs. linearRGB) - they must be the same.)

Update: I was interested enough to go and do a manual comparison. As you can see, the filters do a terrible job with a hue that rotates pure colors in the range of 0 to 180 degrees. This image compares the manual hue correction performed by manually connecting the hsl colors (outer ring) against the rotation of the filter hue on the base color (inner ring)

Explicit HSL Hue Rotation vs. CSS Filter Hue Rotation

But they do better with pure colors like hsl (0.50%, 75%), as you can see. hue rotation with mid HSL

codepen link in case you want to play: http://codepen.io/mullany/pen/fwHrd

+24
Oct 11 '13 at 19:04 on
source share

Michael's answer is terrific, and I wish I had seen him before; but since I need to not only understand that they are damn strange, but also in which direction (I want to work on my logic, so I need math), I encoded the hue-rotate implementation in Javascript (which was mainly taken from reading the source code Firefox) which emulates a hue-rotate that uses Webkit / Blink / Gecko.

Again, the point here is to understand what results it produces.

 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\ 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> 

This snippet was taken from this answer .

+5
Apr 12 '15 at 3:19
source share

tl; dr Error converting colors from floats (inside the filter) to bytes (everywhere).

So this is a little more complicated than spec provides a good formula for hue rotation matrices, for example, for 180 degrees (excluding alpha and shifts):

 -0.5747 1.4304 0.1444 0.4252 0.4304 0.1444 0.4252 1.4304 -0.8556 

Please note: if you multiply this by yourself, you get (up to four decimal places):

  0.9999 0.0001 0.0000 0.0000 1.0 0.0 0.0000 0.0000 1.0 

which is very close to the identity matrix or the null transform.

That would be fine, except that the browser converts back to RGB between each filter. See what happens when we shade: bright red:

 -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 

We get a color that cannot be represented in RGB with values ​​from 0 to 255. Thus, it is 0 0.4235 0.4235 and rounded to 0 0.4235 0.4235 during the RGB conversion, and when it is rotated again, we end with a dark, unsaturated red, 0.6667 0.2431 0.2431 instead of a bright pure the red we started with.

0
Oct 02 '17 at 20:46 on
source share



All Articles