Przekształcenie HLS i RGB
HLS i HSL to ten sam model (zob. modele HLS i HSI). Tu algorytm w obie strony, z przykładem i kodem, oraz przypadki, na których implementacje najczęściej się mylą.
RGB na HLS
max = max(R, G, B), min = min(R, G, B), d = max - min
L = (max + min) / 2
d = 0 → S = 0, H nieokreślony (szarość)
d > 0 → S = d / (1 - |2L - 1|)
H: wzór sześciokątny, jak w HSV (zob. RGB na kąt barwy)
Starsze opisy podają nasycenie dwoma wzorami zależnie od L. To ten sam wynik zapisany inaczej:
L ≤ 0,5 → S = d / (max + min)
L > 0,5 → S = d / (2 - max - min)
HLS na RGB
C = (1 - |2L - 1|) · S (chroma)
X = C · (1 - |(H / 60°) mod 2 - 1|)
m = L - C / 2
0° ≤ H < 60° → (C, X, 0)
60° ≤ H < 120° → (X, C, 0)
120° ≤ H < 180° → (0, C, X)
180° ≤ H < 240° → (0, X, C)
240° ≤ H < 300° → (X, 0, C)
300° ≤ H < 360° → (C, 0, X)
(R, G, B) = wynik z tabeli + m
Różnica względem HSV jest tylko w dwóch miejscach: C liczy się z L, a nie z V, a przesunięcie m to L - C/2, a nie V - C. Tabela sektorów jest identyczna.
Przykład w obie strony
RGB (200, 80, 40), #c85028:
R = 0,7843 G = 0,3137 B = 0,1569
max = 0,7843 min = 0,1569 d = 0,6275
L = (0,7843 + 0,1569) / 2 = 0,4706
S = 0,6275 / (1 - |0,9412 - 1|) = 0,6275 / 0,9412 = 0,6667
(albo: L ≤ 0,5, więc S = 0,6275 / 0,9412, ten sam wynik)
H = 60° · (0,3137 - 0,1569) / 0,6275 = 15°
HLS = (15°, 47,1%, 66,7%)
z powrotem:
C = 0,9412 · 0,6667 = 0,6275
X = 0,6275 · (1 - |0,25 - 1|) = 0,1569
m = 0,4706 - 0,3137 = 0,1569
sektor 0°..60° → (0,6275; 0,1569; 0) + 0,1569 = (0,7843; 0,3137; 0,1569)
RGB = (200, 80, 40)
Przypadki brzegowe
- L = 0 albo L = 1 (czerń, biel): mianownik 1 - |2L - 1| jest zerem. Wtedy d też jest zerem, więc trzeba sprawdzić d przed dzieleniem.
- Bardzo jasne i bardzo ciemne barwy dostają duże S przy prawie niewidocznym zabarwieniu, np. (255, 250, 250) ma S = 100%. Opisuje to strona o nasyceniu.
- H = 360° trzeba zamienić na 0°, inaczej (H / 60°) mod 2 i wybór sektora mogą dać zły wynik.
- Kolejność wartości: CSS
hsl()przyjmuje (H, S, L), a nazwa HLS sugeruje (H, L, S). Przy przenoszeniu wartości łatwo zamienić S z L.
Kod
// r, g, b, s, l w zakresie 0..1, h w stopniach
function rgbToHsl(r, g, b) {
const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min;
const l = (max + min) / 2;
if (d < 1e-9) return { h: 0, s: 0, l };
const s = d / (1 - Math.abs(2 * l - 1));
let h = max === r ? (g - b) / d : max === g ? (b - r) / d + 2 : (r - g) / d + 4;
h *= 60;
return { h: h < 0 ? h + 360 : h, s, l };
}
function hslToRgb(h, s, l) {
h = ((h % 360) + 360) % 360;
const c = (1 - Math.abs(2 * l - 1)) * s;
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
const m = l - c / 2;
const [r, g, b] =
h < 60 ? [c, x, 0] : h < 120 ? [x, c, 0] : h < 180 ? [0, c, x] :
h < 240 ? [0, x, c] : h < 300 ? [x, 0, c] : [c, 0, x];
return { r: r + m, g: g + m, b: b + m };
}
Inna, równie częsta wersja hslToRgb, z funkcją hue2rgb, jest na stronie funkcje pomocnicze.