-
Notifications
You must be signed in to change notification settings - Fork 3
/
rgba.go
200 lines (172 loc) · 4.54 KB
/
rgba.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package mat
import (
"errors"
"fmt"
"image/color"
)
// Lerp linearly interpolates between two floats
func Lerp(a, b, t float64) float64 {
return a + (b-a)*t
}
// LerpColor does linear interpolation between two colors
func LerpColor(a, b RGBA, t float64) RGBA {
return RGBA{
R: Lerp(a.R, b.R, t),
G: Lerp(a.G, b.G, t),
B: Lerp(a.B, b.B, t),
A: Lerp(a.A, b.A, t),
}
}
// RGBA represents an alpha-premultiplied RGBA color with components within range [0, 1].
//
// The difference between color.RGBA is that the value range is [0, 1] and the values are floats.
type RGBA struct {
R, G, B, A float64
}
// ZRGBA is zero value RGBA
var ZRGBA RGBA
// Color constants
var (
Transparent = RGBA{}
Black = RGB(0, 0, 0)
White = RGB(1, 1, 1)
Red = RGB(1, 0, 0)
Green = RGB(0, 1, 0)
Blue = RGB(0, 0, 1)
)
func (r RGBA) String() string {
return fmt.Sprintf("RGBA(%v %v %v %v)", ff(r.R), ff(r.G), ff(r.B), ff(r.A))
}
// RGB returns a fully opaque RGBA color with the given RGB values.
//
// A common way to construct a transparent color is to create one with RGB constructor, then
// multiply it by a color obtained from the Alpha constructor.
func RGB(r, g, b float64) RGBA {
return RGBA{r, g, b, 1}
}
// Alpha returns a white RGBA color with the given alpha component.
func Alpha(a float64) RGBA {
return RGBA{1, 1, 1, a}
}
// Inverted returns inverted color, except alpha channel
func (r RGBA) Inverted() RGBA {
r.R = 1 - r.R
r.G = 1 - r.G
r.B = 1 - r.B
return r
}
// Add adds color d to color r component-wise and returns the result (the components are not
// clamped).
func (r RGBA) Add(d RGBA) RGBA {
return RGBA{
R: r.R + d.R,
G: r.G + d.G,
B: r.B + d.B,
A: r.A + d.A,
}
}
// Sub subtracts color d from color r component-wise and returns the result (the components
// are not clamped).
func (r RGBA) Sub(d RGBA) RGBA {
return RGBA{
R: r.R - d.R,
G: r.G - d.G,
B: r.B - d.B,
A: r.A - d.A,
}
}
// Mul multiplies color r by color d component-wise (the components are not clamped).
func (r RGBA) Mul(d RGBA) RGBA {
return RGBA{
R: r.R * d.R,
G: r.G * d.G,
B: r.B * d.B,
A: r.A * d.A,
}
}
// Div divides r by d component-wise (the components are not clamped).
func (r RGBA) Div(d RGBA) RGBA {
return RGBA{
A: r.A / d.A,
B: r.B / d.B,
R: r.R / d.R,
G: r.G / d.G,
}
}
// Scaled multiplies each component of color r by scale and returns the result (the components
// are not clamped).
func (r RGBA) Scaled(scale float64) RGBA {
return RGBA{
R: r.R * scale,
G: r.G * scale,
B: r.B * scale,
A: r.A * scale,
}
}
// RGBA returns alpha-premultiplied red, green, blue and alpha components of the RGBA color.
func (r RGBA) RGBA() (rc, g, b, a uint32) {
rc = uint32(0xffff * r.R)
g = uint32(0xffff * r.G)
b = uint32(0xffff * r.B)
a = uint32(0xffff * r.A)
return
}
// Flatten returns a array representation of color
func (r RGBA) Flatten() [4]float64 {
return [...]float64{r.R, r.G, r.B, r.A}
}
// Mutator returns array of pointers to color channels
func (r RGBA) Mutator() [4]*float64 {
return [...]*float64{&r.R, &r.G, &r.B, &r.A}
}
// ErrInvalidHex is returned by HexToRGBA if hex string contains non ex characters
var ErrInvalidHex = errors.New("byte is not a hex code")
// ErrTooShort is returned by HexToRGBA if hex string is too short to parse a color
var ErrTooShort = errors.New("hex string is too short (min is 6)")
// HexToRGBA converts hex string to RGBA
func HexToRGBA(s string) (r RGBA, err error) {
if len(s) < 6 {
return r, ErrTooShort
}
hexToByte := func(b byte) (r byte) {
switch {
case b >= '0' && b <= '9':
r = b - '0'
case b >= 'a' && b <= 'f':
r = b - 'a' + 10
case b >= 'A' && b <= 'F':
r = b - 'A' + 10
default:
err = ErrInvalidHex
}
return
}
r.R = float64(hexToByte(s[0])<<4+hexToByte(s[1])) / 0xFF
r.G = float64(hexToByte(s[2])<<4+hexToByte(s[3])) / 0xFF
r.B = float64(hexToByte(s[4])<<4+hexToByte(s[5])) / 0xFF
if len(s) == 8 {
r.A = float64(hexToByte(s[6])<<4+hexToByte(s[7])) / 0xFF
} else {
r.A = 1
}
return r, err
}
// ToRGBA converts a color to RGBA format. Using this function is preferred to using RGBAModel, for
// performance (using RGBAModel introduces additional unnecessary allocations).
func ToRGBA(c color.Color) RGBA {
if r, ok := c.(RGBA); ok {
return r
}
r, g, b, a := c.RGBA()
return RGBA{
float64(r) / 0xffff,
float64(g) / 0xffff,
float64(b) / 0xffff,
float64(a) / 0xffff,
}
}
// RGBAModel converts colors to RGBA format.
var RGBAModel = color.ModelFunc(rgbaModel)
func rgbaModel(r color.Color) color.Color {
return ToRGBA(r)
}