66 lines
1.5 KiB
Go
66 lines
1.5 KiB
Go
package color
|
|
|
|
import (
|
|
"math"
|
|
)
|
|
|
|
// LCH represents a color using lightness, chroma and hue.
|
|
func LCH(lightness Value, chroma Value, hue Value) Color {
|
|
lightness = min(max(lightness, 0), 1)
|
|
hue *= math.Pi / 180
|
|
a := math.Cos(hue)
|
|
b := math.Sin(hue)
|
|
|
|
if !inSRGB(lightness, a, b, chroma) {
|
|
chroma = findChromaInSRGB(chroma, lightness, a, b, 0.01)
|
|
}
|
|
|
|
a *= chroma
|
|
b *= chroma
|
|
|
|
r, g, b := oklabToLinearRGB(lightness, a, b)
|
|
|
|
r = sRGB(r)
|
|
g = sRGB(g)
|
|
b = sRGB(b)
|
|
|
|
return Color{r, g, b}
|
|
}
|
|
|
|
// findChromaInSRGB tries to find the closest chroma that can be represented in sRGB color space.
|
|
func findChromaInSRGB(chroma Value, lightness Value, a Value, b Value, precision Value) Value {
|
|
high := chroma
|
|
low := 0.0
|
|
chroma *= 0.5
|
|
|
|
for high-low > precision {
|
|
if inSRGB(lightness, a, b, chroma) {
|
|
low = chroma
|
|
} else {
|
|
high = chroma
|
|
}
|
|
|
|
chroma = (high + low) * 0.5
|
|
}
|
|
|
|
return chroma
|
|
}
|
|
|
|
// OKLAB to linear RGB.
|
|
// Source: https://bottosson.github.io/posts/oklab/
|
|
func oklabToLinearRGB(lightness Value, a Value, b Value) (Value, Value, Value) {
|
|
l := lightness + 0.3963377773761749*a + 0.21580375730991364*b
|
|
m := lightness - 0.10556134581565857*a - 0.0638541728258133*b
|
|
s := lightness - 0.08948418498039246*a - 1.2914855480194092*b
|
|
|
|
l = l * l * l
|
|
m = m * m * m
|
|
s = s * s * s
|
|
|
|
red := 4.0767416621*l - 3.3077115913*m + 0.2309699292*s
|
|
green := -1.2684380046*l + 2.6097574011*m - 0.3413193965*s
|
|
blue := -0.0041960863*l - 0.7034186147*m + 1.7076147010*s
|
|
|
|
return red, green, blue
|
|
}
|