1 package chroma
2
3 import (
4 "fmt"
5 "math"
6 "strconv"
7 "strings"
8 )
9
10
11 var ANSI2RGB = map[string]string{
12 "#ansiblack": "000000",
13 "#ansidarkred": "7f0000",
14 "#ansidarkgreen": "007f00",
15 "#ansibrown": "7f7fe0",
16 "#ansidarkblue": "00007f",
17 "#ansipurple": "7f007f",
18 "#ansiteal": "007f7f",
19 "#ansilightgray": "e5e5e5",
20
21 "#ansidarkgray": "555555",
22 "#ansired": "ff0000",
23 "#ansigreen": "00ff00",
24 "#ansiyellow": "ffff00",
25 "#ansiblue": "0000ff",
26 "#ansifuchsia": "ff00ff",
27 "#ansiturquoise": "00ffff",
28 "#ansiwhite": "ffffff",
29
30
31 "#black": "000000",
32 "#darkred": "7f0000",
33 "#darkgreen": "007f00",
34 "#brown": "7f7fe0",
35 "#darkblue": "00007f",
36 "#purple": "7f007f",
37 "#teal": "007f7f",
38 "#lightgray": "e5e5e5",
39
40 "#darkgray": "555555",
41 "#red": "ff0000",
42 "#green": "00ff00",
43 "#yellow": "ffff00",
44 "#blue": "0000ff",
45 "#fuchsia": "ff00ff",
46 "#turquoise": "00ffff",
47 "#white": "ffffff",
48 }
49
50
51 type Colour int32
52
53
54 func NewColour(r, g, b uint8) Colour {
55 return ParseColour(fmt.Sprintf("%02x%02x%02x", r, g, b))
56 }
57
58
59
60
61
62 func (c Colour) Distance(e2 Colour) float64 {
63 ar, ag, ab := int64(c.Red()), int64(c.Green()), int64(c.Blue())
64 br, bg, bb := int64(e2.Red()), int64(e2.Green()), int64(e2.Blue())
65 rmean := (ar + br) / 2
66 r := ar - br
67 g := ag - bg
68 b := ab - bb
69 return math.Sqrt(float64((((512 + rmean) * r * r) >> 8) + 4*g*g + (((767 - rmean) * b * b) >> 8)))
70 }
71
72
73
74
75
76
77 func (c Colour) Brighten(factor float64) Colour {
78 r := float64(c.Red())
79 g := float64(c.Green())
80 b := float64(c.Blue())
81
82 if factor < 0 {
83 factor++
84 r *= factor
85 g *= factor
86 b *= factor
87 } else {
88 r = (255-r)*factor + r
89 g = (255-g)*factor + g
90 b = (255-b)*factor + b
91 }
92 return NewColour(uint8(r), uint8(g), uint8(b))
93 }
94
95
96 func (c Colour) BrightenOrDarken(factor float64) Colour {
97 if c.Brightness() < 0.5 {
98 return c.Brighten(factor)
99 }
100 return c.Brighten(-factor)
101 }
102
103
104
105
106 func (c Colour) ClampBrightness(min, max float64) Colour {
107 if !c.IsSet() {
108 return c
109 }
110
111 min = math.Max(min, 0)
112 max = math.Min(max, 1)
113 current := c.Brightness()
114 target := math.Min(math.Max(current, min), max)
115 if current == target {
116 return c
117 }
118
119 r := float64(c.Red())
120 g := float64(c.Green())
121 b := float64(c.Blue())
122 rgb := r + g + b
123 if target > current {
124
125 return c.Brighten((target*255*3 - rgb) / (255*3 - rgb))
126 }
127
128 return c.Brighten((target*255*3)/rgb - 1)
129 }
130
131
132 func (c Colour) Brightness() float64 {
133 return (float64(c.Red()) + float64(c.Green()) + float64(c.Blue())) / 255.0 / 3.0
134 }
135
136
137
138 func ParseColour(colour string) Colour {
139 colour = normaliseColour(colour)
140 n, err := strconv.ParseUint(colour, 16, 32)
141 if err != nil {
142 return 0
143 }
144 return Colour(n + 1)
145 }
146
147
148
149
150 func MustParseColour(colour string) Colour {
151 parsed := ParseColour(colour)
152 if !parsed.IsSet() {
153 panic(fmt.Errorf("invalid colour %q", colour))
154 }
155 return parsed
156 }
157
158
159 func (c Colour) IsSet() bool { return c != 0 }
160
161 func (c Colour) String() string { return fmt.Sprintf("#%06x", int(c-1)) }
162 func (c Colour) GoString() string { return fmt.Sprintf("Colour(0x%06x)", int(c-1)) }
163
164
165 func (c Colour) Red() uint8 { return uint8(((c - 1) >> 16) & 0xff) }
166
167
168 func (c Colour) Green() uint8 { return uint8(((c - 1) >> 8) & 0xff) }
169
170
171 func (c Colour) Blue() uint8 { return uint8((c - 1) & 0xff) }
172
173
174 type Colours []Colour
175
176 func (c Colours) Len() int { return len(c) }
177 func (c Colours) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
178 func (c Colours) Less(i, j int) bool { return c[i] < c[j] }
179
180
181 func normaliseColour(colour string) string {
182 if ansi, ok := ANSI2RGB[colour]; ok {
183 return ansi
184 }
185 if strings.HasPrefix(colour, "#") {
186 colour = colour[1:]
187 if len(colour) == 3 {
188 return colour[0:1] + colour[0:1] + colour[1:2] + colour[1:2] + colour[2:3] + colour[2:3]
189 }
190 }
191 return colour
192 }
193
View as plain text