summaryrefslogtreecommitdiff
path: root/internal/colors/hsl.go
blob: 51a1785d5fe8233858841c43ffd1343476e128d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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
}