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 }