-
Notifications
You must be signed in to change notification settings - Fork 0
/
color.go
145 lines (121 loc) · 3.09 KB
/
color.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package blink
import (
"encoding/hex"
"fmt"
"strconv"
"strings"
"github.com/hashicorp/go-multierror"
"math/rand"
)
var (
Red = Color{R: 255, G: 000, B: 000}
Green = Color{R: 000, G: 255, B: 000}
Blue = Color{R: 000, G: 000, B: 255}
Yellow = Color{R: 255, G: 255, B: 000}
White = Color{R: 255, G: 255, B: 255}
)
// Color contains the 24-bit RGB color information.
type Color struct{ R, G, B byte }
// Add returns a new color where every individual color channel is
// the result of adding up the corresponding value of this color an the given color.
// The maximum value of each color channel is always 0xFF (no overflows).
func (c Color) Add(other Color) Color {
max := func(i int) byte {
if i > 255 {
i = 255
}
return byte(i)
}
return Color{
R: max(int(c.R) + int(other.R)),
G: max(int(c.G) + int(other.G)),
B: max(int(c.B) + int(other.B)),
}
}
// Multiply returns a copy of c where every individual color channel is
// multiplied with the given factor.
// The maximum value of each color channel is always 0xFF (no overflows).
// If f is lower than 0 it is ignored entirely and c is returned unchanged.
func (c Color) Multiply(f float64) Color {
if f < 0 {
return c
}
return Color{
R: floatToByte(float64(c.R) * f),
G: floatToByte(float64(c.G) * f),
B: floatToByte(float64(c.B) * f),
}
}
func floatToByte(f float64) byte {
if f > 255 {
f = 255
}
return byte(f + 0.5)
}
// MustParseColor behaves exactly as ParseColor but panics if an error occurs.
func MustParseColor(s string) Color {
c, err := ParseColor(s)
if err != nil {
panic(err)
}
return c
}
// ParseColor parses a Color from string.
// It accepts the RGB value either as comma separated vector
// or in hexadecimal form with a leading hash tag.
// Examples:
// 255,255,0
// 255, 255, 0
// #ffff00
// #FFFF00
func ParseColor(s string) (c Color, err error) {
switch {
case len(s) == 0:
err = fmt.Errorf("can not parse color from empty string")
case s[0] == '#':
c, err = parseColorFromHex(s[1:])
default:
c, err = parseColorFromCSV(s)
}
return
}
func parseColorFromHex(s string) (Color, error) {
var c Color
b, err := hex.DecodeString(s)
if err != nil {
return c, fmt.Errorf("can not parse hex color from %q: %s", s, err)
}
if len(b) != 3 {
return c, fmt.Errorf("invalid number of bytes (have %d want 3)", len(b))
}
c.R = b[0]
c.G = b[1]
c.B = b[2]
return c, nil
}
func parseColorFromCSV(s string) (Color, error) {
var c Color
parts := strings.Split(s, ",")
if len(parts) != 3 {
return c, fmt.Errorf("can not parse color from CSV: expected exactly three comma separated values")
}
parse := func(s string) (byte, error) {
i, err := strconv.ParseInt(strings.TrimSpace(s), 10, 8)
return byte(i), err
}
var err1, err2, err3 error
c.R, err1 = parse(parts[0])
c.G, err2 = parse(parts[1])
c.B, err3 = parse(parts[2])
if err1 != nil || err2 != nil || err3 != nil {
return c, multierror.Append(err1, err2, err3)
}
return c, nil
}
func RandomColor() Color {
return Color{
R: byte(rand.Int31n(265)),
G: byte(rand.Int31n(265)),
B: byte(rand.Int31n(265)),
}
}