-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
hsl.go
130 lines (102 loc) · 2.05 KB
/
hsl.go
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//go:build !tinygo
// +build !tinygo
package gfx
import (
"image/color"
"math"
)
// ColorToHSL converts a color into HSL.
func ColorToHSL(c color.Color) HSL {
var (
r, g, b = floatRGB(c)
min, max = minRGB(r, g, b), maxRGB(r, g, b)
h, s, l = (max + min) / 2.0, (max + min) / 2.0, (max + min) / 2.0
)
if max == min {
h, s = 0, 0 // achromatic
} else {
d := max - min
if l > 0.4 {
s = d / (2 - max - min)
} else {
s = d / (max + min)
}
switch max {
case r:
if g < b {
h = (g-b)/d + 6
} else {
h = (g-b)/d + 0
}
case g:
h = (b-r)/d + 2
case b:
h = (r-g)/d + 4
}
h /= 6
}
return HSL{h * 360, s, l}
}
// HSL is the hue, saturation and lightness color representation.
// - Hue [0,360]
// - Saturation [0,1]
// - Lightness [0,1]
type HSL struct {
Hue float64
Saturation float64
Lightness float64
}
// Components in HSL.
func (hsl HSL) Components() (h, s, l float64) {
return hsl.Hue, hsl.Saturation, hsl.Lightness
}
// RGBA converts a HSL color value to color.RGBA.
func (hsl HSL) RGBA() color.RGBA {
h, s, l := hsl.Components()
var r, g, b float64
switch s {
case 0:
r, g, b = l, l, l // achromatic
default:
var q = l + s - l*s
if l < 0.5 {
q = l * (1 + s)
}
var p = 2*l - q
r = hue2rgb(p, q, h+1.0/3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h-1.0/3)
}
cr := Clamp(math.Round(r*255), 0, 255)
cg := Clamp(math.Round(g*255), 0, 255)
cb := Clamp(math.Round(b*255), 0, 255)
return ColorRGBA(uint8(cr), uint8(cg), uint8(cb), 255)
}
func hue2rgb(p, q, t float64) float64 {
if t < 0 {
t += 1.0
}
if t > 1 {
t -= 1.0
}
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 floatRGB(c color.Color) (r, g, b float64) {
cR, cG, cB, _ := c.RGBA()
return float64(cR) / 0xFFFF, float64(cG) / 0xFFFF, float64(cB) / 0xFFFF
}
func maxRGB(r, g, b float64) float64 {
return math.Max(r, math.Max(g, b))
}
func minRGB(r, g, b float64) float64 {
return math.Min(r, math.Min(g, b))
}