diff options
author | Benjamin Chausse <benjamin@chausse.xyz> | 2024-11-23 21:05:11 -0500 |
---|---|---|
committer | Benjamin Chausse <benjamin@chausse.xyz> | 2024-11-23 21:05:11 -0500 |
commit | 4d25e4ece0b72d240bb2565f8abb7389e650990a (patch) | |
tree | 55af982b45d9ed576871c6f3ccf5f800cddc9b56 /internal/colors/hsl.go | |
parent | b42ab480dd4c4eec83d79bba9400232ddb79f6b1 (diff) |
Preview + Unit-tests for color conversions
Diffstat (limited to 'internal/colors/hsl.go')
-rw-r--r-- | internal/colors/hsl.go | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/internal/colors/hsl.go b/internal/colors/hsl.go index 7477042..2a31f81 100644 --- a/internal/colors/hsl.go +++ b/internal/colors/hsl.go @@ -1 +1,109 @@ package colors + +import ( + "fmt" + "math" +) + +type HSL struct { + H int // 0-360 + S int // 0-100 + L int // 0-100 +} + +func (h HSL) String() string { + return fmt.Sprintf("hsl(%d, %d, %d)", h.H, h.S, h.L) +} + +func (h HSL) ToPrecise() PreciseColor { + // Normalize H, S, L + hue := float64(h.H) / 360.0 + sat := float64(h.S) / 100.0 + light := float64(h.L) / 100.0 + + var r, g, b float64 + + if sat == 0 { + // Achromatic case + r, g, b = light, light, light + } else { + var q float64 + if light < 0.5 { + q = light * (1 + sat) + } else { + q = light + sat - (light * sat) + } + p := 2*light - q + r = hueToRGB(p, q, hue+1.0/3.0) + g = hueToRGB(p, q, hue) + b = hueToRGB(p, q, hue-1.0/3.0) + } + + return PreciseColor{R: r, G: g, B: b} +} + +func hueToRGB(p, q, t float64) float64 { + if t < 0 { + t += 1 + } + if t > 1 { + t -= 1 + } + if t < 1.0/6.0 { + return p + (q-p)*6*t + } + if t < 1.0/2.0 { + return q + } + if t < 2.0/3.0 { + return p + (q-p)*(2.0/3.0-t)*6 + } + return p +} + +func (h HSL) FromPrecise(p PreciseColor) ColorSpace { + r := p.R + g := p.G + b := p.B + + max := math.Max(math.Max(r, g), b) + min := math.Min(math.Min(r, g), b) + delta := max - min + + light := (max + min) / 2 + var sat, hue float64 + + if delta == 0 { + // Achromatic case + hue, sat = 0, 0 + } else { + if light < 0.5 { + sat = delta / (max + min) + } else { + sat = delta / (2 - max - min) + } + + switch max { + case r: + hue = (g-b)/delta + (6 * boolToFloat64(g < b)) + case g: + hue = (b-r)/delta + 2 + case b: + hue = (r-g)/delta + 4 + } + hue /= 6 + } + + return HSL{ + H: int(math.Round(hue * 360)), + S: int(math.Round(sat * 100)), + L: int(math.Round(light * 100)), + } +} + +func boolToFloat64(b bool) float64 { + if b { + return 1 + } + return 0 +} |